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
17 * The Original Code is Mozilla Communicator client code, released
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.
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.
49 #include "jsarena.h" /* Added by JSIFY */
50 #include "jsutil.h" /* Added by JSIFY */
72 #if JS_HAS_XML_SUPPORT
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))
86 (JS_ASSERT((fp)->script || !(fp)->spbase || (sp) == (fp)->spbase), \
89 #define SAVE_SP(fp) ((fp)->sp = sp)
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) \
126 if (JSDOUBLE_IS_INT(d, i_) && INT_FITS_IN_JSVAL(i_)) { \
127 v_ = INT_TO_JSVAL(i_); \
129 ok = js_NewDoubleValue(cx, d, &v_); \
136 #define STORE_INT(cx, n, i) \
140 if (INT_FITS_IN_JSVAL(i)) { \
141 v_ = INT_TO_JSVAL(i); \
143 ok = js_NewDoubleValue(cx, (jsdouble)(i), &v_); \
150 #define STORE_UINT(cx, n, u) \
154 if ((u) <= JSVAL_INT_MAX) { \
155 v_ = INT_TO_JSVAL(u); \
157 ok = js_NewDoubleValue(cx, (jsdouble)(u), &v_); \
164 #define FETCH_NUMBER(cx, n, d) \
168 v_ = FETCH_OPND(n); \
169 VALUE_TO_NUMBER(cx, v_, d); \
172 #define FETCH_INT(cx, n, i) \
174 jsval v_ = FETCH_OPND(n); \
175 if (JSVAL_IS_INT(v_)) { \
176 i = JSVAL_TO_INT(v_); \
178 SAVE_SP_AND_PC(fp); \
179 ok = js_ValueToECMAInt32(cx, v_, &i); \
185 #define FETCH_UINT(cx, n, ui) \
187 jsval v_ = FETCH_OPND(n); \
189 if (JSVAL_IS_INT(v_) && (i_ = JSVAL_TO_INT(v_)) >= 0) { \
192 SAVE_SP_AND_PC(fp); \
193 ok = js_ValueToECMAUint32(cx, v_, &ui); \
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) \
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); \
210 SAVE_SP_AND_PC(fp); \
211 ok = js_ValueToNumber(cx, v, &d); \
217 #define POP_BOOLEAN(cx, v, b) \
219 v = FETCH_OPND(-1); \
220 if (v == JSVAL_NULL) { \
222 } else if (JSVAL_IS_BOOLEAN(v)) { \
223 b = JSVAL_TO_BOOLEAN(v); \
225 SAVE_SP_AND_PC(fp); \
226 ok = js_ValueToBoolean(cx, v, &b); \
233 /* SAVE_SP_AND_PC must be already called. */
234 #define VALUE_TO_OBJECT(cx, n, v, obj) \
236 ASSERT_SAVED_SP_AND_PC(fp); \
237 if (!JSVAL_IS_PRIMITIVE(v)) { \
238 obj = JSVAL_TO_OBJECT(v); \
240 obj = js_ValueToNonNullObject(cx, v); \
245 STORE_OPND(n, OBJECT_TO_JSVAL(obj)); \
249 /* SAVE_SP_AND_PC must be already called. */
250 #define FETCH_OBJECT(cx, n, v, obj) \
252 ASSERT_SAVED_SP_AND_PC(fp); \
254 VALUE_TO_OBJECT(cx, n, v, obj); \
257 #define DEFAULT_VALUE(cx, n, hint, v) \
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]); \
268 JS_FRIEND_API(jsval
*)
269 js_AllocRawStack(JSContext
*cx
, uintN nslots
, void **markp
)
274 *markp
= JS_ARENA_MARK(&cx
->stackPool
);
275 JS_ARENA_ALLOCATE_CAST(sp
, jsval
*, &cx
->stackPool
, nslots
* sizeof(jsval
));
277 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_STACK_OVERFLOW
,
278 (cx
->fp
&& cx
->fp
->fun
)
279 ? JS_GetFunctionName(cx
->fp
->fun
)
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
;
299 /* Callers don't check for zero nslots: we do to avoid empty segments. */
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
);
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
);
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).
325 if (fp
&& fp
->script
&& fp
->spbase
) {
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
);
331 end
= fp
->spbase
+ fp
->script
->depth
;
332 for (vp
= fp
->sp
; vp
< end
; vp
++)
336 /* Allocate and push a stack segment header from the 2 extra slots. */
337 sh
= (JSStackHeader
*)sp
;
339 sh
->down
= cx
->stackHeaders
;
340 cx
->stackHeaders
= sh
;
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
));
354 js_FreeStack(JSContext
*cx
, void *mark
)
359 /* Check for zero nslots allocation special case. */
363 /* We can assert because js_FreeStack always balances js_AllocStack. */
364 sh
= cx
->stackHeaders
;
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
;
372 cx
->stackHeaders
= sh
->down
;
374 /* Release the stackPool space allocated since mark was set. */
375 JS_ARENA_RELEASE(&cx
->stackPool
, mark
);
379 js_GetArgument(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
385 js_SetArgument(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
391 js_GetLocalVariable(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
397 js_SetLocalVariable(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
403 js_GetScopeChain(JSContext
*cx
, JSStackFrame
*fp
)
405 JSObject
*obj
, *cursor
, *clonedChild
, *parent
;
406 JSTempValueRooter tvr
;
408 obj
= fp
->blockChain
;
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
) ||
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
))
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
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
);
454 JS_POP_TEMP_ROOT(cx
, &tvr
);
459 * The first iteration. Check if other follow and root obj if so
460 * to protect the whole cloned chain against GC.
465 JS_PUSH_TEMP_ROOT_OBJECT(cx
, obj
, &tvr
);
468 * Avoid OBJ_SET_PARENT overhead as clonedChild cannot escape to
471 STOBJ_SET_PARENT(clonedChild
, cursor
);
473 JS_ASSERT(tvr
.u
.value
== OBJECT_TO_JSVAL(obj
));
474 JS_POP_TEMP_ROOT(cx
, &tvr
);
478 clonedChild
= cursor
;
481 fp
->flags
|= JSFRAME_POP_BLOCKS
;
482 fp
->scopeChain
= obj
;
483 fp
->blockChain
= NULL
;
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.
492 PutBlockObjects(JSContext
*cx
, JSStackFrame
*fp
)
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
)
502 ok
&= js_PutBlockObject(cx
, obj
);
509 js_GetPrimitiveThis(JSContext
*cx
, jsval
*vp
, JSClass
*clasp
, jsval
*thisvp
)
515 if (JSVAL_IS_OBJECT(v
)) {
516 obj
= JSVAL_TO_OBJECT(v
);
517 if (!JS_InstanceOf(cx
, obj
, clasp
, vp
+ 2))
519 v
= OBJ_GET_SLOT(cx
, obj
, JSSLOT_PRIVATE
);
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:
531 * function f() { return this }
532 * function g() { return f }
538 * The alert should display "true".
541 ComputeGlobalThis(JSContext
*cx
, jsval
*argv
)
545 if (JSVAL_IS_PRIMITIVE(argv
[-2]) ||
546 !OBJ_GET_PARENT(cx
, JSVAL_TO_OBJECT(argv
[-2]))) {
547 thisp
= cx
->globalObject
;
554 /* Walk up the parent chain. */
555 thisp
= JSVAL_TO_OBJECT(argv
[-2]);
556 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.parentAtom
);
558 if (!OBJ_CHECK_ACCESS(cx
, thisp
, id
, JSACC_PARENT
, &v
, &attrs
))
560 parent
= JSVAL_IS_VOID(v
)
561 ? OBJ_GET_PARENT(cx
, thisp
)
562 : JSVAL_TO_OBJECT(v
);
568 argv
[-1] = OBJECT_TO_JSVAL(thisp
);
573 ComputeThis(JSContext
*cx
, jsval
*argv
)
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
)
588 /* Some objects (e.g., With) delegate 'this' to another object. */
589 thisp
= thisp
->map
->ops
->thisObject(cx
, thisp
);
592 argv
[-1] = OBJECT_TO_JSVAL(thisp
);
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
607 NoSuchMethod(JSContext
*cx
, JSStackFrame
*fp
, jsval
*vp
, uint32 flags
,
610 JSObject
*thisp
, *argsobj
;
613 JSTempValueRooter tvr
;
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]));
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
)) {
633 ops
= (JSXMLObjectOps
*) thisp
->map
->ops
;
634 thisp
= ops
->getMethod(cx
, thisp
, id
, &roots
[2]);
639 vp
[1] = OBJECT_TO_JSVAL(thisp
);
643 ok
= OBJ_GET_PROPERTY(cx
, thisp
, id
, &roots
[2]);
647 if (JSVAL_IS_PRIMITIVE(roots
[2]))
650 pc
= (jsbytecode
*) vp
[-(intN
)fp
->script
->depth
];
651 switch ((JSOp
) *pc
) {
654 #if JS_HAS_XML_SUPPORT
657 GET_ATOM_FROM_BYTECODE(fp
->script
, pc
, 0, atom
);
658 roots
[0] = ATOM_KEY(atom
);
659 argsobj
= js_NewArrayObject(cx
, argc
, vp
+ 2);
664 roots
[1] = OBJECT_TO_JSVAL(argsobj
);
665 ok
= js_InternalInvoke(cx
, thisp
, roots
[2], flags
| JSINVOKE_INTERNAL
,
674 JS_POP_TEMP_ROOT(cx
, &tvr
);
678 js_ReportIsNotFunction(cx
, vp
, flags
& JSINVOKE_FUNFLAGS
);
683 #endif /* JS_HAS_NO_SUCH_METHOD */
685 #ifdef DUMP_CALL_TABLE
691 typedef struct CallKey
{
692 jsval callee
; /* callee value */
693 const char *filename
; /* function filename or null */
694 uintN lineno
; /* function lineno or 0 */
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 */
709 uint32 typeHist
[NTYPEHIST
]; /* histogram by type */
710 JSCList lruList
; /* top 10 values LRU list */
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 */
720 typedef struct CallEntry
{
724 char name
[32]; /* function name copy */
728 AllocCallTable(void *pool
, size_t size
)
734 FreeCallTable(void *pool
, void *item
)
740 AllocCallEntry(void *pool
, const void *key
)
742 return (JSHashEntry
*) calloc(1, sizeof(CallEntry
));
746 FreeCallEntry(void *pool
, JSHashEntry
*he
, uintN flag
)
748 JS_ASSERT(flag
== HT_FREE_ENTRY
);
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;
764 hash
= (hash
<< 4) ^ JS_HashString(ck
->filename
);
765 hash
= (hash
<< 4) ^ ck
->lineno
;
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
;
794 struct ArgValCount
*avc
;
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
);
802 fprintf(fp
, "@%p ", (void *) ce
->key
.callee
);
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
];
818 for (type
= JSTYPE_VOID
; type
<= JSTYPE_LIMIT
; type
++) {
819 if (ai
->typeHist
[type
]) {
825 fprintf(fp
, " arg %u type %s: %lu\n",
826 i
, TYPENAME(save
), (unsigned long) ai
->typeHist
[save
]);
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
)
838 fprintf(fp
, " arg %u top 10 values:\n", i
);
840 for (cl
= ai
->lruList
.prev
; cl
!= &ai
->lruList
; cl
= cl
->prev
) {
841 avc
= (struct ArgValCount
*)cl
;
845 fprintf(fp
, " %9u: %8lu %.*s (%#lx)\n",
846 n
, (unsigned long) avc
->count
,
847 (int) sizeof avc
->strbuf
, avc
->strbuf
,
853 return HT_ENUMERATE_NEXT
;
857 js_DumpCallTable(JSContext
*cx
)
861 static uintN dumpCount
;
866 JS_snprintf(name
, sizeof name
, "/tmp/calltable.dump.%u", dumpCount
& 7);
868 fp
= fopen(name
, "w");
872 JS_HashTableEnumerateEntries(js_CallTable
, CallTableDumper
, fp
);
877 LogCall(JSContext
*cx
, jsval callee
, uintN argc
, jsval
*argv
)
880 const char *name
, *cstr
;
882 JSHashNumber keyHash
;
883 JSHashEntry
**hep
, *he
;
889 struct ArgValCount
*avc
;
893 js_CallTable
= JS_NewHashTable(1024, js_hash_call_key
,
894 js_compare_call_keys
, NULL
,
895 &callTableAllocOps
, NULL
);
904 if (VALUE_IS_FUNCTION(cx
, callee
)) {
905 fun
= (JSFunction
*) OBJ_GET_PRIVATE(cx
, JSVAL_TO_OBJECT(callee
));
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
);
918 ce
= (CallEntry
*) he
;
919 JS_ASSERT(strncmp(ce
->name
, name
, sizeof ce
->name
) == 0);
921 he
= JS_HashTableRawAdd(js_CallTable
, hep
, keyHash
, &key
, NULL
);
924 ce
= (CallEntry
*) he
;
925 ce
->entry
.key
= &ce
->key
;
926 ce
->entry
.value
= &ce
->value
;
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
);
938 if (ce
->value
.minargc
< argc
)
939 ce
->value
.minargc
= argc
;
940 if (ce
->value
.maxargc
< argc
)
941 ce
->value
.maxargc
= argc
;
944 for (i
= 0; i
< argc
; i
++) {
945 ai
= &ce
->value
.argInfo
[i
];
947 type
= TYPEOF(cx
, argval
);
948 ++ai
->typeHist
[type
];
952 avc
= (struct ArgValCount
*) ai
->lruList
.next
;
953 ce
->value
.recycled
+= avc
->count
;
958 avc
= &ai
->topValCounts
[j
];
959 if (avc
->value
== argval
) {
965 /* Move avc to the back of the LRU list. */
966 JS_REMOVE_LINK(&avc
->lruLink
);
967 JS_APPEND_LINK(&avc
->lruLink
, &ai
->lruList
);
971 switch (TYPEOF(cx
, argval
)) {
973 cstr
= js_undefined_str
;
979 cstr
= JS_BOOLEAN_STR(JSVAL_TO_BOOLEAN(argval
));
982 if (JSVAL_IS_INT(argval
)) {
983 JS_snprintf(avc
->strbuf
, sizeof avc
->strbuf
, "%ld",
984 JSVAL_TO_INT(argval
));
986 JS_dtostr(avc
->strbuf
, sizeof avc
->strbuf
, DTOSTR_STANDARD
, 0,
987 *JSVAL_TO_DOUBLE(argval
));
991 str
= js_QuoteString(cx
, JSVAL_TO_STRING(argval
), (jschar
)'"');
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
);
1003 js_LogCallToSourceLimit
= sizeof avc
->strbuf
;
1004 cx
->options
|= JSOPTION_LOGCALL_TOSOURCE
;
1005 str
= js_ValueToSource(cx
, argval
);
1006 cx
->options
&= ~JSOPTION_LOGCALL_TOSOURCE
;
1010 js_PutEscapedString(avc
->strbuf
, sizeof avc
->strbuf
, str
, 0);
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
1026 #ifdef DEBUG_NOT_THROWING
1027 # define ASSERT_NOT_THROWING(cx) JS_ASSERT(!(cx)->throwing)
1029 # define ASSERT_NOT_THROWING(cx) /* nothing */
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
)
1071 JSStackFrame
*fp
, frame
;
1072 jsval
*sp
, *newsp
, *limit
;
1074 JSObject
*funobj
, *parent
;
1081 uintN nslots
, nvars
, nalloc
, surplus
;
1082 JSInterpreterHook hook
;
1085 /* Mark the top of stack and load frequently-used registers. */
1086 mark
= JS_ARENA_MARK(&cx
->stackPool
);
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
);
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
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
);
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
);
1144 if (VALUE_IS_FUNCTION(cx
, v
)) {
1145 /* Make vp refer to funobj to keep it available as argv[-2]. */
1147 funobj
= JSVAL_TO_OBJECT(v
);
1148 parent
= OBJ_GET_PARENT(cx
, funobj
);
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]);
1163 native
= ops
->construct
;
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
)) {
1177 script
= fun
->u
.i
.script
;
1178 nvars
= fun
->u
.i
.nvars
;
1180 native
= fun
->u
.n
.native
;
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]))
1196 if (flags
& JSINVOKE_CONSTRUCT
) {
1197 /* Default return value for a constructor is the new object. */
1198 JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp
[1]));
1202 * We must call js_ComputeThis in case we are not called from the
1203 * interpreter, where a prior bytecode has computed an appropriate
1206 ok
= js_ComputeThis(cx
, vp
+ 2);
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
;
1226 frame
.argv
= sp
- argc
;
1227 frame
.nvars
= nvars
;
1230 frame
.annotation
= NULL
;
1231 frame
.scopeChain
= NULL
; /* set below for real, after cx->fp is set */
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. */
1244 /* Init these now in case we goto out before first hook call. */
1245 hook
= cx
->debugHooks
->callHook
;
1248 /* Check for argument slots required by the function. */
1250 /* All arguments must be contiguous, so we may have to copy actuals. */
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)]. */
1258 /* Take advantage of surplus slots in the caller's frame depth. */
1259 JS_ASSERT((jsval
*)mark
>= sp
);
1260 surplus
= (jsval
*)mark
- sp
;
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
);
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
);
1280 memcpy(newsp
, frame
.argv
, argc
* sizeof(jsval
));
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. */
1292 } while (--nslots
!= 0);
1294 JS_ASSERT(nslots
== 0);
1296 /* Now allocate stack space for local variables. */
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
);
1307 /* NB: Discontinuity between argv and vars. */
1308 sp
= frame
.vars
= newsp
;
1312 /* Push void to initialize local variables. */
1315 } while (--nvars
!= 0);
1317 JS_ASSERT(nvars
== 0);
1319 /* Store the current sp in frame before calling fun. */
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. */
1328 #ifdef DEBUG_NOT_THROWING
1329 JSBool alreadyThrowing
= cx
->throwing
;
1332 #if JS_HAS_LVALUE_RETURN
1333 /* Set by JS_SetCallReturnValue2, used to return reference types. */
1334 cx
->rval2set
= JS_FALSE
;
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];
1354 #ifdef DEBUG_brendan
1357 fp
= fopen("/tmp/slow-natives.dump", "w");
1362 fprintf(fp
, "%p %s.%s\n",
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
,
1374 ? JS_GetFunctionName(fun
)
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
);
1386 } else if (script
) {
1387 #ifdef DUMP_CALL_TABLE
1388 LogCall(cx
, *vp
, argc
, frame
.argv
);
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
)) {
1399 ok
= js_Interpret(cx
, script
->code
, &v
);
1401 /* fun might be onerror trying to report a syntax error in itself. */
1402 frame
.scopeChain
= NULL
;
1408 hook
= cx
->debugHooks
->callHook
;
1410 hook(cx
, &frame
, JS_FALSE
, &ok
, hookData
);
1413 /* If frame has a call object, sync values and clear back-pointer. */
1415 ok
&= js_PutCallObject(cx
, &frame
);
1417 /* If frame has an arguments object, sync values and clear back-pointer. */
1419 ok
&= js_PutArgsObject(cx
, &frame
);
1421 /* Restore cx->fp now that we're done releasing frame objects. */
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. */
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
;
1442 js_ReportIsNotFunction(cx
, vp
, flags
& JSINVOKE_FUNFLAGS
);
1448 js_InternalInvoke(JSContext
*cx
, JSObject
*obj
, jsval fval
, uintN flags
,
1449 uintN argc
, jsval
*argv
, jsval
*rval
)
1451 JSStackFrame
*fp
, *oldfp
, frame
;
1457 fp
= oldfp
= cx
->fp
;
1459 memset(&frame
, 0, sizeof frame
);
1460 cx
->fp
= fp
= &frame
;
1463 sp
= js_AllocStack(cx
, 2 + argc
, &mark
);
1470 PUSH(OBJECT_TO_JSVAL(obj
));
1471 for (i
= 0; i
< argc
; i
++)
1474 ok
= js_Invoke(cx
, argc
, flags
| JSINVOKE_INTERNAL
);
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.
1486 if (JSVAL_IS_GCTHING(*rval
) && *rval
!= JSVAL_NULL
) {
1487 if (cx
->localRootStack
) {
1488 if (js_PushLocalRoot(cx
, cx
->localRootStack
, *rval
) < 0)
1491 cx
->weakRoots
.lastInternalResult
= *rval
;
1496 js_FreeStack(cx
, mark
);
1506 js_InternalGetOrSet(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval fval
,
1507 JSAccessMode mode
, uintN argc
, jsval
*argv
, jsval
*rval
)
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
);
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
,
1546 return js_InternalCall(cx
, obj
, fval
, argc
, argv
, rval
);
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
;
1559 hook
= cx
->debugHooks
->executeHook
;
1560 hookData
= mark
= NULL
;
1562 frame
.script
= script
;
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
;
1578 frame
.callobj
= frame
.argsobj
= NULL
;
1580 if (cx
->options
& JSOPTION_VAROBJFIX
) {
1581 while ((tmp
= OBJ_GET_PARENT(cx
, obj
)) != NULL
)
1585 frame
.callee
= NULL
;
1587 frame
.thisp
= chain
;
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
);
1597 memset(frame
.vars
, 0, frame
.nvars
* sizeof(jsval
));
1601 frame
.annotation
= NULL
;
1602 frame
.sharpArray
= NULL
;
1604 frame
.rval
= JSVAL_VOID
;
1606 frame
.scopeChain
= chain
;
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
;
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
;
1649 hook
= cx
->debugHooks
->executeHook
;
1651 hook(cx
, &frame
, JS_FALSE
, &ok
, hookData
);
1654 js_FreeRawStack(cx
, mark
);
1657 if (oldfp
&& oldfp
!= down
) {
1658 JS_ASSERT(cx
->dormantFrameChain
== oldfp
);
1659 cx
->dormantFrameChain
= oldfp
->dormantNext
;
1660 oldfp
->dormantNext
= NULL
;
1666 #if JS_HAS_EXPORT_IMPORT
1668 * If id is JSVAL_VOID, import all exported properties from obj.
1671 ImportProperty(JSContext
*cx
, JSObject
*obj
, jsid id
)
1676 JSObject
*obj2
, *target
, *funobj
, *closure
;
1681 if (JSVAL_IS_VOID(id
)) {
1682 ida
= JS_Enumerate(cx
, obj
);
1686 if (ida
->length
== 0)
1690 if (!OBJ_LOOKUP_PROPERTY(cx
, obj
, id
, &obj2
, &prop
))
1693 js_ReportValueError(cx
, JSMSG_NOT_DEFINED
,
1694 JSDVG_IGNORE_STACK
, ID_TO_VALUE(id
), NULL
);
1697 ok
= OBJ_GET_ATTRIBUTES(cx
, obj
, id
, prop
, &attrs
);
1698 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
1701 if (!(attrs
& JSPROP_EXPORTED
)) {
1702 js_ReportValueError(cx
, JSMSG_NOT_EXPORTED
,
1703 JSDVG_IGNORE_STACK
, ID_TO_VALUE(id
), NULL
);
1708 target
= cx
->fp
->varobj
;
1712 id
= ida
->vector
[i
];
1713 ok
= OBJ_GET_ATTRIBUTES(cx
, obj
, id
, NULL
, &attrs
);
1716 if (!(attrs
& JSPROP_EXPORTED
))
1719 ok
= OBJ_CHECK_ACCESS(cx
, obj
, id
, JSACC_IMPORT
, &value
, &attrs
);
1722 if (VALUE_IS_FUNCTION(cx
, value
)) {
1723 funobj
= JSVAL_TO_OBJECT(value
);
1724 closure
= js_CloneFunctionObject(cx
, funobj
, obj
);
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
);
1748 if (prop
&& target
== obj2
) {
1749 ok
= OBJ_SET_PROPERTY(cx
, target
, id
, &value
);
1751 ok
= OBJ_DEFINE_PROPERTY(cx
, target
, id
, value
, NULL
, NULL
,
1752 attrs
& ~(JSPROP_EXPORTED
|
1758 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
1761 } while (ida
&& ++i
< ida
->length
);
1765 JS_DestroyIdArray(cx
, ida
);
1768 #endif /* JS_HAS_EXPORT_IMPORT */
1770 #define JSPROP_INITIALIZER 0x100 /* NB: Not a valid property attribute. */
1773 js_CheckRedeclaration(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN attrs
,
1774 JSObject
**objp
, JSProperty
**propp
)
1778 uintN oldAttrs
, report
;
1781 const char *type
, *name
;
1783 if (!OBJ_LOOKUP_PROPERTY(cx
, obj
, id
, &obj2
, &prop
))
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
);
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).
1809 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
1813 if (attrs
== JSPROP_INITIALIZER
) {
1814 /* Allow the new object to override properties. */
1817 report
= JSREPORT_WARNING
| JSREPORT_STRICT
;
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
)))
1829 if ((~(oldAttrs
^ attrs
) & (JSPROP_GETTER
| JSPROP_SETTER
)) == 0)
1831 if (!(oldAttrs
& JSPROP_PERMANENT
))
1835 report
= JSREPORT_ERROR
;
1836 isFunction
= (oldAttrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) != 0;
1838 if (!OBJ_GET_PROPERTY(cx
, obj
, id
, &value
))
1840 isFunction
= VALUE_IS_FUNCTION(cx
, value
);
1844 type
= (attrs
== JSPROP_INITIALIZER
)
1846 : (oldAttrs
& attrs
& JSPROP_GETTER
)
1848 : (oldAttrs
& attrs
& JSPROP_SETTER
)
1850 : (oldAttrs
& JSPROP_READONLY
)
1855 name
= js_ValueToPrintableString(cx
, ID_TO_VALUE(id
));
1858 return JS_ReportErrorFlagsAndNumber(cx
, report
,
1859 js_GetErrorMessage
, NULL
,
1860 JSMSG_REDECLARED_VAR
,
1873 js_StrictlyEqual(jsval lval
, jsval rval
)
1875 jsval ltag
= JSVAL_TAG(lval
), rtag
= JSVAL_TAG(rval
);
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
;
1905 js_InvokeConstructor(JSContext
*cx
, jsval
*vp
, uintN argc
)
1908 JSObject
*obj
, *obj2
, *proto
, *parent
;
1910 JSClass
*clasp
, *funclasp
;
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
);
1926 clasp
= &js_ObjectClass
;
1928 proto
= parent
= NULL
;
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
1937 if (!OBJ_GET_PROPERTY(cx
, obj2
,
1938 ATOM_TO_JSID(cx
->runtime
->atomState
1939 .classPrototypeAtom
),
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
;
1953 obj
= js_NewObject(cx
, clasp
, proto
, parent
);
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
;
1964 /* Check the return value and if it's primitive, force it to be obj. */
1966 if (JSVAL_IS_PRIMITIVE(rval
)) {
1968 /* native [[Construct]] returning primitive is error */
1969 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1970 JSMSG_BAD_NEW_RESULT
,
1971 js_ValueToPrintableString(cx
, rval
));
1974 *vp
= OBJECT_TO_JSVAL(obj
);
1977 JS_RUNTIME_METER(cx
->runtime
, constructs
);
1982 InternNonIntElementId(JSContext
*cx
, JSObject
*obj
, jsval idval
, jsid
*idp
)
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
);
1994 if (!js_IsFunctionQName(cx
, JSVAL_TO_OBJECT(idval
), idp
))
2001 atom
= js_ValueToStringAtom(cx
, idval
);
2004 *idp
= ATOM_TO_JSID(atom
);
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 && ( \
2018 (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \
2019 __SUNPRO_C >= 0x570)
2020 # define JS_THREADED_INTERP 1
2022 # define JS_THREADED_INTERP 0
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.
2037 # define METER_OP_INIT(op) /* nothing */
2038 # define METER_OP_PAIR(op1,op2) /* nothing */
2039 # define METER_SLOT_OP(op,slot) /* nothing */
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
{
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
;
2075 const char *name
, *from
, *style
;
2077 uint32 total
, count
;
2078 uint32 i
, j
, nedges
;
2081 name
= getenv("JS_OPMETER_FILE");
2083 name
= "/tmp/ops.dot";
2084 fp
= fopen(name
, "w");
2091 for (i
= 0; i
< JSOP_LIMIT
; i
++) {
2092 for (j
= 0; j
< JSOP_LIMIT
; j
++) {
2093 count
= succeeds
[i
][j
];
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
;
2116 qsort(graph
, nedges
, sizeof(Edge
), compare_edges
);
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
);
2136 name
= getenv("JS_OPMETER_HIST");
2138 name
= "/tmp/ops.hist";
2139 fp
= fopen(name
, "w");
2144 fputs("bytecode", fp
);
2145 for (j
= 0; j
< HIST_NSLOTS
; j
++)
2146 fprintf(fp
, " slot %1u", (unsigned)j
);
2148 fputs("========", fp
);
2149 for (j
= 0; j
< HIST_NSLOTS
; j
++)
2150 fputs(" =======", 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
]);
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
);
2181 js_Interpret(JSContext
*cx
, jsbytecode
*pc
, jsval
*result
)
2186 uintN inlineCallCount
;
2188 JSObject
*obj
, *obj2
, *parent
;
2189 JSVersion currentVersion
, originalVersion
;
2191 JSTrapHandler interruptHandler
;
2195 jsbytecode
*endpc
, *pc2
;
2199 uintN argc
, attrs
, flags
, slot
;
2200 jsval
*vp
, lval
, rval
, ltmp
, rtmp
;
2202 JSObject
*withobj
, *iterobj
;
2204 JSScopeProperty
*sprop
;
2205 JSString
*str
, *str2
;
2211 #if !JS_THREADED_INTERP && defined DEBUG
2212 FILE *tracefp
= NULL
;
2214 #if JS_HAS_EXPORT_IMPORT
2217 jsint low
, high
, off
, npairs
;
2219 #if JS_HAS_GETTER_SETTER
2220 JSPropertyOp getter
, setter
;
2225 # define JS_EXTENSION __extension__
2226 # define JS_EXTENSION_(s) __extension__ ({ s; })
2228 # define JS_EXTENSION
2229 # define JS_EXTENSION_(s) s
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"
2240 static void *interruptJumpTable
[] = {
2241 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2242 JS_EXTENSION &&interrupt,
2243 # include "jsopcode.tbl"
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();
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)
2268 *result
= JSVAL_VOID
;
2271 /* Set registerized frame pointer and derived script pointer. */
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) \
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)]; \
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) \
2303 LOAD_OBJECT(PCOFF); \
2304 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass); \
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
2316 currentVersion
= (JSVersion
) script
->version
;
2317 originalVersion
= (JSVersion
) cx
->version
;
2318 if (currentVersion
!= originalVersion
)
2319 js_SetVersion(cx
, currentVersion
);
2322 flags
= 0; /* suppress gcc warnings */
2327 * Prepare to call a user-supplied branch handler, and abort the script
2328 * if it returns false.
2330 #define CHECK_BRANCH(len) \
2332 if (len <= 0 && cx->branchCallback) { \
2333 SAVE_SP_AND_PC(fp); \
2334 if (!(ok = cx->branchCallback(cx, script))) \
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)
2349 # define LOAD_JUMP_TABLE() /* nothing */
2352 #define LOAD_INTERRUPT_HANDLER(cx) \
2354 interruptHandler = (cx)->debugHooks->interruptHandler; \
2355 LOAD_JUMP_TABLE(); \
2358 LOAD_INTERRUPT_HANDLER(cx
);
2360 /* Check for too much js_Interpret nesting, or too deep a C stack. */
2362 if (!JS_CHECK_STACK_SIZE(cx
, stackDummy
)) {
2363 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_OVER_RECURSED
);
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
);
2385 JS_ASSERT(JS_UPTRDIFF(sp
, fp
->spbase
) <= depth
* sizeof(jsval
));
2386 newsp
= fp
->spbase
- depth
;
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.
2398 #ifdef DEBUG_NOT_THROWING
2399 printf("JS INTERPRETER CALLED WITH PENDING EXCEPTION %lx\n",
2400 (unsigned long) cx
->exception
);
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.
2420 if (interruptHandler
) {
2423 switch (interruptHandler(cx
, script
, pc
, &rval
,
2424 cx
->debugHooks
->interruptHandlerData
)) {
2428 case JSTRAP_CONTINUE
:
2434 cx
->throwing
= JS_TRUE
;
2435 cx
->exception
= rval
;
2440 LOAD_INTERRUPT_HANDLER(cx
);
2443 JS_ASSERT((uintN
)op
< (uintN
)JSOP_LIMIT
);
2444 JS_EXTENSION_(goto *normalJumpTable
[op
]);
2446 #else /* !JS_THREADED_INTERP */
2451 len
= js_CodeSpec
[op
].length
;
2454 tracefp
= (FILE *) cx
->tracefp
;
2458 fprintf(tracefp
, "%4u: ", js_PCToLineNumber(cx
, script
, pc
));
2459 js_Disassemble1(cx
, script
, pc
,
2460 PTRDIFF(pc
, script
->code
, jsbytecode
), JS_FALSE
,
2462 nuses
= js_CodeSpec
[op
].nuses
;
2465 for (n
= -nuses
; n
< 0; n
++) {
2466 char *bytes
= js_DecompileValueGenerator(cx
, n
, sp
[n
],
2469 fprintf(tracefp
, "%s %s",
2470 (n
== -nuses
) ? " inputs:" : ",",
2475 fprintf(tracefp
, " @ %d\n", sp
- fp
->spbase
);
2480 if (interruptHandler
) {
2482 switch (interruptHandler(cx
, script
, pc
, &rval
,
2483 cx
->debugHooks
->interruptHandlerData
)) {
2487 case JSTRAP_CONTINUE
:
2493 cx
->throwing
= JS_TRUE
;
2494 cx
->exception
= rval
;
2499 LOAD_INTERRUPT_HANDLER(cx
);
2504 #endif /* !JS_THREADED_INTERP */
2506 BEGIN_CASE(JSOP_STOP
)
2509 EMPTY_CASE(JSOP_NOP
)
2511 EMPTY_CASE(JSOP_GROUP
)
2513 BEGIN_CASE(JSOP_PUSH
)
2514 PUSH_OPND(JSVAL_VOID
);
2517 BEGIN_CASE(JSOP_POP
)
2521 BEGIN_CASE(JSOP_POPN
)
2522 sp
-= GET_UINT16(pc
);
2524 JS_ASSERT(fp
->spbase
<= sp
);
2525 obj
= fp
->blockChain
;
2527 fp
->spbase
+ OBJ_BLOCK_DEPTH(cx
, obj
)
2528 + OBJ_BLOCK_COUNT(cx
, obj
)
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
)
2534 if (JS_GetPrivate(cx
, obj
) != fp
)
2536 JS_ASSERT(fp
->spbase
+ OBJ_BLOCK_DEPTH(cx
, obj
)
2537 + ((clasp
== &js_BlockClass
)
2538 ? OBJ_BLOCK_COUNT(cx
, obj
)
2545 BEGIN_CASE(JSOP_SWAP
)
2546 vp
= sp
- depth
; /* swap generating pc's for the decompiler */
2555 BEGIN_CASE(JSOP_POPV
)
2556 *result
= POP_OPND();
2559 BEGIN_CASE(JSOP_ENTERWITH
)
2561 FETCH_OBJECT(cx
, -1, rval
, obj
);
2562 OBJ_TO_INNER_OBJECT(cx
, obj
);
2563 if (!obj
|| !(obj2
= js_GetScopeChain(cx
, fp
))) {
2567 withobj
= js_NewWithObject(cx
, obj
, obj2
, sp
- fp
->spbase
- 1);
2572 fp
->scopeChain
= withobj
;
2573 STORE_OPND(-1, OBJECT_TO_JSVAL(withobj
));
2574 END_CASE(JSOP_ENTERWITH
)
2576 BEGIN_CASE(JSOP_LEAVEWITH
)
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
)
2592 fp
->rval
= POP_OPND();
2595 BEGIN_CASE(JSOP_RETRVAL
) /* fp->rval already set */
2596 ASSERT_NOT_THROWING(cx
);
2597 if (inlineCallCount
)
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
2610 if (fp
->flags
& JSFRAME_POP_BLOCKS
) {
2612 ok
&= PutBlockObjects(cx
, fp
);
2616 JSInterpreterHook hook
= cx
->debugHooks
->callHook
;
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.
2632 ok
&= js_PutCallObject(cx
, 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. */
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. */
2659 /* Restore the calling script's interpreter registers. */
2660 script
= fp
->script
;
2661 depth
= (jsint
) script
->depth
;
2662 atoms
= script
->atomMap
.vector
;
2664 #if !JS_THREADED_INTERP
2665 endpc
= script
->code
+ script
->length
;
2668 /* Store the generating pc for the return value. */
2669 vp
[-depth
] = (jsval
)pc
;
2671 /* Resume execution in the calling frame. */
2673 if (JS_LIKELY(ok
)) {
2674 JS_ASSERT(js_CodeSpec
[*pc
].length
== JSOP_CALL_LENGTH
);
2675 len
= JSOP_CALL_LENGTH
;
2681 BEGIN_CASE(JSOP_DEFAULT
)
2684 BEGIN_CASE(JSOP_GOTO
)
2685 len
= GET_JUMP_OFFSET(pc
);
2689 BEGIN_CASE(JSOP_IFEQ
)
2690 POP_BOOLEAN(cx
, rval
, cond
);
2691 if (cond
== JS_FALSE
) {
2692 len
= GET_JUMP_OFFSET(pc
);
2698 BEGIN_CASE(JSOP_IFNE
)
2699 POP_BOOLEAN(cx
, rval
, cond
);
2700 if (cond
!= JS_FALSE
) {
2701 len
= GET_JUMP_OFFSET(pc
);
2708 POP_BOOLEAN(cx
, rval
, cond
);
2709 if (cond
== JS_TRUE
) {
2710 len
= GET_JUMP_OFFSET(pc
);
2716 BEGIN_CASE(JSOP_AND
)
2717 POP_BOOLEAN(cx
, rval
, cond
);
2718 if (cond
== JS_FALSE
) {
2719 len
= GET_JUMP_OFFSET(pc
);
2725 BEGIN_CASE(JSOP_DEFAULTX
)
2728 BEGIN_CASE(JSOP_GOTOX
)
2729 len
= GET_JUMPX_OFFSET(pc
);
2733 BEGIN_CASE(JSOP_IFEQX
)
2734 POP_BOOLEAN(cx
, rval
, cond
);
2735 if (cond
== JS_FALSE
) {
2736 len
= GET_JUMPX_OFFSET(pc
);
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
);
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
);
2760 BEGIN_CASE(JSOP_ANDX
)
2761 POP_BOOLEAN(cx
, rval
, cond
);
2762 if (cond
== JS_FALSE
) {
2763 len
= GET_JUMPX_OFFSET(pc
);
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
2775 * SAVE_SP_AND_PC must be already called.
2777 #define FETCH_ELEMENT_ID(obj, n, id) \
2779 jsval idval_ = FETCH_OPND(n); \
2780 if (JSVAL_IS_INT(idval_)) { \
2781 id = INT_JSVAL_TO_JSID(idval_); \
2783 ok = InternNonIntElementId(cx, obj, idval_, &id); \
2790 rval
= FETCH_OPND(-1);
2792 if (JSVAL_IS_PRIMITIVE(rval
)) {
2793 js_ReportValueError(cx
, JSMSG_IN_NOT_OBJECT
, -1, rval
, NULL
);
2797 obj
= JSVAL_TO_OBJECT(rval
);
2798 FETCH_ELEMENT_ID(obj
, -2, id
);
2799 ok
= OBJ_LOOKUP_PROPERTY(cx
, obj
, id
, &obj2
, &prop
);
2803 STORE_OPND(-1, BOOLEAN_TO_JSVAL(prop
!= NULL
));
2805 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
2808 BEGIN_CASE(JSOP_FOREACH
)
2809 flags
= JSITER_ENUMERATE
| JSITER_FOREACH
;
2812 #if JS_HAS_DESTRUCTURING
2813 BEGIN_CASE(JSOP_FOREACHKEYVAL
)
2814 flags
= JSITER_ENUMERATE
| JSITER_FOREACH
| JSITER_KEYVALUE
;
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__
2825 flags
= JSITER_ENUMERATE
;
2828 JS_ASSERT(sp
> fp
->spbase
);
2830 ok
= js_ValueToIterator(cx
, flags
, &sp
[-1]);
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.
2843 id
= ATOM_TO_JSID(atom
);
2847 BEGIN_CASE(JSOP_FORNAME
)
2849 id
= ATOM_TO_JSID(atom
);
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.
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.
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
]);
2882 ok
= js_CallIteratorNext(cx
, iterobj
, &rval
);
2885 if (rval
== JSVAL_HOLE
) {
2892 slot
= GET_ARGNO(pc
);
2893 JS_ASSERT(slot
< fp
->fun
->nargs
);
2894 fp
->argv
[slot
] = rval
;
2898 slot
= GET_VARNO(pc
);
2899 JS_ASSERT(slot
< fp
->fun
->u
.i
.nvars
);
2900 fp
->vars
[slot
] = rval
;
2904 /* Don't update the const slot. */
2908 slot
= GET_UINT16(pc
);
2909 JS_ASSERT(slot
< (uintN
)depth
);
2910 vp
= &fp
->spbase
[slot
];
2916 /* FORELEM is not a SET operation, it's more like BINDNAME. */
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
;
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
);
2940 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
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
;
2956 /* Push true to keep looping through properties. */
2962 len
= js_CodeSpec
[op
].length
;
2965 BEGIN_CASE(JSOP_DUP
)
2966 JS_ASSERT(sp
> fp
->spbase
);
2967 vp
= sp
- 1; /* address top of stack */
2969 vp
-= depth
; /* address generating pc */
2974 BEGIN_CASE(JSOP_DUP2
)
2975 JS_ASSERT(sp
- 2 >= fp
->spbase
);
2976 vp
= sp
- 1; /* address top of stack */
2979 vp
-= depth
; /* address generating pc */
2980 vp
[1] = vp
[2] = *vp
;
2985 #define PROPERTY_OP(n, call) \
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. */\
2997 #define ELEMENT_OP(n, call) \
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. */ \
3012 #define NATIVE_GET(cx,obj,pobj,sprop,vp) \
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) \
3022 SAVE_SP_AND_PC(fp); \
3023 ok = js_NativeGet(cx, obj, pobj, sprop, vp); \
3029 BEGIN_CASE(JSOP_SETCONST
)
3032 rval
= FETCH_OPND(-1);
3034 ok
= OBJ_DEFINE_PROPERTY(cx
, obj
, ATOM_TO_JSID(atom
), rval
,
3036 JSPROP_ENUMERATE
| JSPROP_PERMANENT
|
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);
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
|
3057 END_CASE(JSOP_ENUMCONSTELEM
)
3060 BEGIN_CASE(JSOP_BINDNAME
)
3062 id
= ATOM_TO_JSID(atom
);
3064 obj
= js_FindIdentifierBase(cx
, id
);
3069 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
3070 END_CASE(JSOP_BINDNAME
)
3072 BEGIN_CASE(JSOP_SETNAME
)
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
);
3080 ok
= OBJ_SET_PROPERTY(cx
, obj
, id
, &rval
);
3084 STORE_OPND(-1, rval
);
3085 END_CASE(JSOP_SETNAME
)
3087 #define INTEGER_OP(OP, EXTRA_CODE) \
3089 FETCH_INT(cx, -1, j); \
3090 FETCH_INT(cx, -2, i); \
3094 STORE_INT(cx, -1, i); \
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
)
3102 END_CASE(JSOP_BITOR
)
3104 BEGIN_CASE(JSOP_BITXOR
)
3106 END_CASE(JSOP_BITXOR
)
3108 BEGIN_CASE(JSOP_BITAND
)
3110 END_CASE(JSOP_BITAND
)
3112 #define RELATIONAL_OP(OP) \
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); \
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); \
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; \
3137 VALUE_TO_NUMBER(cx, lval, d); \
3138 VALUE_TO_NUMBER(cx, rval, d2); \
3139 cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \
3143 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
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)) \
3164 SAVE_SP_AND_PC(fp); \
3165 ok = ops->equality(cx, obj2, rval, &cond); \
3168 cond = cond OP JS_TRUE; \
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); \
3182 cond = cond OP JS_TRUE; \
3185 #define XML_EQUALITY_OP(OP) /* nothing */
3186 #define EXTENDED_EQUALITY_OP(OP) /* nothing */
3189 #define EQUALITY_OP(OP, IFNAN) \
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); \
3206 EXTENDED_EQUALITY_OP(OP) \
3207 /* Handle all undefined (=>NaN) and int combinations. */ \
3208 cond = lval OP rval; \
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)) { \
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; \
3228 VALUE_TO_NUMBER(cx, lval, d); \
3229 VALUE_TO_NUMBER(cx, rval, d2); \
3230 cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \
3235 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
3239 EQUALITY_OP(==, JS_FALSE
);
3243 EQUALITY_OP(!=, JS_TRUE
);
3246 #define STRICT_EQUALITY_OP(OP) \
3248 rval = FETCH_OPND(-1); \
3249 lval = FETCH_OPND(-2); \
3250 cond = js_StrictlyEqual(lval, rval) OP JS_TRUE; \
3252 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
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(==);
3268 len
= GET_JUMP_OFFSET(pc
);
3272 sp
[-depth
] = (jsval
)pc2
;
3276 BEGIN_CASE(JSOP_CASEX
)
3277 pc2
= (jsbytecode
*) sp
[-2-depth
];
3278 STRICT_EQUALITY_OP(==);
3281 len
= GET_JUMPX_OFFSET(pc
);
3285 sp
[-depth
] = (jsval
)pc2
;
3287 END_CASE(JSOP_CASEX
)
3306 #undef RELATIONAL_OP
3308 BEGIN_CASE(JSOP_LSH
)
3309 SIGNED_SHIFT_OP(<<);
3312 BEGIN_CASE(JSOP_RSH
)
3313 SIGNED_SHIFT_OP(>>);
3316 BEGIN_CASE(JSOP_URSH
)
3320 FETCH_INT(cx
, -1, j
);
3321 FETCH_UINT(cx
, -2, u
);
3324 STORE_UINT(cx
, -1, u
);
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
;
3343 ok
= ops
->concatenate(cx
, obj2
, rval
, &rval
);
3347 STORE_OPND(-1, rval
);
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
)) {
3358 str
= JSVAL_TO_STRING(lval
);
3359 ok
= (str2
= js_ValueToString(cx
, rval
)) != NULL
;
3362 sp
[-1] = STRING_TO_JSVAL(str2
);
3364 str2
= JSVAL_TO_STRING(rval
);
3365 ok
= (str
= js_ValueToString(cx
, lval
)) != NULL
;
3368 sp
[-2] = STRING_TO_JSVAL(str
);
3370 str
= js_ConcatStrings(cx
, str
, str2
);
3376 STORE_OPND(-1, STRING_TO_JSVAL(str
));
3378 VALUE_TO_NUMBER(cx
, lval
, d
);
3379 VALUE_TO_NUMBER(cx
, rval
, d2
);
3382 STORE_NUMBER(cx
, -1, d
);
3387 #define BINARY_OP(OP) \
3389 FETCH_NUMBER(cx, -1, d2); \
3390 FETCH_NUMBER(cx, -2, d); \
3393 STORE_NUMBER(cx, -1, d); \
3396 BEGIN_CASE(JSOP_SUB
)
3400 BEGIN_CASE(JSOP_MUL
)
3404 BEGIN_CASE(JSOP_DIV
)
3405 FETCH_NUMBER(cx
, -1, d2
);
3406 FETCH_NUMBER(cx
, -2, d
);
3410 /* XXX MSVC miscompiles such that (NaN == 0) */
3411 if (JSDOUBLE_IS_NaN(d2
))
3412 rval
= DOUBLE_TO_JSVAL(rt
->jsNaN
);
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
);
3420 rval
= DOUBLE_TO_JSVAL(rt
->jsPositiveInfinity
);
3421 STORE_OPND(-1, rval
);
3424 STORE_NUMBER(cx
, -1, d
);
3428 BEGIN_CASE(JSOP_MOD
)
3429 FETCH_NUMBER(cx
, -1, d2
);
3430 FETCH_NUMBER(cx
, -2, d
);
3433 STORE_OPND(-1, DOUBLE_TO_JSVAL(rt
->jsNaN
));
3436 /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
3437 if (!(JSDOUBLE_IS_FINITE(d
) && JSDOUBLE_IS_INFINITE(d2
)))
3440 STORE_NUMBER(cx
, -1, d
);
3444 BEGIN_CASE(JSOP_NOT
)
3445 POP_BOOLEAN(cx
, rval
, cond
);
3446 PUSH_OPND(BOOLEAN_TO_JSVAL(!cond
));
3449 BEGIN_CASE(JSOP_BITNOT
)
3450 FETCH_INT(cx
, -1, 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) {
3464 JS_ASSERT(INT_FITS_IN_JSVAL(i
));
3465 rval
= INT_TO_JSVAL(i
);
3467 if (JSVAL_IS_DOUBLE(rval
)) {
3468 d
= *JSVAL_TO_DOUBLE(rval
);
3471 ok
= js_ValueToNumber(cx
, rval
, &d
);
3477 * Negation of a zero doesn't produce a negative
3478 * zero on HPUX. Perform the operation by bit
3481 JSDOUBLE_HI32(d
) ^= JSDOUBLE_HI32_SIGNBIT
;
3485 ok
= js_NewNumberValue(cx
, d
, &rval
);
3489 STORE_OPND(-1, rval
);
3492 BEGIN_CASE(JSOP_POS
)
3493 rval
= FETCH_OPND(-1);
3494 if (!JSVAL_IS_NUMBER(rval
)) {
3496 ok
= js_ValueToNumber(cx
, rval
, &d
);
3499 ok
= js_NewNumberValue(cx
, d
, &rval
);
3504 sp
[-1-depth
] = (jsval
)pc
;
3507 BEGIN_CASE(JSOP_NEW
)
3508 /* Get immediate argc and find the constructor function. */
3509 argc
= GET_ARGC(pc
);
3513 vp
= sp
- (2 + argc
);
3514 JS_ASSERT(vp
>= fp
->spbase
);
3516 ok
= js_InvokeConstructor(cx
, vp
, argc
);
3520 LOAD_INTERRUPT_HANDLER(cx
);
3521 obj
= JSVAL_TO_OBJECT(*vp
);
3522 len
= js_CodeSpec
[op
].length
;
3525 BEGIN_CASE(JSOP_DELNAME
)
3527 id
= ATOM_TO_JSID(atom
);
3530 ok
= js_FindProperty(cx
, id
, &obj
, &obj2
, &prop
);
3534 /* ECMA says to return true if name is undefined or inherited. */
3537 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
3538 ok
= OBJ_DELETE_PROPERTY(cx
, obj
, id
, &rval
);
3543 END_CASE(JSOP_DELNAME
)
3545 BEGIN_CASE(JSOP_DELPROP
)
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
));
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);
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
)
3569 PUSH_OPND(JSVAL_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.
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
)
3589 id
= ATOM_TO_JSID(atom
);
3594 FETCH_OBJECT(cx
, i
, lval
, obj
);
3595 STORE_OPND(i
, OBJECT_TO_JSVAL(obj
));
3597 FETCH_ELEMENT_ID(obj
, -1, id
);
3600 BEGIN_CASE(JSOP_INCNAME
)
3601 BEGIN_CASE(JSOP_DECNAME
)
3602 BEGIN_CASE(JSOP_NAMEINC
)
3603 BEGIN_CASE(JSOP_NAMEDEC
)
3605 id
= ATOM_TO_JSID(atom
);
3608 ok
= js_FindProperty(cx
, id
, &obj
, &obj2
, &prop
);
3612 goto atom_not_defined
;
3614 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
3615 lval
= OBJECT_TO_JSVAL(obj
);
3620 const JSCodeSpec
*cs
;
3622 /* The operand must contain a number. */
3623 ok
= OBJ_GET_PROPERTY(cx
, obj
, id
, &rval
);
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
) {
3636 (cs
->format
& JOF_INC
) ? (rval
+= 2) : (rval
-= 2);
3638 (cs
->format
& JOF_INC
) ? (rval
+= 2) : (rval
-= 2);
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
3650 #define NONINT_INCREMENT_OP_MIDDLE() \
3652 VALUE_TO_NUMBER(cx, rval, d); \
3653 if (cs->format & JOF_POST) { \
3655 if (!JSVAL_IS_NUMBER(rtmp)) { \
3656 ok = js_NewNumberValue(cx, d, &rtmp); \
3661 (cs->format & JOF_INC) ? d++ : d--; \
3662 ok = js_NewNumberValue(cx, d, &rval); \
3664 (cs->format & JOF_INC) ? ++d : --d; \
3665 ok = js_NewNumberValue(cx, d, &rval); \
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.
3684 else vp
= NULL
; /* suppress bogus gcc warnings */
3687 NONINT_INCREMENT_OP_MIDDLE();
3690 fp
->flags
|= JSFRAME_ASSIGNING
;
3691 ok
= OBJ_SET_PROPERTY(cx
, obj
, id
, &rval
);
3692 fp
->flags
&= ~JSFRAME_ASSIGNING
;
3697 len
= js_CodeSpec
[op
].length
;
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) \
3704 JS_ASSERT(slot < fp->fun->COUNT); \
3705 METER_SLOT_OP(op, slot); \
3706 vp = fp->BASE + slot; \
3708 if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \
3709 goto do_nonint_fast_incop; \
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 */
3738 #undef FAST_INCREMENT_OP
3740 do_nonint_fast_incop
:
3742 const JSCodeSpec
*cs
= &js_CodeSpec
[op
];
3744 NONINT_INCREMENT_OP_MIDDLE();
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)) { \
3761 slot = JSVAL_TO_INT(lval); \
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; \
3768 OBJ_SET_SLOT(cx, obj, slot, rval); \
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
);
3786 #undef FAST_GLOBAL_INCREMENT_OP
3788 do_nonint_fast_global_incop
:
3790 const JSCodeSpec
*cs
= &js_CodeSpec
[op
];
3794 NONINT_INCREMENT_OP_MIDDLE();
3795 OBJ_SET_SLOT(cx
, obj
, slot
, rval
);
3796 STORE_OPND(-1, rtmp
);
3801 BEGIN_CASE(JSOP_GETTHISPROP
)
3803 id
= ATOM_TO_JSID(atom
);
3806 ok
= OBJ_GET_PROPERTY(cx
, obj
, id
, &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
)
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. */
3840 len
= JSOP_GETPROP_LENGTH
;
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
));
3848 id
= ATOM_TO_JSID(atom
);
3850 VALUE_TO_OBJECT(cx
, -1, lval
, obj
);
3851 ok
= OBJ_GET_PROPERTY(cx
, obj
, id
, &rval
);
3855 STORE_OPND(-1, rval
);
3858 BEGIN_CASE(JSOP_SETPROP
)
3859 /* Get an immediate atom naming the property. */
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
));
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
));
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
));
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);
3897 FETCH_OBJECT(cx
, -2, lval
, obj
);
3898 FETCH_ELEMENT_ID(obj
, -1, id
);
3899 ok
= OBJ_SET_PROPERTY(cx
, obj
, id
, &rval
);
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);
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
;
3918 jsuword avail
, nbytes
;
3922 JSInlineFrame
*newifp
;
3923 JSInterpreterHook hook
;
3925 /* Compute the total number of stack slots needed by fun. */
3926 nframeslots
= JS_HOWMANY(sizeof(JSInlineFrame
),
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
;
3938 newmark
= (void *) avail
;
3940 newsp
= sp
+ missing
;
3941 overflow
= (jsuword
) newsp
> a
->limit
;
3943 nslots
+= 2 + argc
+ missing
;
3944 else if ((jsuword
) newsp
> avail
)
3945 avail
= a
->avail
= (jsuword
) newsp
;
3948 else overflow
= JS_FALSE
; /* suppress bogus gcc warnings */
3951 /* Allocate the inline frame with its vars and operands. */
3952 newsp
= (jsval
*) avail
;
3953 nbytes
= nslots
* sizeof(jsval
);
3955 if (avail
<= a
->limit
) {
3958 JS_ARENA_ALLOCATE_CAST(newsp
, jsval
*, &cx
->stackPool
,
3961 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3962 JSMSG_STACK_OVERFLOW
,
3964 ? JS_GetFunctionName(fp
->fun
)
3966 goto bad_inline_call
;
3971 * Move args if missing overflow arena a, then push any
3977 memcpy(newsp
, vp
, (2 + argc
) * sizeof(jsval
));
3980 newsp
= sp
+ missing
;
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
;
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);
4021 /* Push void to initialize local variables. */
4026 newifp
->frame
.spbase
= sp
;
4027 SAVE_SP(&newifp
->frame
);
4029 /* Call the debugger hook if present. */
4030 hook
= cx
->debugHooks
->callHook
;
4032 newifp
->frame
.pc
= NULL
;
4033 newifp
->hookData
= hook(cx
, &newifp
->frame
, JS_TRUE
, 0,
4034 cx
->debugHooks
->callHookData
);
4035 LOAD_INTERRUPT_HANDLER(cx
);
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
;
4057 #if !JS_THREADED_INTERP
4058 endpc
= pc
+ script
->length
;
4061 JS_RUNTIME_METER(rt
, inlineCalls
);
4063 /* Load first op and dispatch it (safe since JSOP_STOP). */
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
);
4078 if (fun
->flags
& JSFUN_FAST_NATIVE
) {
4079 uintN nargs
= JS_MAX(argc
, fun
->u
.n
.minargs
);
4081 nargs
+= fun
->u
.n
.extra
;
4084 * If we can't fit missing args and local roots in
4085 * this frame's operand stack, take the slow path.
4088 if (sp
+ nargs
> fp
->spbase
+ depth
)
4092 } while (--nargs
!= 0);
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
);
4105 vp
[-depth
] = (jsval
)pc
;
4111 ok
= js_Invoke(cx
, argc
, 0);
4113 LOAD_INTERRUPT_HANDLER(cx
);
4116 JS_RUNTIME_METER(rt
, nonInlineCalls
);
4119 #if JS_HAS_LVALUE_RETURN
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
));
4139 STORE_OPND(-1, rval
);
4140 cx
->rval2set
= JS_FALSE
;
4142 #endif /* JS_HAS_LVALUE_RETURN */
4145 #if JS_HAS_LVALUE_RETURN
4146 BEGIN_CASE(JSOP_SETCALL
)
4147 argc
= GET_ARGC(pc
);
4149 ok
= js_Invoke(cx
, argc
, 0);
4151 LOAD_INTERRUPT_HANDLER(cx
);
4154 if (!cx
->rval2set
) {
4155 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
4156 JSMSG_BAD_LEFTSIDE_OF_ASS
);
4160 PUSH_OPND(cx
->rval2
);
4161 cx
->rval2set
= JS_FALSE
;
4162 END_CASE(JSOP_SETCALL
)
4165 BEGIN_CASE(JSOP_NAME
)
4166 BEGIN_CASE(JSOP_CALLNAME
)
4168 id
= ATOM_TO_JSID(atom
);
4171 ok
= js_FindProperty(cx
, id
, &obj
, &obj2
, &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
++) {
4180 if (op2
== JSOP_TYPEOF
) {
4181 PUSH_OPND(JSVAL_VOID
);
4184 if (op2
!= JSOP_GROUP
)
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
);
4197 sprop
= (JSScopeProperty
*)prop
;
4198 NATIVE_GET(cx
, obj
, obj2
, sprop
, &rval
);
4199 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
4202 if (op
== JSOP_CALLNAME
) {
4203 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
4205 ok
= ComputeThis(cx
, sp
);
4211 BEGIN_CASE(JSOP_UINT16
)
4212 i
= (jsint
) GET_UINT16(pc
);
4213 rval
= INT_TO_JSVAL(i
);
4215 END_CASE(JSOP_UINT16
)
4217 BEGIN_CASE(JSOP_UINT24
)
4218 i
= (jsint
) GET_UINT24(pc
);
4219 rval
= INT_TO_JSVAL(i
);
4221 END_CASE(JSOP_UINT24
)
4223 BEGIN_CASE(JSOP_INT8
)
4225 rval
= INT_TO_JSVAL(i
);
4229 BEGIN_CASE(JSOP_INT32
)
4231 rval
= INT_TO_JSVAL(i
);
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
)
4257 PUSH_OPND(ATOM_KEY(atom
));
4258 END_CASE(JSOP_DOUBLE
)
4260 BEGIN_CASE(JSOP_OBJECT
)
4262 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
4263 END_CASE(JSOP_OBJECT
)
4265 BEGIN_CASE(JSOP_REGEXP
)
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
);
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
))
4308 if (JSVAL_IS_VOID(rval
))
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
];
4320 funobj
= NULL
; /* suppress bogus gcc warnings */
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
)
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.
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
);
4371 rval
= OBJECT_TO_JSVAL(obj
);
4373 /* Store the regexp object value in its cloneIndex slot. */
4375 if (!JS_SetReservedSlot(cx
, funobj
, slot
, rval
))
4378 fp
->vars
[slot
] = rval
;
4384 END_CASE(JSOP_REGEXP
)
4386 BEGIN_CASE(JSOP_ZERO
)
4387 PUSH_OPND(JSVAL_ZERO
);
4390 BEGIN_CASE(JSOP_ONE
)
4391 PUSH_OPND(JSVAL_ONE
);
4394 BEGIN_CASE(JSOP_NULL
)
4395 PUSH_OPND(JSVAL_NULL
);
4398 BEGIN_CASE(JSOP_THIS
)
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
);
4414 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
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
);
4425 BEGIN_CASE(JSOP_TABLESWITCH
)
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.)
4435 if (!JSVAL_IS_INT(rval
))
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
);
4445 if ((jsuint
)i
< (jsuint
)(high
- low
+ 1)) {
4446 pc2
+= JUMP_OFFSET_LEN
+ JUMP_OFFSET_LEN
* i
;
4447 off
= (jsint
) GET_JUMP_OFFSET(pc2
);
4453 BEGIN_CASE(JSOP_TABLESWITCHX
)
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.)
4463 if (!JSVAL_IS_INT(rval
))
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
);
4473 if ((jsuint
)i
< (jsuint
)(high
- low
+ 1)) {
4474 pc2
+= JUMP_OFFSET_LEN
+ JUMPX_OFFSET_LEN
* i
;
4475 off
= (jsint
) GET_JUMPX_OFFSET(pc2
);
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
;
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
);
4497 if (!JSVAL_IS_NUMBER(lval
) &&
4498 !JSVAL_IS_STRING(lval
) &&
4499 !JSVAL_IS_BOOLEAN(lval
)) {
4500 goto end_lookup_switch
;
4504 npairs
= (jsint
) GET_UINT16(pc2
);
4506 JS_ASSERT(npairs
); /* empty switch uses JSOP_TABLESWITCH */
4508 #define SEARCH_PAIRS(MATCH_CODE) \
4510 JS_ASSERT(GET_INDEX(pc2) < script->atomMap.length); \
4511 atom = atoms[GET_INDEX(pc2)]; \
4512 rval = ATOM_KEY(atom); \
4518 if (--npairs == 0) { \
4523 if (JSVAL_IS_STRING(lval
)) {
4524 str
= JSVAL_TO_STRING(lval
);
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
);
4533 match
= (JSVAL_IS_DOUBLE(rval
) &&
4534 *JSVAL_TO_DOUBLE(rval
) == d
);
4538 match
= (lval
== rval
);
4544 len
= (op
== JSOP_LOOKUPSWITCH
)
4545 ? GET_JUMP_OFFSET(pc2
)
4546 : GET_JUMPX_OFFSET(pc2
);
4549 EMPTY_CASE(JSOP_CONDSWITCH
)
4551 #if JS_HAS_EXPORT_IMPORT
4552 BEGIN_CASE(JSOP_EXPORTALL
)
4555 ida
= JS_Enumerate(cx
, obj
);
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
);
4566 ok
= OBJ_GET_ATTRIBUTES(cx
, obj
, id
, prop
, &attrs
);
4568 attrs
|= JSPROP_EXPORTED
;
4569 ok
= OBJ_SET_ATTRIBUTES(cx
, obj
, id
, prop
, &attrs
);
4571 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
4575 JS_DestroyIdArray(cx
, ida
);
4577 END_CASE(JSOP_EXPORTALL
)
4579 BEGIN_CASE(JSOP_EXPORTNAME
)
4581 id
= ATOM_TO_JSID(atom
);
4584 ok
= OBJ_LOOKUP_PROPERTY(cx
, obj
, id
, &obj2
, &prop
);
4588 ok
= OBJ_DEFINE_PROPERTY(cx
, obj
, id
, JSVAL_VOID
, NULL
, NULL
,
4589 JSPROP_EXPORTED
, NULL
);
4591 ok
= OBJ_GET_ATTRIBUTES(cx
, obj
, id
, prop
, &attrs
);
4593 attrs
|= JSPROP_EXPORTED
;
4594 ok
= OBJ_SET_ATTRIBUTES(cx
, obj
, id
, prop
, &attrs
);
4596 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
4600 END_CASE(JSOP_EXPORTNAME
)
4602 BEGIN_CASE(JSOP_IMPORTALL
)
4603 id
= (jsid
) JSVAL_VOID
;
4604 PROPERTY_OP(-1, ok
= ImportProperty(cx
, obj
, id
));
4606 END_CASE(JSOP_IMPORTALL
)
4608 BEGIN_CASE(JSOP_IMPORTPROP
)
4609 /* Get an immediate atom naming the property. */
4611 id
= ATOM_TO_JSID(atom
);
4612 PROPERTY_OP(-1, ok
= ImportProperty(cx
, obj
, id
));
4614 END_CASE(JSOP_IMPORTPROP
)
4616 BEGIN_CASE(JSOP_IMPORTELEM
)
4617 ELEMENT_OP(-1, ok
= ImportProperty(cx
, obj
, id
));
4619 END_CASE(JSOP_IMPORTELEM
)
4620 #endif /* JS_HAS_EXPORT_IMPORT */
4622 BEGIN_CASE(JSOP_TRAP
)
4624 switch (JS_HandleTrap(cx
, script
, pc
, &rval
)) {
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
);
4638 cx
->throwing
= JS_TRUE
;
4639 cx
->exception
= rval
;
4644 LOAD_INTERRUPT_HANDLER(cx
);
4647 BEGIN_CASE(JSOP_ARGUMENTS
)
4649 ok
= js_GetArgsValue(cx
, fp
, &rval
);
4653 END_CASE(JSOP_ARGUMENTS
)
4655 BEGIN_CASE(JSOP_ARGSUB
)
4656 id
= INT_TO_JSID(GET_ARGNO(pc
));
4658 ok
= js_GetArgsProperty(cx
, fp
, id
, &rval
);
4662 END_CASE(JSOP_ARGSUB
)
4664 BEGIN_CASE(JSOP_ARGCNT
)
4665 id
= ATOM_TO_JSID(rt
->atomState
.lengthAtom
);
4667 ok
= js_GetArgsProperty(cx
, fp
, id
, &rval
);
4671 END_CASE(JSOP_ARGCNT
)
4673 #define PUSH_GLOBAL_THIS(cx,sp) \
4675 PUSH_OPND(JSVAL_NULL); \
4676 SAVE_SP_AND_PC(fp); \
4677 ok = ComputeGlobalThis(cx, sp); \
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
];
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
];
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
;
4734 slot
= JSVAL_TO_INT(lval
);
4736 rval
= OBJ_GET_SLOT(cx
, obj
, slot
);
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
];
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.
4756 id
= ATOM_TO_JSID(atom
);
4758 ok
= OBJ_SET_PROPERTY(cx
, obj
, id
, &rval
);
4761 STORE_OPND(-1, rval
);
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
;
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
);
4789 ok
= js_CheckRedeclaration(cx
, obj
, id
, attrs
, &obj2
, &prop
);
4793 /* Bind a variable only if it's not yet defined. */
4795 ok
= OBJ_DEFINE_PROPERTY(cx
, obj
, id
, JSVAL_VOID
, NULL
, NULL
,
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
) &&
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
)
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
);
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
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
);
4899 attrs
|= flags
| JSPROP_SHARED
;
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
;
4911 ok
= js_CheckRedeclaration(cx
, parent
, id
, attrs
, NULL
, NULL
);
4913 ok
= OBJ_DEFINE_PROPERTY(cx
, parent
, id
, rval
,
4914 (flags
& JSPROP_GETTER
)
4915 ? JS_EXTENSION (JSPropertyOp
) obj
4917 (flags
& JSPROP_SETTER
)
4918 ? JS_EXTENSION (JSPropertyOp
) obj
4924 /* Restore fp->scopeChain now that obj is defined in fp->varobj. */
4925 fp
->scopeChain
= obj2
;
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
);
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
))
4980 /* If re-parenting, store a clone of the function object. */
4981 if (OBJ_GET_PARENT(cx
, obj
) != parent
) {
4983 obj
= js_CloneFunctionObject(cx
, obj
, parent
);
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. */
4997 /* If re-parenting, push a clone of the function object. */
4999 parent
= js_GetScopeChain(cx
, fp
);
5004 if (OBJ_GET_PARENT(cx
, obj
) != parent
) {
5005 obj
= js_CloneFunctionObject(cx
, obj
, parent
);
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.]. */
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.
5028 obj2
= js_GetScopeChain(cx
, fp
);
5033 parent
= js_NewObject(cx
, &js_ObjectClass
, NULL
, obj2
);
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
);
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
);
5076 attrs
|= JSPROP_SHARED
;
5079 ok
= OBJ_DEFINE_PROPERTY(cx
, parent
, ATOM_TO_JSID(fun
->atom
), rval
,
5080 (attrs
& JSPROP_GETTER
)
5081 ? JS_EXTENSION (JSPropertyOp
) obj
5083 (attrs
& JSPROP_SETTER
)
5084 ? JS_EXTENSION (JSPropertyOp
) obj
5087 JSPROP_ENUMERATE
| JSPROP_PERMANENT
|
5091 /* Restore fp->scopeChain now that obj is defined in parent. */
5092 fp
->scopeChain
= obj2
;
5094 cx
->weakRoots
.newborn
[GCX_OBJECT
] = NULL
;
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).
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.
5121 obj2
= js_GetScopeChain(cx
, fp
);
5126 if (OBJ_GET_PARENT(cx
, obj
) != obj2
) {
5127 obj
= js_CloneFunctionObject(cx
, obj
, obj2
);
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
);
5150 attrs
|= JSPROP_SHARED
;
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
5158 (attrs
& JSPROP_SETTER
)
5159 ? JS_EXTENSION (JSPropertyOp
) obj
5161 attrs
| JSPROP_ENUMERATE
5165 /* Restore fp->scopeChain now that obj is defined in fp->varobj. */
5166 fp
->scopeChain
= obj2
;
5168 cx
->weakRoots
.newborn
[GCX_OBJECT
] = NULL
;
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
)
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
;
5193 id
= ATOM_TO_JSID(atom
);
5194 rval
= FETCH_OPND(-1);
5199 rval
= FETCH_OPND(-1);
5204 FETCH_OBJECT(cx
, i
- 1, lval
, obj
);
5208 JS_ASSERT(sp
- fp
->spbase
>= 2);
5209 rval
= FETCH_OPND(-1);
5212 id
= ATOM_TO_JSID(atom
);
5216 JS_ASSERT(sp
- fp
->spbase
>= 3);
5217 rval
= FETCH_OPND(-1);
5221 lval
= FETCH_OPND(i
-1);
5222 JS_ASSERT(JSVAL_IS_OBJECT(lval
));
5223 obj
= JSVAL_TO_OBJECT(lval
);
5231 /* Ensure that id has a type suitable for use with obj. */
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
,
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
);
5253 if (op
== JSOP_GETTER
) {
5254 getter
= JS_EXTENSION (JSPropertyOp
) JSVAL_TO_OBJECT(rval
);
5256 attrs
= JSPROP_GETTER
;
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
);
5269 ok
= OBJ_DEFINE_PROPERTY(cx
, obj
, id
, JSVAL_VOID
, getter
, setter
,
5275 if (js_CodeSpec
[op2
].ndefs
)
5276 STORE_OPND(-1, rval
);
5277 len
= js_CodeSpec
[op2
].length
;
5279 #endif /* JS_HAS_GETTER_SETTER */
5281 BEGIN_CASE(JSOP_NEWINIT
)
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. */
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);
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);
5315 FETCH_ELEMENT_ID(obj
, -2, id
);
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
,
5328 ok
= OBJ_SET_PROPERTY(cx
, obj
, id
, &rval
);
5332 len
= js_CodeSpec
[op
].length
;
5335 #if JS_HAS_SHARP_VARS
5336 BEGIN_CASE(JSOP_DEFSHARP
)
5338 obj
= fp
->sharpArray
;
5340 obj
= js_NewArrayObject(cx
, 0, NULL
);
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
)) {
5352 JS_snprintf(numBuf
, sizeof numBuf
, "%u", (unsigned) i
);
5353 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
5354 JSMSG_BAD_SHARP_DEF
, numBuf
);
5358 ok
= OBJ_SET_PROPERTY(cx
, obj
, id
, &rval
);
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
;
5371 ok
= OBJ_GET_PROPERTY(cx
, obj
, id
, &rval
);
5375 if (!JSVAL_IS_OBJECT(rval
)) {
5377 JS_snprintf(numBuf
, sizeof numBuf
, "%u", (unsigned) i
);
5380 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
5381 JSMSG_BAD_SHARP_USE
, numBuf
);
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
)
5395 i
= PTRDIFF(pc
, script
->main
, jsbytecode
) + JSOP_GOSUB_LENGTH
;
5396 len
= GET_JUMP_OFFSET(pc
);
5397 PUSH(INT_TO_JSVAL(i
));
5400 BEGIN_CASE(JSOP_GOSUBX
)
5402 i
= PTRDIFF(pc
, script
->main
, jsbytecode
) + JSOP_GOSUBX_LENGTH
;
5403 len
= GET_JUMPX_OFFSET(pc
);
5404 PUSH(INT_TO_JSVAL(i
));
5407 BEGIN_CASE(JSOP_RETSUB
)
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
;
5423 JS_ASSERT(JSVAL_IS_INT(rval
));
5424 len
= JSVAL_TO_INT(rval
);
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();
5445 /* let the code at out try to catch the exception. */
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
)
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
,
5469 lval
= FETCH_OPND(-2);
5471 ok
= obj
->map
->ops
->hasInstance(cx
, obj
, lval
, &cond
);
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
;
5484 switch (handler(cx
, script
, pc
, &rval
,
5485 cx
->debugHooks
->debuggerHandlerData
)) {
5489 case JSTRAP_CONTINUE
:
5495 cx
->throwing
= JS_TRUE
;
5496 cx
->exception
= rval
;
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
)
5511 ok
= js_SetDefaultXMLNamespace(cx
, rval
);
5514 END_CASE(JSOP_DEFXMLNS
)
5516 BEGIN_CASE(JSOP_ANYNAME
)
5518 ok
= js_GetAnyName(cx
, &rval
);
5522 END_CASE(JSOP_ANYNAME
)
5524 BEGIN_CASE(JSOP_QNAMEPART
)
5526 PUSH_OPND(ATOM_KEY(atom
));
5527 END_CASE(JSOP_QNAMEPART
)
5529 BEGIN_CASE(JSOP_QNAMECONST
)
5531 rval
= ATOM_KEY(atom
);
5532 lval
= FETCH_OPND(-1);
5534 obj
= js_ConstructXMLQNameObject(cx
, lval
, rval
);
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);
5546 obj
= js_ConstructXMLQNameObject(cx
, lval
, rval
);
5552 STORE_OPND(-1, OBJECT_TO_JSVAL(obj
));
5553 END_CASE(JSOP_QNAME
)
5555 BEGIN_CASE(JSOP_TOATTRNAME
)
5556 rval
= FETCH_OPND(-1);
5558 ok
= js_ToAttributeName(cx
, &rval
);
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
));
5568 str
= js_EscapeAttributeValue(cx
, JSVAL_TO_STRING(rval
));
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
);
5583 str
= js_AddAttributePart(cx
, op
== JSOP_ADDATTRNAME
, str
, str2
);
5589 STORE_OPND(-1, STRING_TO_JSVAL(str
));
5590 END_CASE(JSOP_ADDATTRNAME
)
5592 BEGIN_CASE(JSOP_BINDXMLNAME
)
5593 lval
= FETCH_OPND(-1);
5595 ok
= js_FindXMLProperty(cx
, lval
, &obj
, &id
);
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);
5606 FETCH_ELEMENT_ID(obj
, -2, id
);
5607 ok
= OBJ_SET_PROPERTY(cx
, obj
, id
, &rval
);
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);
5618 ok
= js_FindXMLProperty(cx
, lval
, &obj
, &id
);
5621 ok
= OBJ_GET_PROPERTY(cx
, obj
, id
, &rval
);
5624 STORE_OPND(-1, rval
);
5625 if (op
== JSOP_CALLXMLNAME
) {
5626 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
5628 ok
= ComputeThis(cx
, sp
);
5632 END_CASE(JSOP_XMLNAME
)
5634 BEGIN_CASE(JSOP_DESCENDANTS
)
5635 BEGIN_CASE(JSOP_DELDESC
)
5637 FETCH_OBJECT(cx
, -2, lval
, obj
);
5638 rval
= FETCH_OPND(-1);
5639 ok
= js_GetXMLDescendants(cx
, obj
, rval
, &rval
);
5643 if (op
== JSOP_DELDESC
) {
5644 sp
[-1] = rval
; /* set local root */
5645 ok
= js_DeleteXMLListElements(cx
, JSVAL_TO_OBJECT(rval
));
5648 rval
= JSVAL_TRUE
; /* always succeed */
5652 STORE_OPND(-1, rval
);
5653 END_CASE(JSOP_DESCENDANTS
)
5655 BEGIN_CASE(JSOP_FILTER
)
5656 len
= GET_JUMP_OFFSET(pc
);
5658 FETCH_OBJECT(cx
, -1, lval
, obj
);
5659 ok
= js_FilterXMLList(cx
, obj
, pc
+ js_CodeSpec
[op
].length
, &rval
);
5662 JS_ASSERT(fp
->sp
== sp
);
5663 STORE_OPND(-1, rval
);
5666 BEGIN_CASE(JSOP_ENDFILTER
)
5667 *result
= POP_OPND();
5670 EMPTY_CASE(JSOP_STARTXML
)
5671 EMPTY_CASE(JSOP_STARTXMLEXPR
)
5673 BEGIN_CASE(JSOP_TOXML
)
5674 rval
= FETCH_OPND(-1);
5676 obj
= js_ValueToXMLObject(cx
, rval
);
5681 STORE_OPND(-1, OBJECT_TO_JSVAL(obj
));
5682 END_CASE(JSOP_TOXML
)
5684 BEGIN_CASE(JSOP_TOXMLLIST
)
5685 rval
= FETCH_OPND(-1);
5687 obj
= js_ValueToXMLListObject(cx
, rval
);
5692 STORE_OPND(-1, OBJECT_TO_JSVAL(obj
));
5693 END_CASE(JSOP_TOXMLLIST
)
5695 BEGIN_CASE(JSOP_XMLTAGEXPR
)
5696 rval
= FETCH_OPND(-1);
5698 str
= js_ValueToString(cx
, rval
);
5703 STORE_OPND(-1, STRING_TO_JSVAL(str
));
5704 END_CASE(JSOP_XMLTAGEXPR
)
5706 BEGIN_CASE(JSOP_XMLELTEXPR
)
5707 rval
= FETCH_OPND(-1);
5709 if (VALUE_IS_XML(cx
, rval
)) {
5710 str
= js_ValueToXMLString(cx
, rval
);
5712 str
= js_ValueToString(cx
, rval
);
5714 str
= js_EscapeElementValue(cx
, str
);
5720 STORE_OPND(-1, STRING_TO_JSVAL(str
));
5721 END_CASE(JSOP_XMLELTEXPR
)
5723 BEGIN_CASE(JSOP_XMLOBJECT
)
5726 obj
= js_CloneXMLObject(cx
, obj
);
5731 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
5732 END_CASE(JSOP_XMLOBJECT
)
5734 BEGIN_CASE(JSOP_XMLCDATA
)
5736 str
= ATOM_TO_STRING(atom
);
5737 obj
= js_NewXMLSpecialObject(cx
, JSXML_CLASS_TEXT
, NULL
, str
);
5742 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
5743 END_CASE(JSOP_XMLCDATA
)
5745 BEGIN_CASE(JSOP_XMLCOMMENT
)
5747 str
= ATOM_TO_STRING(atom
);
5748 obj
= js_NewXMLSpecialObject(cx
, JSXML_CLASS_COMMENT
, NULL
, str
);
5753 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
5754 END_CASE(JSOP_XMLCOMMENT
)
5756 BEGIN_CASE(JSOP_XMLPI
)
5758 str
= ATOM_TO_STRING(atom
);
5759 rval
= FETCH_OPND(-1);
5760 str2
= JSVAL_TO_STRING(rval
);
5762 obj
= js_NewXMLSpecialObject(cx
,
5763 JSXML_CLASS_PROCESSING_INSTRUCTION
,
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. */
5775 id
= ATOM_TO_JSID(atom
);
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
);
5791 ok
= OBJ_GET_PROPERTY(cx
, obj
, id
, &rval
);
5795 STORE_OPND(-1, OBJECT_TO_JSVAL(obj
));
5796 STORE_OPND(-2, rval
);
5797 ok
= ComputeThis(cx
, sp
);
5801 if (JSVAL_IS_STRING(lval
)) {
5803 } else if (JSVAL_IS_NUMBER(lval
)) {
5805 } else if (JSVAL_IS_BOOLEAN(lval
)) {
5806 i
= JSProto_Boolean
;
5808 JS_ASSERT(JSVAL_IS_NULL(lval
) || JSVAL_IS_VOID(lval
));
5809 js_ReportValueError(cx
, JSMSG_NO_PROPERTIES
, -2, lval
,
5815 ok
= js_GetClassPrototype(cx
, NULL
, INT_TO_JSID(i
), &obj
);
5819 ok
= OBJ_GET_PROPERTY(cx
, obj
, id
, &rval
);
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]);
5835 END_CASE(JSOP_CALLPROP
)
5837 BEGIN_CASE(JSOP_GETFUNNS
)
5839 ok
= js_GetFunctionNamespace(cx
, &rval
);
5843 END_CASE(JSOP_GETFUNNS
)
5844 #endif /* JS_HAS_XML_SUPPORT */
5846 BEGIN_CASE(JSOP_ENTERBLOCK
)
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
);
5852 STORE_OPND(0, JSVAL_VOID
);
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
);
5877 obj
= js_CloneBlockObject(cx
, obj
, parent
, fp
);
5882 fp
->scopeChain
= obj
;
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
)
5896 /* Grab the result of the expression. */
5897 if (op
== JSOP_LEAVEBLOCKEXPR
)
5898 rval
= FETCH_OPND(-1);
5900 chainp
= &fp
->blockChain
;
5903 chainp
= &fp
->scopeChain
;
5907 * This block was cloned, so clear its private data and sync
5908 * its locals to their property slots.
5911 ok
= js_PutBlockObject(cx
, obj
);
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
||
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
];
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; \
5958 if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \
5959 goto do_nonint_fast_incop; \
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.
5989 ok
= js_CloseIterator(cx
, sp
[-1]);
5993 END_CASE(JSOP_ENDITER
)
5995 #if JS_HAS_GENERATORS
5996 BEGIN_CASE(JSOP_GENERATOR
)
5997 pc
+= JSOP_GENERATOR_LENGTH
;
5999 obj
= js_NewGenerator(cx
, fp
);
6003 JS_ASSERT(!fp
->callobj
&& !fp
->argsobj
);
6004 fp
->rval
= OBJECT_TO_JSVAL(obj
);
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
);
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
);
6023 fp
->rval
= FETCH_OPND(-1);
6024 fp
->flags
|= JSFRAME_YIELDING
;
6025 pc
+= JSOP_YIELD_LENGTH
;
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
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
);
6052 id
= INT_TO_JSID(i
);
6055 ok
= OBJ_SET_PROPERTY(cx
, obj
, id
, &rval
);
6059 END_CASE(JSOP_ARRAYPUSH
)
6060 #endif /* JS_HAS_GENERATORS */
6062 #if !JS_HAS_GENERATORS
6068 #if !JS_HAS_DESTRUCTURING
6069 L_JSOP_FOREACHKEYVAL
:
6070 L_JSOP_ENUMCONSTELEM
:
6073 #if JS_THREADED_INTERP
6075 L_JSOP_BACKPATCH_POP
:
6081 JS_snprintf(numBuf
, sizeof numBuf
, "%d", op
);
6082 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6083 JSMSG_BAD_BYTECODE
, numBuf
);
6088 #if !JS_THREADED_INTERP
6100 ndefs
= js_CodeSpec
[op
].ndefs
;
6103 if (op
== JSOP_FORELEM
&& sp
[-1] == JSVAL_FALSE
)
6105 for (n
= -ndefs
; n
< 0; n
++) {
6106 char *bytes
= js_DecompileValueGenerator(cx
, n
, sp
[n
],
6109 fprintf(tracefp
, "%s %s",
6110 (n
== -ndefs
) ? " output:" : ",",
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
);
6121 fputs("<null>", tracefp
);
6123 js_FileEscapedString(tracefp
, str
, 0);
6124 fputc(' ', tracefp
);
6126 fputc('\n', tracefp
);
6130 #endif /* !JS_THREADED_INTERP */
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:
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
;
6164 * Call debugger throw hook if set (XXX thread safety?).
6166 handler
= cx
->debugHooks
->throwHook
;
6169 switch (handler(cx
, script
, pc
, &rval
,
6170 cx
->debugHooks
->throwHookData
)) {
6172 cx
->throwing
= JS_FALSE
;
6176 cx
->throwing
= JS_FALSE
;
6180 cx
->exception
= rval
;
6181 case JSTRAP_CONTINUE
:
6184 LOAD_INTERRUPT_HANDLER(cx
);
6188 * Look for a try block in script that can catch this exception.
6190 if (script
->trynotesOffset
== 0)
6193 offset
= (uint32
)(pc
- script
->main
);
6194 tn
= JS_SCRIPT_TRYNOTES(script
)->vector
;
6195 tnlimit
= tn
+ JS_SCRIPT_TRYNOTES(script
)->length
;
6197 if (offset
- tn
->start
>= tn
->length
)
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
)
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.
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
)
6235 fp
->blockChain
= obj
;
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
)
6242 if (JS_GetPrivate(cx
, obj
) != fp
||
6243 OBJ_BLOCK_DEPTH(cx
, obj
) < i
) {
6246 if (clasp
== &js_BlockClass
) {
6247 /* Don't fail until after we've updated all stacks. */
6248 ok
&= js_PutBlockObject(cx
, obj
);
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
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
;
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
))
6282 * Don't clear cx->throwing to save cx->exception from GC
6283 * until it is pushed to the stack via [exception] in the
6291 * Push (true, exception) pair for finally to indicate that
6292 * [retsub] should rethrow the exception.
6295 PUSH(cx
->exception
);
6296 cx
->throwing
= JS_FALSE
;
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
;
6310 ok
= js_CloseIterator(cx
, sp
[-2]);
6314 * close generated a new exception error or an error,
6315 * restart the handler search to properly notify the
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
6331 } while (++tn
!= tnlimit
);
6334 #if JS_HAS_GENERATORS
6335 if (JS_UNLIKELY(cx
->throwing
&& cx
->exception
== JSVAL_ARETURN
)) {
6336 cx
->throwing
= JS_FALSE
;
6338 fp
->rval
= JSVAL_VOID
;
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
)
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
) {
6360 ok
&= PutBlockObjects(cx
, fp
);
6363 fp
->sp
= fp
->spbase
;
6365 js_FreeRawStack(cx
, mark
);
6371 if (cx
->version
== currentVersion
&& currentVersion
!= originalVersion
)
6372 js_SetVersion(cx
, originalVersion
);
6378 const char *printable
= js_AtomToPrintableString(cx
, atom
);
6380 js_ReportIsNotDefined(cx
, printable
);