1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=79:
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 ***** */
41 /* This file needs to be included in possibly multiple places. */
43 #if JS_THREADED_INTERP
45 #else /* !JS_THREADED_INTERP */
47 JS_ASSERT(switchMask
== -1);
48 #endif /* !JS_THREADED_INTERP */
50 bool moreInterrupts
= false;
51 JSInterruptHook hook
= cx
->debugHooks
->interruptHook
;
54 if (TRACE_RECORDER(cx
))
55 AbortRecording(cx
, "interrupt hook");
57 switch (hook(cx
, script
, regs
.pc
, &rval
,
58 cx
->debugHooks
->interruptHookData
)) {
68 cx
->throwing
= JS_TRUE
;
73 moreInterrupts
= true;
77 if (TraceRecorder
* tr
= TRACE_RECORDER(cx
)) {
78 AbortableRecordingStatus status
= tr
->monitorRecording(op
);
79 JS_ASSERT_IF(cx
->throwing
, status
== ARECORD_ERROR
);
81 case ARECORD_CONTINUE
:
82 moreInterrupts
= true;
85 atoms
= COMMON_ATOMS_START(&rt
->atomState
);
87 DO_OP(); /* keep interrupting for op. */
90 // The code at 'error:' aborts the recording.
93 case ARECORD_COMPLETED
:
96 /* A 'stop' error should have already aborted recording. */
98 JS_NOT_REACHED("Bad recording status");
101 #endif /* !JS_TRACER */
103 #if JS_THREADED_INTERP
106 ExitTraceVisState(cx
, R_ABORT
);
108 jumpTable
= moreInterrupts
? interruptJumpTable
: normalJumpTable
;
109 JS_EXTENSION_(goto *normalJumpTable
[op
]);
111 switchMask
= moreInterrupts
? -1 : 0;
117 /* No-ops for ease of decompilation. */
118 ADD_EMPTY_CASE(JSOP_NOP
)
119 ADD_EMPTY_CASE(JSOP_CONDSWITCH
)
120 ADD_EMPTY_CASE(JSOP_TRY
)
121 ADD_EMPTY_CASE(JSOP_TRACE
)
122 #if JS_HAS_XML_SUPPORT
123 ADD_EMPTY_CASE(JSOP_STARTXML
)
124 ADD_EMPTY_CASE(JSOP_STARTXMLEXPR
)
128 /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
129 BEGIN_CASE(JSOP_LINENO
)
130 END_CASE(JSOP_LINENO
)
132 BEGIN_CASE(JSOP_PUSH
)
133 PUSH_OPND(JSVAL_VOID
);
140 BEGIN_CASE(JSOP_POPN
)
141 regs
.sp
-= GET_UINT16(regs
.pc
);
143 JS_ASSERT(StackBase(fp
) <= regs
.sp
);
144 obj
= fp
->blockChain
;
146 OBJ_BLOCK_DEPTH(cx
, obj
) + OBJ_BLOCK_COUNT(cx
, obj
)
147 <= (size_t) (regs
.sp
- StackBase(fp
)));
148 for (obj
= fp
->scopeChain
; obj
; obj
= obj
->getParent()) {
149 clasp
= obj
->getClass();
150 if (clasp
!= &js_BlockClass
&& clasp
!= &js_WithClass
)
152 if (obj
->getPrivate() != fp
)
154 JS_ASSERT(StackBase(fp
) + OBJ_BLOCK_DEPTH(cx
, obj
)
155 + ((clasp
== &js_BlockClass
)
156 ? OBJ_BLOCK_COUNT(cx
, obj
)
163 BEGIN_CASE(JSOP_SETRVAL
)
164 BEGIN_CASE(JSOP_POPV
)
165 ASSERT_NOT_THROWING(cx
);
166 fp
->rval
= POP_OPND();
169 BEGIN_CASE(JSOP_ENTERWITH
)
170 if (!js_EnterWith(cx
, -1))
174 * We must ensure that different "with" blocks have different stack depth
175 * associated with them. This allows the try handler search to properly
176 * recover the scope chain. Thus we must keep the stack at least at the
179 * We set sp[-1] to the current "with" object to help asserting the
180 * enter/leave balance in [leavewith].
182 regs
.sp
[-1] = OBJECT_TO_JSVAL(fp
->scopeChain
);
183 END_CASE(JSOP_ENTERWITH
)
185 BEGIN_CASE(JSOP_LEAVEWITH
)
186 JS_ASSERT(regs
.sp
[-1] == OBJECT_TO_JSVAL(fp
->scopeChain
));
189 END_CASE(JSOP_LEAVEWITH
)
191 BEGIN_CASE(JSOP_RETURN
)
192 fp
->rval
= POP_OPND();
195 BEGIN_CASE(JSOP_RETRVAL
) /* fp->rval already set */
196 BEGIN_CASE(JSOP_STOP
)
198 * When the inlined frame exits with an exception or an error, ok will be
199 * false after the inline_return label.
201 ASSERT_NOT_THROWING(cx
);
206 * If we are at the end of an imacro, return to its caller in the
209 JS_ASSERT(op
== JSOP_STOP
);
210 JS_ASSERT((uintN
)(regs
.sp
- fp
->slots
) <= script
->nslots
);
211 regs
.pc
= fp
->imacpc
+ js_CodeSpec
[*fp
->imacpc
].length
;
213 atoms
= script
->atomMap
.vector
;
218 JS_ASSERT(regs
.sp
== StackBase(fp
));
219 if ((fp
->flags
& JSFRAME_CONSTRUCTING
) && JSVAL_IS_PRIMITIVE(fp
->rval
))
220 fp
->rval
= fp
->thisv
;
225 JSInlineFrame
*ifp
= (JSInlineFrame
*) fp
;
226 void *hookData
= ifp
->hookData
;
228 JS_ASSERT(!fp
->blockChain
);
229 JS_ASSERT(!js_IsActiveWithOrBlock(cx
, fp
->scopeChain
, 0));
231 if (script
->staticLevel
< JS_DISPLAY_SIZE
)
232 cx
->display
[script
->staticLevel
] = fp
->displaySave
;
235 JSInterpreterHook hook
;
238 hook
= cx
->debugHooks
->callHook
;
241 * Do not pass &ok directly as exposing the address inhibits
242 * optimizations and uninitialised warnings.
245 hook(cx
, fp
, JS_FALSE
, &status
, hookData
);
247 CHECK_INTERRUPT_HANDLER();
252 * If fp has a call object, sync values and clear the back-
253 * pointer. This can happen for a lightweight function if it calls eval
254 * unexpectedly (in a way that is hidden from the compiler). See bug
257 fp
->putActivationObjects(cx
);
259 DTrace::exitJSFun(cx
, fp
, fp
->fun
, fp
->rval
);
261 /* Restore context version only if callee hasn't set version. */
262 if (JS_LIKELY(cx
->version
== currentVersion
)) {
263 currentVersion
= ifp
->callerVersion
;
264 if (currentVersion
!= cx
->version
)
265 js_SetVersion(cx
, currentVersion
);
269 * If inline-constructing, replace primitive rval with the new object
270 * passed in via |this|, and instrument this constructor invocation.
272 if (fp
->flags
& JSFRAME_CONSTRUCTING
) {
273 if (JSVAL_IS_PRIMITIVE(fp
->rval
))
274 fp
->rval
= fp
->thisv
;
275 JS_RUNTIME_METER(cx
->runtime
, constructs
);
278 /* Restore caller's registers. */
279 regs
= ifp
->callerRegs
;
281 /* Store the return value in the caller's operand frame. */
282 regs
.sp
-= 1 + (size_t) ifp
->frame
.argc
;
283 regs
.sp
[-1] = fp
->rval
;
285 bool recursive
= fp
->script
== fp
->down
->script
;
287 /* Restore cx->fp and release the inline frame's space. */
288 cx
->fp
= fp
= fp
->down
;
289 JS_ASSERT(fp
->regs
== &ifp
->callerRegs
);
291 cx
->stackPool
.release(ifp
->mark
);
293 /* Restore the calling script's interpreter registers. */
295 atoms
= FrameAtomBase(cx
, fp
);
297 /* Resume execution in the calling frame. */
300 JS_ASSERT(js_CodeSpec
[js_GetOpcode(cx
, script
, regs
.pc
)].length
301 == JSOP_CALL_LENGTH
);
303 if (!TRACE_RECORDER(cx
) && recursive
) {
304 if (*(regs
.pc
+ JSOP_CALL_LENGTH
) == JSOP_TRACE
) {
305 regs
.pc
+= JSOP_CALL_LENGTH
;
306 MONITOR_BRANCH(Record_LeaveFrame
);
311 if (*(regs
.pc
+ JSOP_CALL_LENGTH
) == JSOP_TRACE
||
312 *(regs
.pc
+ JSOP_CALL_LENGTH
) == JSOP_NOP
) {
313 JS_STATIC_ASSERT(JSOP_TRACE_LENGTH
== JSOP_NOP_LENGTH
);
314 regs
.pc
+= JSOP_CALL_LENGTH
;
315 len
= JSOP_TRACE_LENGTH
;
317 len
= JSOP_CALL_LENGTH
;
325 BEGIN_CASE(JSOP_DEFAULT
)
328 BEGIN_CASE(JSOP_GOTO
)
329 len
= GET_JUMP_OFFSET(regs
.pc
);
333 BEGIN_CASE(JSOP_IFEQ
)
334 POP_BOOLEAN(cx
, rval
, cond
);
335 if (cond
== JS_FALSE
) {
336 len
= GET_JUMP_OFFSET(regs
.pc
);
341 BEGIN_CASE(JSOP_IFNE
)
342 POP_BOOLEAN(cx
, rval
, cond
);
343 if (cond
!= JS_FALSE
) {
344 len
= GET_JUMP_OFFSET(regs
.pc
);
350 POP_BOOLEAN(cx
, rval
, cond
);
351 if (cond
== JS_TRUE
) {
352 len
= GET_JUMP_OFFSET(regs
.pc
);
359 POP_BOOLEAN(cx
, rval
, cond
);
360 if (cond
== JS_FALSE
) {
361 len
= GET_JUMP_OFFSET(regs
.pc
);
367 BEGIN_CASE(JSOP_DEFAULTX
)
370 BEGIN_CASE(JSOP_GOTOX
)
371 len
= GET_JUMPX_OFFSET(regs
.pc
);
373 END_CASE(JSOP_GOTOX
);
375 BEGIN_CASE(JSOP_IFEQX
)
376 POP_BOOLEAN(cx
, rval
, cond
);
377 if (cond
== JS_FALSE
) {
378 len
= GET_JUMPX_OFFSET(regs
.pc
);
383 BEGIN_CASE(JSOP_IFNEX
)
384 POP_BOOLEAN(cx
, rval
, cond
);
385 if (cond
!= JS_FALSE
) {
386 len
= GET_JUMPX_OFFSET(regs
.pc
);
392 POP_BOOLEAN(cx
, rval
, cond
);
393 if (cond
== JS_TRUE
) {
394 len
= GET_JUMPX_OFFSET(regs
.pc
);
400 BEGIN_CASE(JSOP_ANDX
)
401 POP_BOOLEAN(cx
, rval
, cond
);
402 if (cond
== JS_FALSE
) {
403 len
= GET_JUMPX_OFFSET(regs
.pc
);
410 * If the index value at sp[n] is not an int that fits in a jsval, it could
411 * be an object (an XML QName, AttributeName, or AnyName), but only if we are
412 * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a
415 #define FETCH_ELEMENT_ID(obj, n, id) \
417 jsval idval_ = FETCH_OPND(n); \
418 if (JSVAL_IS_INT(idval_)) { \
419 id = INT_JSVAL_TO_JSID(idval_); \
421 if (!js_InternNonIntElementId(cx, obj, idval_, &id)) \
423 regs.sp[n] = ID_TO_VALUE(id); \
427 #define TRY_BRANCH_AFTER_COND(cond,spdec) \
430 JS_ASSERT(js_CodeSpec[op].length == 1); \
431 diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ; \
434 if (cond == (diff_ != 0)) { \
436 len = GET_JUMP_OFFSET(regs.pc); \
439 len = 1 + JSOP_IFEQ_LENGTH; \
445 rval
= FETCH_OPND(-1);
446 if (JSVAL_IS_PRIMITIVE(rval
)) {
447 js_ReportValueError(cx
, JSMSG_IN_NOT_OBJECT
, -1, rval
, NULL
);
450 obj
= JSVAL_TO_OBJECT(rval
);
451 FETCH_ELEMENT_ID(obj
, -2, id
);
452 if (!obj
->lookupProperty(cx
, id
, &obj2
, &prop
))
456 obj2
->dropProperty(cx
, prop
);
457 TRY_BRANCH_AFTER_COND(cond
, 2);
459 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond
));
462 BEGIN_CASE(JSOP_ITER
)
463 JS_ASSERT(regs
.sp
> StackBase(fp
));
465 if (!js_ValueToIterator(cx
, flags
, ®s
.sp
[-1]))
467 CHECK_INTERRUPT_HANDLER();
468 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs
.sp
[-1]));
471 BEGIN_CASE(JSOP_MOREITER
)
472 JS_ASSERT(regs
.sp
- 1 >= StackBase(fp
));
473 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs
.sp
[-1]));
474 if (!IteratorMore(cx
, JSVAL_TO_OBJECT(regs
.sp
[-1]), &cond
, ®s
.sp
[0]))
476 CHECK_INTERRUPT_HANDLER();
477 TRY_BRANCH_AFTER_COND(cond
, 0);
478 JS_ASSERT(regs
.pc
[1] == JSOP_IFNEX
);
479 PUSH_OPND(BOOLEAN_TO_JSVAL(cond
));
480 END_CASE(JSOP_MOREITER
)
482 BEGIN_CASE(JSOP_ENDITER
)
483 JS_ASSERT(regs
.sp
- 1 >= StackBase(fp
));
484 ok
= js_CloseIterator(cx
, regs
.sp
[-1]);
488 END_CASE(JSOP_ENDITER
)
490 BEGIN_CASE(JSOP_FORARG
)
491 JS_ASSERT(regs
.sp
- 1 >= StackBase(fp
));
492 slot
= GET_ARGNO(regs
.pc
);
493 JS_ASSERT(slot
< fp
->fun
->nargs
);
494 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs
.sp
[-1]));
495 if (!IteratorNext(cx
, JSVAL_TO_OBJECT(regs
.sp
[-1]), &fp
->argv
[slot
]))
497 END_CASE(JSOP_FORARG
)
499 BEGIN_CASE(JSOP_FORLOCAL
)
500 JS_ASSERT(regs
.sp
- 1 >= StackBase(fp
));
501 slot
= GET_SLOTNO(regs
.pc
);
502 JS_ASSERT(slot
< fp
->script
->nslots
);
503 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs
.sp
[-1]));
504 if (!IteratorNext(cx
, JSVAL_TO_OBJECT(regs
.sp
[-1]), &fp
->slots
[slot
]))
506 END_CASE(JSOP_FORLOCAL
)
508 BEGIN_CASE(JSOP_FORNAME
)
509 JS_ASSERT(regs
.sp
- 1 >= StackBase(fp
));
511 id
= ATOM_TO_JSID(atom
);
512 if (!js_FindProperty(cx
, id
, &obj
, &obj2
, &prop
))
515 obj2
->dropProperty(cx
, prop
);
517 AutoValueRooter
tvr(cx
);
518 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs
.sp
[-1]));
519 if (!IteratorNext(cx
, JSVAL_TO_OBJECT(regs
.sp
[-1]), tvr
.addr()))
521 ok
= obj
->setProperty(cx
, id
, tvr
.addr());
525 END_CASE(JSOP_FORNAME
)
527 BEGIN_CASE(JSOP_FORPROP
)
528 JS_ASSERT(regs
.sp
- 2 >= StackBase(fp
));
530 id
= ATOM_TO_JSID(atom
);
531 FETCH_OBJECT(cx
, -1, lval
, obj
);
533 AutoValueRooter
tvr(cx
);
534 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs
.sp
[-2]));
535 if (!IteratorNext(cx
, JSVAL_TO_OBJECT(regs
.sp
[-2]), tvr
.addr()))
537 ok
= obj
->setProperty(cx
, id
, tvr
.addr());
542 END_CASE(JSOP_FORPROP
)
544 BEGIN_CASE(JSOP_FORELEM
)
546 * JSOP_FORELEM simply dups the property identifier at top of stack and
547 * lets the subsequent JSOP_ENUMELEM opcode sequence handle the left-hand
548 * side expression evaluation and assignment. This opcode exists solely to
549 * help the decompiler.
551 JS_ASSERT(regs
.sp
- 1 >= StackBase(fp
));
552 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs
.sp
[-1]));
553 if (!IteratorNext(cx
, JSVAL_TO_OBJECT(regs
.sp
[-1]), ®s
.sp
[0]))
556 END_CASE(JSOP_FORELEM
)
559 JS_ASSERT(regs
.sp
> StackBase(fp
));
560 rval
= FETCH_OPND(-1);
564 BEGIN_CASE(JSOP_DUP2
)
565 JS_ASSERT(regs
.sp
- 2 >= StackBase(fp
));
566 lval
= FETCH_OPND(-2);
567 rval
= FETCH_OPND(-1);
572 BEGIN_CASE(JSOP_SWAP
)
573 JS_ASSERT(regs
.sp
- 2 >= StackBase(fp
));
574 lval
= FETCH_OPND(-2);
575 rval
= FETCH_OPND(-1);
576 STORE_OPND(-1, lval
);
577 STORE_OPND(-2, rval
);
580 BEGIN_CASE(JSOP_PICK
)
582 JS_ASSERT(regs
.sp
- (i
+1) >= StackBase(fp
));
583 lval
= regs
.sp
[-(i
+1)];
584 memmove(regs
.sp
- (i
+1), regs
.sp
- i
, sizeof(jsval
)*i
);
588 #define PROPERTY_OP(n, call) \
590 /* Fetch the left part and resolve it to a non-null object. */ \
591 FETCH_OBJECT(cx, n, lval, obj); \
593 /* Get or set the property. */ \
598 #define ELEMENT_OP(n, call) \
600 /* Fetch the left part and resolve it to a non-null object. */ \
601 FETCH_OBJECT(cx, n - 1, lval, obj); \
603 /* Fetch index and convert it to id suitable for use with obj. */ \
604 FETCH_ELEMENT_ID(obj, n, id); \
606 /* Get or set the element. */ \
611 #define NATIVE_GET(cx,obj,pobj,sprop,getHow,vp) \
613 if (sprop->hasDefaultGetter()) { \
614 /* Fast path for Object instance properties. */ \
615 JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT || \
616 !sprop->hasDefaultSetter()); \
617 *vp = ((sprop)->slot != SPROP_INVALID_SLOT) \
618 ? (pobj)->lockedGetSlot((sprop)->slot) \
621 if (!js_NativeGet(cx, obj, pobj, sprop, getHow, vp)) \
626 #define NATIVE_SET(cx,obj,sprop,entry,vp) \
628 TRACE_2(SetPropHit, entry, sprop); \
629 if (sprop->hasDefaultSetter() && \
630 (sprop)->slot != SPROP_INVALID_SLOT && \
631 !(obj)->scope()->brandedOrHasMethodBarrier()) { \
632 /* Fast path for, e.g., plain Object instance properties. */ \
633 (obj)->lockedSetSlot((sprop)->slot, *vp); \
635 if (!js_NativeSet(cx, obj, sprop, false, vp)) \
641 * Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is
642 * the constant length of the SET opcode sequence, and spdec is the constant
643 * by which to decrease the stack pointer to pop all of the SET op's operands.
645 * NB: unlike macros that could conceivably be replaced by functions (ignoring
646 * goto error), where a call should not have to be braced in order to expand
647 * correctly (e.g., in if (cond) FOO(); else BAR()), these three macros lack
648 * JS_{BEGIN,END}_MACRO brackets. They are also indented so as to align with
649 * nearby opcode code.
651 #define SKIP_POP_AFTER_SET(oplen,spdec) \
652 if (regs.pc[oplen] == JSOP_POP) { \
654 regs.pc += oplen + JSOP_POP_LENGTH; \
655 op = (JSOp) *regs.pc; \
659 #define END_SET_CASE(OP) \
660 SKIP_POP_AFTER_SET(OP##_LENGTH, 1); \
663 #define END_SET_CASE_STORE_RVAL(OP,spdec) \
664 SKIP_POP_AFTER_SET(OP##_LENGTH, spdec); \
665 rval = FETCH_OPND(-1); \
666 regs.sp -= (spdec) - 1; \
667 STORE_OPND(-1, rval); \
670 BEGIN_CASE(JSOP_SETCONST
)
672 obj
= fp
->varobj(cx
);
673 rval
= FETCH_OPND(-1);
674 if (!obj
->defineProperty(cx
, ATOM_TO_JSID(atom
), rval
,
675 JS_PropertyStub
, JS_PropertyStub
,
676 JSPROP_ENUMERATE
| JSPROP_PERMANENT
| JSPROP_READONLY
)) {
679 END_SET_CASE(JSOP_SETCONST
);
681 #if JS_HAS_DESTRUCTURING
682 BEGIN_CASE(JSOP_ENUMCONSTELEM
)
683 rval
= FETCH_OPND(-3);
684 FETCH_OBJECT(cx
, -2, lval
, obj
);
685 FETCH_ELEMENT_ID(obj
, -1, id
);
686 if (!obj
->defineProperty(cx
, id
, rval
,
687 JS_PropertyStub
, JS_PropertyStub
,
688 JSPROP_ENUMERATE
| JSPROP_PERMANENT
| JSPROP_READONLY
)) {
692 END_CASE(JSOP_ENUMCONSTELEM
)
695 BEGIN_CASE(JSOP_BINDNAME
)
697 PropertyCacheEntry
*entry
;
700 * We can skip the property lookup for the global object. If the
701 * property does not exist anywhere on the scope chain, JSOP_SETNAME
702 * adds the property to the global.
704 * As a consequence of this optimization for the global object we run
705 * its JSRESOLVE_ASSIGNING-tolerant resolve hooks only in JSOP_SETNAME,
706 * after the interpreter evaluates the right- hand-side of the
707 * assignment, and not here.
709 * This should be transparent to the hooks because the script, instead
710 * of name = rhs, could have used global.name = rhs given a global
711 * object reference, which also calls the hooks only after evaluating
712 * the rhs. We desire such resolve hook equivalence between the two
715 obj
= fp
->scopeChain
;
716 if (!obj
->getParent())
719 JS_PROPERTY_CACHE(cx
).test(cx
, regs
.pc
, obj
, obj2
, entry
, atom
);
721 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj
, obj2
, entry
);
725 id
= ATOM_TO_JSID(atom
);
726 obj
= js_FindIdentifierBase(cx
, fp
->scopeChain
, id
);
730 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
731 END_CASE(JSOP_BINDNAME
)
733 BEGIN_CASE(JSOP_IMACOP
)
734 JS_ASSERT(JS_UPTRDIFF(fp
->imacpc
, script
->code
) < script
->length
);
735 op
= JSOp(*fp
->imacpc
);
738 #define BITWISE_OP(OP) \
740 FETCH_INT(cx, -2, i); \
741 FETCH_INT(cx, -1, j); \
744 STORE_INT(cx, -1, i); \
747 BEGIN_CASE(JSOP_BITOR
)
751 BEGIN_CASE(JSOP_BITXOR
)
753 END_CASE(JSOP_BITXOR
)
755 BEGIN_CASE(JSOP_BITAND
)
757 END_CASE(JSOP_BITAND
)
759 #define RELATIONAL_OP(OP) \
761 rval = FETCH_OPND(-1); \
762 lval = FETCH_OPND(-2); \
763 /* Optimize for two int-tagged operands (typical loop control). */ \
764 if ((lval & rval) & JSVAL_INT) { \
765 cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval); \
767 if (!JSVAL_IS_PRIMITIVE(lval)) \
768 DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \
769 if (!JSVAL_IS_PRIMITIVE(rval)) \
770 DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \
771 if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { \
772 str = JSVAL_TO_STRING(lval); \
773 str2 = JSVAL_TO_STRING(rval); \
774 cond = js_CompareStrings(str, str2) OP 0; \
776 VALUE_TO_NUMBER(cx, lval, d); \
777 VALUE_TO_NUMBER(cx, rval, d2); \
778 cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \
781 TRY_BRANCH_AFTER_COND(cond, 2); \
783 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
787 * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies
788 * because they begin if/else chains, so callers must not put semicolons after
789 * the call expressions!
791 #if JS_HAS_XML_SUPPORT
792 #define XML_EQUALITY_OP(OP) \
793 if ((ltmp == JSVAL_OBJECT && \
794 (obj2 = JSVAL_TO_OBJECT(lval)) && \
795 OBJECT_IS_XML(cx, obj2)) || \
796 (rtmp == JSVAL_OBJECT && \
797 (obj2 = JSVAL_TO_OBJECT(rval)) && \
798 OBJECT_IS_XML(cx, obj2))) { \
799 if (JSVAL_IS_OBJECT(rval) && obj2 == JSVAL_TO_OBJECT(rval)) \
801 if (!js_TestXMLEquality(cx, obj2, rval, &cond)) \
803 cond = cond OP JS_TRUE; \
806 #define EXTENDED_EQUALITY_OP(OP) \
807 if (ltmp == JSVAL_OBJECT && \
808 (obj2 = JSVAL_TO_OBJECT(lval)) && \
809 ((clasp = obj2->getClass())->flags & JSCLASS_IS_EXTENDED) && \
810 (((JSExtendedClass *) clasp)->equality)) { \
811 if (!((JSExtendedClass *) clasp)->equality(cx, obj2, rval, &cond)) \
813 cond = cond OP JS_TRUE; \
816 #define XML_EQUALITY_OP(OP) /* nothing */
817 #define EXTENDED_EQUALITY_OP(OP) /* nothing */
820 #define EQUALITY_OP(OP, IFNAN) \
822 rval = FETCH_OPND(-1); \
823 lval = FETCH_OPND(-2); \
824 ltmp = JSVAL_TAG(lval); \
825 rtmp = JSVAL_TAG(rval); \
826 XML_EQUALITY_OP(OP) \
827 if (ltmp == rtmp) { \
828 if (ltmp == JSVAL_STRING) { \
829 str = JSVAL_TO_STRING(lval); \
830 str2 = JSVAL_TO_STRING(rval); \
831 cond = js_EqualStrings(str, str2) OP JS_TRUE; \
832 } else if (ltmp == JSVAL_DOUBLE) { \
833 d = *JSVAL_TO_DOUBLE(lval); \
834 d2 = *JSVAL_TO_DOUBLE(rval); \
835 cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \
837 EXTENDED_EQUALITY_OP(OP) \
838 /* Handle all undefined (=>NaN) and int combinations. */ \
839 cond = lval OP rval; \
842 if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) { \
843 cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) OP 1; \
844 } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) { \
847 if (ltmp == JSVAL_OBJECT) { \
848 DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval); \
849 ltmp = JSVAL_TAG(lval); \
850 } else if (rtmp == JSVAL_OBJECT) { \
851 DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); \
852 rtmp = JSVAL_TAG(rval); \
854 if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) { \
855 str = JSVAL_TO_STRING(lval); \
856 str2 = JSVAL_TO_STRING(rval); \
857 cond = js_EqualStrings(str, str2) OP JS_TRUE; \
859 VALUE_TO_NUMBER(cx, lval, d); \
860 VALUE_TO_NUMBER(cx, rval, d2); \
861 cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \
865 TRY_BRANCH_AFTER_COND(cond, 2); \
867 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
871 EQUALITY_OP(==, JS_FALSE
);
875 EQUALITY_OP(!=, JS_TRUE
);
878 #define STRICT_EQUALITY_OP(OP) \
880 rval = FETCH_OPND(-1); \
881 lval = FETCH_OPND(-2); \
882 cond = js_StrictlyEqual(cx, lval, rval) OP JS_TRUE; \
884 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
887 BEGIN_CASE(JSOP_STRICTEQ
)
888 STRICT_EQUALITY_OP(==);
889 END_CASE(JSOP_STRICTEQ
)
891 BEGIN_CASE(JSOP_STRICTNE
)
892 STRICT_EQUALITY_OP(!=);
893 END_CASE(JSOP_STRICTNE
)
895 BEGIN_CASE(JSOP_CASE
)
896 STRICT_EQUALITY_OP(==);
899 len
= GET_JUMP_OFFSET(regs
.pc
);
905 BEGIN_CASE(JSOP_CASEX
)
906 STRICT_EQUALITY_OP(==);
909 len
= GET_JUMPX_OFFSET(regs
.pc
);
934 #define SIGNED_SHIFT_OP(OP) \
936 FETCH_INT(cx, -2, i); \
937 FETCH_INT(cx, -1, j); \
940 STORE_INT(cx, -1, i); \
951 BEGIN_CASE(JSOP_URSH
)
955 FETCH_UINT(cx
, -2, u
);
956 FETCH_INT(cx
, -1, j
);
959 STORE_UINT(cx
, -1, u
);
964 #undef SIGNED_SHIFT_OP
967 rval
= FETCH_OPND(-1);
968 lval
= FETCH_OPND(-2);
969 #if JS_HAS_XML_SUPPORT
970 if (!JSVAL_IS_PRIMITIVE(lval
) &&
971 (obj2
= JSVAL_TO_OBJECT(lval
), OBJECT_IS_XML(cx
, obj2
)) &&
972 VALUE_IS_XML(cx
, rval
)) {
973 if (!js_ConcatenateXML(cx
, obj2
, rval
, &rval
))
976 STORE_OPND(-1, rval
);
980 if (!JSVAL_IS_PRIMITIVE(lval
))
981 DEFAULT_VALUE(cx
, -2, JSTYPE_VOID
, lval
);
982 if (!JSVAL_IS_PRIMITIVE(rval
))
983 DEFAULT_VALUE(cx
, -1, JSTYPE_VOID
, rval
);
984 if ((cond
= JSVAL_IS_STRING(lval
)) || JSVAL_IS_STRING(rval
)) {
986 str
= JSVAL_TO_STRING(lval
);
987 str2
= js_ValueToString(cx
, rval
);
990 regs
.sp
[-1] = STRING_TO_JSVAL(str2
);
992 str2
= JSVAL_TO_STRING(rval
);
993 str
= js_ValueToString(cx
, lval
);
996 regs
.sp
[-2] = STRING_TO_JSVAL(str
);
998 str
= js_ConcatStrings(cx
, str
, str2
);
1002 STORE_OPND(-1, STRING_TO_JSVAL(str
));
1004 VALUE_TO_NUMBER(cx
, lval
, d
);
1005 VALUE_TO_NUMBER(cx
, rval
, d2
);
1008 STORE_NUMBER(cx
, -1, d
);
1013 BEGIN_CASE(JSOP_OBJTOSTR
)
1014 rval
= FETCH_OPND(-1);
1015 if (!JSVAL_IS_PRIMITIVE(rval
)) {
1016 str
= js_ValueToString(cx
, rval
);
1019 STORE_OPND(-1, STRING_TO_JSVAL(str
));
1021 END_CASE(JSOP_OBJTOSTR
)
1023 BEGIN_CASE(JSOP_CONCATN
)
1025 JSCharBuffer
buf(cx
);
1026 argc
= GET_ARGC(regs
.pc
);
1027 for (vp
= regs
.sp
- argc
; vp
< regs
.sp
; vp
++) {
1028 JS_ASSERT(JSVAL_IS_PRIMITIVE(*vp
));
1029 if (!js_ValueToCharBuffer(cx
, *vp
, buf
))
1032 str
= js_NewStringFromCharBuffer(cx
, buf
);
1035 regs
.sp
-= argc
- 1;
1036 STORE_OPND(-1, STRING_TO_JSVAL(str
));
1038 END_CASE(JSOP_CONCATN
)
1040 #define BINARY_OP(OP) \
1042 FETCH_NUMBER(cx, -2, d); \
1043 FETCH_NUMBER(cx, -1, d2); \
1046 STORE_NUMBER(cx, -1, d); \
1049 BEGIN_CASE(JSOP_SUB
)
1053 BEGIN_CASE(JSOP_MUL
)
1057 BEGIN_CASE(JSOP_DIV
)
1058 FETCH_NUMBER(cx
, -1, d2
);
1059 FETCH_NUMBER(cx
, -2, d
);
1063 /* XXX MSVC miscompiles such that (NaN == 0) */
1064 if (JSDOUBLE_IS_NaN(d2
))
1065 rval
= rt
->NaNValue
;
1068 if (d
== 0 || JSDOUBLE_IS_NaN(d
))
1069 rval
= rt
->NaNValue
;
1070 else if (JSDOUBLE_IS_NEG(d
) != JSDOUBLE_IS_NEG(d2
))
1071 rval
= rt
->negativeInfinityValue
;
1073 rval
= rt
->positiveInfinityValue
;
1074 STORE_OPND(-1, rval
);
1077 STORE_NUMBER(cx
, -1, d
);
1081 BEGIN_CASE(JSOP_MOD
)
1082 FETCH_NUMBER(cx
, -1, d2
);
1083 FETCH_NUMBER(cx
, -2, d
);
1086 STORE_OPND(-1, rt
->NaNValue
);
1089 STORE_NUMBER(cx
, -1, d
);
1093 BEGIN_CASE(JSOP_NOT
)
1094 POP_BOOLEAN(cx
, rval
, cond
);
1095 PUSH_OPND(BOOLEAN_TO_JSVAL(!cond
));
1098 BEGIN_CASE(JSOP_BITNOT
)
1099 FETCH_INT(cx
, -1, i
);
1101 STORE_INT(cx
, -1, i
);
1102 END_CASE(JSOP_BITNOT
)
1104 BEGIN_CASE(JSOP_NEG
)
1106 * When the operand is int jsval, INT_FITS_IN_JSVAL(i) implies
1107 * INT_FITS_IN_JSVAL(-i) unless i is 0 or JSVAL_INT_MIN when the
1108 * results, -0.0 or JSVAL_INT_MAX + 1, are jsdouble values.
1110 rval
= FETCH_OPND(-1);
1111 if (JSVAL_IS_INT(rval
) &&
1112 rval
!= INT_TO_JSVAL(JSVAL_INT_MIN
) &&
1113 (i
= JSVAL_TO_INT(rval
)) != 0) {
1114 JS_STATIC_ASSERT(!INT_FITS_IN_JSVAL(-JSVAL_INT_MIN
));
1116 JS_ASSERT(INT_FITS_IN_JSVAL(i
));
1117 regs
.sp
[-1] = INT_TO_JSVAL(i
);
1119 if (!ValueToNumber(cx
, regs
.sp
[-1], &d
))
1122 if (!js_NewNumberInRootedValue(cx
, d
, ®s
.sp
[-1]))
1127 BEGIN_CASE(JSOP_POS
)
1128 if (!ValueToNumberValue(cx
, ®s
.sp
[-1]))
1132 BEGIN_CASE(JSOP_DELNAME
)
1134 id
= ATOM_TO_JSID(atom
);
1135 if (!js_FindProperty(cx
, id
, &obj
, &obj2
, &prop
))
1138 /* ECMA says to return true if name is undefined or inherited. */
1139 PUSH_OPND(JSVAL_TRUE
);
1141 obj2
->dropProperty(cx
, prop
);
1142 if (!obj
->deleteProperty(cx
, id
, ®s
.sp
[-1]))
1145 END_CASE(JSOP_DELNAME
)
1147 BEGIN_CASE(JSOP_DELPROP
)
1149 id
= ATOM_TO_JSID(atom
);
1150 PROPERTY_OP(-1, obj
->deleteProperty(cx
, id
, &rval
));
1151 STORE_OPND(-1, rval
);
1152 END_CASE(JSOP_DELPROP
)
1154 BEGIN_CASE(JSOP_DELELEM
)
1155 ELEMENT_OP(-1, obj
->deleteProperty(cx
, id
, &rval
));
1157 STORE_OPND(-1, rval
);
1158 END_CASE(JSOP_DELELEM
)
1160 BEGIN_CASE(JSOP_TYPEOFEXPR
)
1161 BEGIN_CASE(JSOP_TYPEOF
)
1162 rval
= FETCH_OPND(-1);
1163 type
= JS_TypeOfValue(cx
, rval
);
1164 atom
= rt
->atomState
.typeAtoms
[type
];
1165 STORE_OPND(-1, ATOM_KEY(atom
));
1166 END_CASE(JSOP_TYPEOF
)
1168 BEGIN_CASE(JSOP_VOID
)
1169 STORE_OPND(-1, JSVAL_VOID
);
1172 BEGIN_CASE(JSOP_INCELEM
)
1173 BEGIN_CASE(JSOP_DECELEM
)
1174 BEGIN_CASE(JSOP_ELEMINC
)
1175 BEGIN_CASE(JSOP_ELEMDEC
)
1177 * Delay fetching of id until we have the object to ensure the proper
1178 * evaluation order. See bug 372331.
1182 goto fetch_incop_obj
;
1184 BEGIN_CASE(JSOP_INCPROP
)
1185 BEGIN_CASE(JSOP_DECPROP
)
1186 BEGIN_CASE(JSOP_PROPINC
)
1187 BEGIN_CASE(JSOP_PROPDEC
)
1189 id
= ATOM_TO_JSID(atom
);
1193 FETCH_OBJECT(cx
, i
, lval
, obj
);
1195 FETCH_ELEMENT_ID(obj
, -1, id
);
1198 BEGIN_CASE(JSOP_INCNAME
)
1199 BEGIN_CASE(JSOP_DECNAME
)
1200 BEGIN_CASE(JSOP_NAMEINC
)
1201 BEGIN_CASE(JSOP_NAMEDEC
)
1203 PropertyCacheEntry
*entry
;
1205 obj
= fp
->scopeChain
;
1207 JS_PROPERTY_CACHE(cx
).test(cx
, regs
.pc
, obj
, obj2
, entry
, atom
);
1209 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj
, obj2
, entry
);
1210 if (obj
== obj2
&& entry
->vword
.isSlot()) {
1211 slot
= entry
->vword
.toSlot();
1212 JS_ASSERT(slot
< obj
->scope()->freeslot
);
1213 rval
= obj
->lockedGetSlot(slot
);
1214 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval
))) {
1216 rval
+= (js_CodeSpec
[op
].format
& JOF_INC
) ? 2 : -2;
1217 if (!(js_CodeSpec
[op
].format
& JOF_POST
))
1219 obj
->lockedSetSlot(slot
, rval
);
1221 len
= JSOP_INCNAME_LENGTH
;
1228 id
= ATOM_TO_JSID(atom
);
1229 if (!js_FindPropertyHelper(cx
, id
, true, &obj
, &obj2
, &prop
))
1232 goto atom_not_defined
;
1233 obj2
->dropProperty(cx
, prop
);
1238 const JSCodeSpec
*cs
;
1242 * We need a root to store the value to leave on the stack until
1243 * we have done with obj->setProperty.
1245 PUSH_OPND(JSVAL_NULL
);
1246 if (!obj
->getProperty(cx
, id
, ®s
.sp
[-1]))
1249 cs
= &js_CodeSpec
[op
];
1250 JS_ASSERT(cs
->ndefs
== 1);
1251 JS_ASSERT((cs
->format
& JOF_TMPSLOT_MASK
) == JOF_TMPSLOT2
);
1253 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(v
))) {
1256 incr
= (cs
->format
& JOF_INC
) ? 2 : -2;
1257 if (cs
->format
& JOF_POST
) {
1258 regs
.sp
[-1] = v
+ incr
;
1263 fp
->flags
|= JSFRAME_ASSIGNING
;
1264 ok
= obj
->setProperty(cx
, id
, ®s
.sp
[-1]);
1265 fp
->flags
&= ~JSFRAME_ASSIGNING
;
1270 * We must set regs.sp[-1] to v for both post and pre increments
1271 * as the setter overwrites regs.sp[-1].
1275 /* We need an extra root for the result. */
1276 PUSH_OPND(JSVAL_NULL
);
1277 if (!js_DoIncDec(cx
, cs
, ®s
.sp
[-2], ®s
.sp
[-1]))
1279 fp
->flags
|= JSFRAME_ASSIGNING
;
1280 ok
= obj
->setProperty(cx
, id
, ®s
.sp
[-1]);
1281 fp
->flags
&= ~JSFRAME_ASSIGNING
;
1287 if (cs
->nuses
== 0) {
1288 /* regs.sp[-1] already contains the result of name increment. */
1291 regs
.sp
-= cs
->nuses
;
1301 /* Position cases so the most frequent i++ does not need a jump. */
1302 BEGIN_CASE(JSOP_DECARG
)
1303 incr
= -2; incr2
= -2; goto do_arg_incop
;
1304 BEGIN_CASE(JSOP_ARGDEC
)
1305 incr
= -2; incr2
= 0; goto do_arg_incop
;
1306 BEGIN_CASE(JSOP_INCARG
)
1307 incr
= 2; incr2
= 2; goto do_arg_incop
;
1308 BEGIN_CASE(JSOP_ARGINC
)
1309 incr
= 2; incr2
= 0;
1312 slot
= GET_ARGNO(regs
.pc
);
1313 JS_ASSERT(slot
< fp
->fun
->nargs
);
1314 METER_SLOT_OP(op
, slot
);
1315 vp
= fp
->argv
+ slot
;
1316 goto do_int_fast_incop
;
1318 BEGIN_CASE(JSOP_DECLOCAL
)
1319 incr
= -2; incr2
= -2; goto do_local_incop
;
1320 BEGIN_CASE(JSOP_LOCALDEC
)
1321 incr
= -2; incr2
= 0; goto do_local_incop
;
1322 BEGIN_CASE(JSOP_INCLOCAL
)
1323 incr
= 2; incr2
= 2; goto do_local_incop
;
1324 BEGIN_CASE(JSOP_LOCALINC
)
1325 incr
= 2; incr2
= 0;
1328 * do_local_incop comes right before do_int_fast_incop as we want to
1329 * avoid an extra jump for variable cases as local++ is more frequent
1333 slot
= GET_SLOTNO(regs
.pc
);
1334 JS_ASSERT(slot
< fp
->script
->nslots
);
1335 vp
= fp
->slots
+ slot
;
1336 METER_SLOT_OP(op
, slot
);
1337 vp
= fp
->slots
+ slot
;
1341 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval
))) {
1343 JS_ASSERT(JSOP_INCARG_LENGTH
== js_CodeSpec
[op
].length
);
1344 SKIP_POP_AFTER_SET(JSOP_INCARG_LENGTH
, 0);
1345 PUSH_OPND(rval
+ incr2
);
1348 if (!js_DoIncDec(cx
, &js_CodeSpec
[op
], ®s
.sp
[-1], vp
))
1351 len
= JSOP_INCARG_LENGTH
;
1352 JS_ASSERT(len
== js_CodeSpec
[op
].length
);
1356 /* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */
1357 #define FAST_GLOBAL_INCREMENT_OP(SLOWOP,INCR,INCR2) \
1361 goto do_global_incop
1366 BEGIN_CASE(JSOP_DECGVAR
)
1367 FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME
, -2, -2);
1368 BEGIN_CASE(JSOP_GVARDEC
)
1369 FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC
, -2, 0);
1370 BEGIN_CASE(JSOP_INCGVAR
)
1371 FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME
, 2, 2);
1372 BEGIN_CASE(JSOP_GVARINC
)
1373 FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC
, 2, 0);
1375 #undef FAST_GLOBAL_INCREMENT_OP
1378 JS_ASSERT((js_CodeSpec
[op
].format
& JOF_TMPSLOT_MASK
) ==
1380 slot
= GET_SLOTNO(regs
.pc
);
1381 JS_ASSERT(slot
< GlobalVarCount(fp
));
1382 METER_SLOT_OP(op
, slot
);
1383 lval
= fp
->slots
[slot
];
1384 if (JSVAL_IS_NULL(lval
)) {
1388 slot
= JSVAL_TO_INT(lval
);
1389 JS_ASSERT(fp
->varobj(cx
) == cx
->activeCallStack()->getInitialVarObj());
1390 rval
= cx
->activeCallStack()->getInitialVarObj()->getSlotMT(cx
, slot
);
1391 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval
))) {
1392 PUSH_OPND(rval
+ incr2
);
1396 PUSH_OPND(JSVAL_NULL
); /* Extra root */
1397 if (!js_DoIncDec(cx
, &js_CodeSpec
[op
], ®s
.sp
[-2], ®s
.sp
[-1]))
1402 fp
->varobj(cx
)->setSlotMT(cx
, slot
, rval
);
1403 len
= JSOP_INCGVAR_LENGTH
; /* all gvar incops are same length */
1404 JS_ASSERT(len
== js_CodeSpec
[op
].length
);
1408 #define COMPUTE_THIS(cx, fp, obj) \
1410 if (!(obj = (fp)->getThisObject(cx))) \
1414 BEGIN_CASE(JSOP_THIS
)
1415 COMPUTE_THIS(cx
, fp
, obj
);
1416 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
1419 BEGIN_CASE(JSOP_UNBRANDTHIS
)
1420 COMPUTE_THIS(cx
, fp
, obj
);
1421 if (!obj
->unbrand(cx
))
1423 END_CASE(JSOP_UNBRANDTHIS
)
1425 BEGIN_CASE(JSOP_GETTHISPROP
)
1427 COMPUTE_THIS(cx
, fp
, obj
);
1429 goto do_getprop_with_obj
;
1433 BEGIN_CASE(JSOP_GETARGPROP
)
1435 slot
= GET_ARGNO(regs
.pc
);
1436 JS_ASSERT(slot
< fp
->fun
->nargs
);
1437 PUSH_OPND(fp
->argv
[slot
]);
1438 goto do_getprop_body
;
1440 BEGIN_CASE(JSOP_GETLOCALPROP
)
1442 slot
= GET_SLOTNO(regs
.pc
);
1443 JS_ASSERT(slot
< script
->nslots
);
1444 PUSH_OPND(fp
->slots
[slot
]);
1445 goto do_getprop_body
;
1447 BEGIN_CASE(JSOP_GETPROP
)
1448 BEGIN_CASE(JSOP_GETXPROP
)
1452 lval
= FETCH_OPND(-1);
1454 do_getprop_with_lval
:
1455 VALUE_TO_OBJECT(cx
, -1, lval
, obj
);
1457 do_getprop_with_obj
:
1460 PropertyCacheEntry
*entry
;
1463 * We do not impose the method read barrier if in an imacro,
1464 * assuming any property gets it does (e.g., for 'toString'
1465 * from JSOP_NEW) will not be leaked to the calling script.
1467 aobj
= js_GetProtoIfDenseArray(obj
);
1469 JS_PROPERTY_CACHE(cx
).test(cx
, regs
.pc
, aobj
, obj2
, entry
, atom
);
1471 ASSERT_VALID_PROPERTY_CACHE_HIT(i
, aobj
, obj2
, entry
);
1472 if (entry
->vword
.isObject()) {
1473 rval
= entry
->vword
.toJsval();
1474 } else if (entry
->vword
.isSlot()) {
1475 slot
= entry
->vword
.toSlot();
1476 JS_ASSERT(slot
< obj2
->scope()->freeslot
);
1477 rval
= obj2
->lockedGetSlot(slot
);
1479 JS_ASSERT(entry
->vword
.isSprop());
1480 sprop
= entry
->vword
.toSprop();
1481 NATIVE_GET(cx
, obj
, obj2
, sprop
,
1482 fp
->imacpc
? JSGET_NO_METHOD_BARRIER
: JSGET_METHOD_BARRIER
,
1488 id
= ATOM_TO_JSID(atom
);
1489 if (JS_LIKELY(aobj
->map
->ops
->getProperty
== js_GetProperty
)
1490 ? !js_GetPropertyHelper(cx
, obj
, id
,
1492 ? JSGET_CACHE_RESULT
| JSGET_NO_METHOD_BARRIER
1493 : JSGET_CACHE_RESULT
| JSGET_METHOD_BARRIER
,
1495 : !obj
->getProperty(cx
, id
, &rval
)) {
1500 STORE_OPND(-1, rval
);
1501 JS_ASSERT(JSOP_GETPROP_LENGTH
+ i
== js_CodeSpec
[op
].length
);
1502 len
= JSOP_GETPROP_LENGTH
+ i
;
1505 BEGIN_CASE(JSOP_LENGTH
)
1506 lval
= FETCH_OPND(-1);
1507 if (JSVAL_IS_STRING(lval
)) {
1508 str
= JSVAL_TO_STRING(lval
);
1509 regs
.sp
[-1] = INT_TO_JSVAL(str
->length());
1510 } else if (!JSVAL_IS_PRIMITIVE(lval
)) {
1511 obj
= JSVAL_TO_OBJECT(lval
);
1512 if (obj
->isArray()) {
1513 jsuint length
= obj
->getArrayLength();
1515 if (length
<= JSVAL_INT_MAX
)
1516 regs
.sp
[-1] = INT_TO_JSVAL(length
);
1517 else if (!js_NewDoubleInRootedValue(cx
, (jsdouble
) length
, ®s
.sp
[-1]))
1519 } else if (obj
->isArguments() && !obj
->isArgsLengthOverridden()) {
1520 uint32 length
= obj
->getArgsLength();
1522 JS_ASSERT(INT_FITS_IN_JSVAL(length
));
1523 regs
.sp
[-1] = INT_TO_JSVAL(length
);
1526 goto do_getprop_with_lval
;
1530 goto do_getprop_with_lval
;
1532 END_CASE(JSOP_LENGTH
)
1534 BEGIN_CASE(JSOP_CALLPROP
)
1537 PropertyCacheEntry
*entry
;
1539 lval
= FETCH_OPND(-1);
1540 if (!JSVAL_IS_PRIMITIVE(lval
)) {
1541 obj
= JSVAL_TO_OBJECT(lval
);
1543 JSProtoKey protoKey
;
1544 if (JSVAL_IS_STRING(lval
)) {
1545 protoKey
= JSProto_String
;
1546 } else if (JSVAL_IS_NUMBER(lval
)) {
1547 protoKey
= JSProto_Number
;
1548 } else if (JSVAL_IS_BOOLEAN(lval
)) {
1549 protoKey
= JSProto_Boolean
;
1551 JS_ASSERT(JSVAL_IS_NULL(lval
) || JSVAL_IS_VOID(lval
));
1552 js_ReportIsNullOrUndefined(cx
, -1, lval
, NULL
);
1555 if (!js_GetClassPrototype(cx
, NULL
, protoKey
, &obj
))
1559 aobj
= js_GetProtoIfDenseArray(obj
);
1561 JS_PROPERTY_CACHE(cx
).test(cx
, regs
.pc
, aobj
, obj2
, entry
, atom
);
1563 ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj
, obj2
, entry
);
1564 if (entry
->vword
.isObject()) {
1565 rval
= entry
->vword
.toJsval();
1566 } else if (entry
->vword
.isSlot()) {
1567 slot
= entry
->vword
.toSlot();
1568 JS_ASSERT(slot
< obj2
->scope()->freeslot
);
1569 rval
= obj2
->lockedGetSlot(slot
);
1571 JS_ASSERT(entry
->vword
.isSprop());
1572 sprop
= entry
->vword
.toSprop();
1573 NATIVE_GET(cx
, obj
, obj2
, sprop
, JSGET_NO_METHOD_BARRIER
, &rval
);
1575 STORE_OPND(-1, rval
);
1581 * Cache miss: use the immediate atom that was loaded for us under
1582 * PropertyCache::test.
1584 id
= ATOM_TO_JSID(atom
);
1586 if (!JSVAL_IS_PRIMITIVE(lval
)) {
1587 if (!js_GetMethod(cx
, obj
, id
,
1588 JS_LIKELY(aobj
->map
->ops
->getProperty
== js_GetProperty
)
1589 ? JSGET_CACHE_RESULT
| JSGET_NO_METHOD_BARRIER
1590 : JSGET_NO_METHOD_BARRIER
,
1594 STORE_OPND(-1, OBJECT_TO_JSVAL(obj
));
1595 STORE_OPND(-2, rval
);
1597 JS_ASSERT(obj
->map
->ops
->getProperty
== js_GetProperty
);
1598 if (!js_GetPropertyHelper(cx
, obj
, id
,
1599 JSGET_CACHE_RESULT
| JSGET_NO_METHOD_BARRIER
,
1603 STORE_OPND(-1, lval
);
1604 STORE_OPND(-2, rval
);
1608 /* Wrap primitive lval in object clothing if necessary. */
1609 if (JSVAL_IS_PRIMITIVE(lval
)) {
1610 /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */
1611 if (!VALUE_IS_FUNCTION(cx
, rval
) ||
1612 (obj
= JSVAL_TO_OBJECT(rval
),
1613 fun
= GET_FUNCTION_PRIVATE(cx
, obj
),
1614 !PRIMITIVE_THIS_TEST(fun
, lval
))) {
1615 if (!js_PrimitiveToObject(cx
, ®s
.sp
[-1]))
1619 #if JS_HAS_NO_SUCH_METHOD
1620 if (JS_UNLIKELY(JSVAL_IS_VOID(rval
))) {
1622 regs
.sp
[-2] = ATOM_KEY(atom
);
1623 if (!js_OnUnknownMethod(cx
, regs
.sp
- 2))
1628 END_CASE(JSOP_CALLPROP
)
1630 BEGIN_CASE(JSOP_UNBRAND
)
1631 JS_ASSERT(regs
.sp
- fp
->slots
>= 1);
1632 lval
= FETCH_OPND(-1);
1633 obj
= JSVAL_TO_OBJECT(lval
);
1634 if (!obj
->unbrand(cx
))
1636 END_CASE(JSOP_UNBRAND
)
1638 BEGIN_CASE(JSOP_SETNAME
)
1639 BEGIN_CASE(JSOP_SETPROP
)
1640 BEGIN_CASE(JSOP_SETMETHOD
)
1641 rval
= FETCH_OPND(-1);
1642 JS_ASSERT_IF(op
== JSOP_SETMETHOD
, VALUE_IS_FUNCTION(cx
, rval
));
1643 lval
= FETCH_OPND(-2);
1644 JS_ASSERT_IF(op
== JSOP_SETNAME
, !JSVAL_IS_PRIMITIVE(lval
));
1645 VALUE_TO_OBJECT(cx
, -2, lval
, obj
);
1648 PropertyCache
*cache
= &JS_PROPERTY_CACHE(cx
);
1649 PropertyCacheEntry
*entry
= NULL
;
1653 * Probe the property cache, specializing for two important
1654 * set-property cases. First:
1656 * function f(a, b, c) {
1657 * var o = {p:a, q:b, r:c};
1661 * or similar real-world cases, which evolve a newborn native
1662 * object predicatably through some bounded number of property
1663 * additions. And second:
1667 * in a frequently executed method or loop body, where p will
1668 * (possibly after the first iteration) always exist in native
1671 if (cache
->testForSet(cx
, regs
.pc
, obj
, &entry
, &obj2
, &atom
)) {
1673 * Fast property cache hit, only partially confirmed by
1674 * testForSet. We know that the entry applies to regs.pc and
1675 * that obj's shape matches.
1677 * The entry predicts either a new property to be added
1678 * directly to obj by this set, or on an existing "own"
1679 * property, or on a prototype property that has a setter.
1681 JS_ASSERT(entry
->vword
.isSprop());
1682 sprop
= entry
->vword
.toSprop();
1683 JS_ASSERT_IF(sprop
->isDataDescriptor(), sprop
->writable());
1684 JS_ASSERT_IF(sprop
->hasSlot(), entry
->vcapTag() == 0);
1686 JSScope
*scope
= obj
->scope();
1687 JS_ASSERT(!scope
->sealed());
1690 * Fastest path: check whether the cached sprop is already
1691 * in scope and call NATIVE_SET and break to get out of the
1692 * do-while(0). But we can call NATIVE_SET only if obj owns
1693 * scope or sprop is shared.
1696 if (!sprop
->hasSlot()) {
1697 if (entry
->vcapTag() == 0 ||
1698 ((obj2
= obj
->getProto()) &&
1700 obj2
->shape() == entry
->vshape())) {
1701 goto fast_set_propcache_hit
;
1704 /* The cache entry doesn't apply. vshape mismatch. */
1705 checkForAdd
= false;
1706 } else if (!scope
->isSharedEmpty()) {
1707 if (sprop
== scope
->lastProperty() || scope
->hasProperty(sprop
)) {
1708 fast_set_propcache_hit
:
1709 PCMETER(cache
->pchits
++);
1710 PCMETER(cache
->setpchits
++);
1711 NATIVE_SET(cx
, obj
, sprop
, entry
, &rval
);
1714 checkForAdd
= sprop
->hasSlot() && sprop
->parent
== scope
->lastProperty();
1717 * We check that cx own obj here and will continue to
1718 * own it after js_GetMutableScope returns so we can
1719 * continue to skip JS_UNLOCK_OBJ calls.
1721 JS_ASSERT(CX_OWNS_OBJECT_TITLE(cx
, obj
));
1722 scope
= js_GetMutableScope(cx
, obj
);
1723 JS_ASSERT(CX_OWNS_OBJECT_TITLE(cx
, obj
));
1726 checkForAdd
= !sprop
->parent
;
1730 entry
->vshape() == rt
->protoHazardShape
&&
1731 sprop
->hasDefaultSetter() &&
1732 (slot
= sprop
->slot
) == scope
->freeslot
) {
1734 * Fast path: adding a plain old property that was once
1735 * at the frontier of the property tree, whose slot is
1736 * next to claim among the allocated slots in obj,
1737 * where scope->table has not been created yet.
1739 * We may want to remove hazard conditions above and
1740 * inline compensation code here, depending on
1741 * real-world workloads.
1743 PCMETER(cache
->pchits
++);
1744 PCMETER(cache
->addpchits
++);
1747 * Beware classes such as Function that use the
1748 * reserveSlots hook to allocate a number of reserved
1749 * slots that may vary with obj.
1751 if (slot
< obj
->numSlots() &&
1752 !obj
->getClass()->reserveSlots
) {
1755 if (!js_AllocSlot(cx
, obj
, &slot
))
1760 * If this obj's number of reserved slots differed, or
1761 * if something created a hash table for scope, we must
1762 * pay the price of JSScope::putProperty.
1764 * (A reserveSlots hook can cause scopes of the same
1765 * shape to have different freeslot values. This is
1766 * what causes the slot != sprop->slot case. See
1767 * js_GetMutableScope.)
1769 if (slot
!= sprop
->slot
|| scope
->table
) {
1770 JSScopeProperty
*sprop2
=
1771 scope
->putProperty(cx
, sprop
->id
,
1772 sprop
->getter(), sprop
->setter(),
1773 slot
, sprop
->attributes(),
1774 sprop
->getFlags(), sprop
->shortid
);
1776 js_FreeSlot(cx
, obj
, slot
);
1781 scope
->extend(cx
, sprop
);
1785 * No method change check here because here we are
1786 * adding a new property, not updating an existing
1787 * slot's value that might contain a method of a
1790 TRACE_2(SetPropHit
, entry
, sprop
);
1791 obj
->lockedSetSlot(slot
, rval
);
1794 * Purge the property cache of the id we may have just
1795 * shadowed in obj's scope and proto chains. We do this
1796 * after unlocking obj's scope to avoid lock nesting.
1798 js_PurgeScopeChain(cx
, obj
, sprop
->id
);
1801 PCMETER(cache
->setpcmisses
++);
1805 * Slower property cache hit, fully confirmed by testForSet (in
1806 * the slow path, via fullTest).
1808 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj
, obj2
, entry
);
1811 sprop
= entry
->vword
.toSprop();
1812 JS_ASSERT(sprop
->writable());
1813 JS_ASSERT(!obj2
->scope()->sealed());
1814 NATIVE_SET(cx
, obj
, sprop
, entry
, &rval
);
1822 id
= ATOM_TO_JSID(atom
);
1823 if (entry
&& JS_LIKELY(obj
->map
->ops
->setProperty
== js_SetProperty
)) {
1825 if (op
== JSOP_SETMETHOD
)
1826 defineHow
= JSDNP_CACHE_RESULT
| JSDNP_SET_METHOD
;
1827 else if (op
== JSOP_SETNAME
)
1828 defineHow
= JSDNP_CACHE_RESULT
| JSDNP_UNQUALIFIED
;
1830 defineHow
= JSDNP_CACHE_RESULT
;
1831 if (!js_SetPropertyHelper(cx
, obj
, id
, defineHow
, &rval
))
1834 if (!obj
->setProperty(cx
, id
, &rval
))
1836 ABORT_RECORDING(cx
, "Non-native set");
1839 END_SET_CASE_STORE_RVAL(JSOP_SETPROP
, 2);
1841 BEGIN_CASE(JSOP_GETELEM
)
1842 /* Open-coded ELEMENT_OP optimized for strings and dense arrays. */
1843 lval
= FETCH_OPND(-2);
1844 rval
= FETCH_OPND(-1);
1845 if (JSVAL_IS_STRING(lval
) && JSVAL_IS_INT(rval
)) {
1846 str
= JSVAL_TO_STRING(lval
);
1847 i
= JSVAL_TO_INT(rval
);
1848 if ((size_t)i
< str
->length()) {
1849 str
= JSString::getUnitString(cx
, str
, size_t(i
));
1852 rval
= STRING_TO_JSVAL(str
);
1857 VALUE_TO_OBJECT(cx
, -2, lval
, obj
);
1858 if (JSVAL_IS_INT(rval
)) {
1859 if (obj
->isDenseArray()) {
1860 jsuint idx
= jsuint(JSVAL_TO_INT(rval
));
1862 if (idx
< obj
->getArrayLength() &&
1863 idx
< obj
->getDenseArrayCapacity()) {
1864 rval
= obj
->getDenseArrayElement(idx
);
1865 if (rval
!= JSVAL_HOLE
)
1868 /* Reload rval from the stack in the rare hole case. */
1869 rval
= FETCH_OPND(-1);
1871 } else if (obj
->isArguments()
1873 && !GetArgsPrivateNative(obj
)
1876 uint32 arg
= uint32(JSVAL_TO_INT(rval
));
1878 if (arg
< obj
->getArgsLength()) {
1879 JSStackFrame
*afp
= (JSStackFrame
*) obj
->getPrivate();
1881 rval
= afp
->argv
[arg
];
1885 rval
= obj
->getArgsElement(arg
);
1886 if (rval
!= JSVAL_HOLE
)
1888 rval
= FETCH_OPND(-1);
1891 id
= INT_JSVAL_TO_JSID(rval
);
1893 if (!js_InternNonIntElementId(cx
, obj
, rval
, &id
))
1897 if (!obj
->getProperty(cx
, id
, &rval
))
1901 STORE_OPND(-1, rval
);
1902 END_CASE(JSOP_GETELEM
)
1904 BEGIN_CASE(JSOP_CALLELEM
)
1905 ELEMENT_OP(-1, js_GetMethod(cx
, obj
, id
, JSGET_NO_METHOD_BARRIER
, &rval
));
1906 #if JS_HAS_NO_SUCH_METHOD
1907 if (JS_UNLIKELY(JSVAL_IS_VOID(rval
))) {
1908 regs
.sp
[-2] = regs
.sp
[-1];
1909 regs
.sp
[-1] = OBJECT_TO_JSVAL(obj
);
1910 if (!js_OnUnknownMethod(cx
, regs
.sp
- 2))
1915 STORE_OPND(-2, rval
);
1916 STORE_OPND(-1, OBJECT_TO_JSVAL(obj
));
1918 END_CASE(JSOP_CALLELEM
)
1920 BEGIN_CASE(JSOP_SETELEM
)
1921 rval
= FETCH_OPND(-1);
1922 FETCH_OBJECT(cx
, -3, lval
, obj
);
1923 FETCH_ELEMENT_ID(obj
, -2, id
);
1925 if (obj
->isDenseArray() && JSID_IS_INT(id
)) {
1928 length
= obj
->getDenseArrayCapacity();
1929 i
= JSID_TO_INT(id
);
1930 if ((jsuint
)i
< length
) {
1931 if (obj
->getDenseArrayElement(i
) == JSVAL_HOLE
) {
1932 if (js_PrototypeHasIndexedProperties(cx
, obj
))
1934 if ((jsuint
)i
>= obj
->getArrayLength())
1935 obj
->setDenseArrayLength(i
+ 1);
1936 obj
->incDenseArrayCountBy(1);
1938 obj
->setDenseArrayElement(i
, rval
);
1943 if (!obj
->setProperty(cx
, id
, &rval
))
1946 END_SET_CASE_STORE_RVAL(JSOP_SETELEM
, 3)
1948 BEGIN_CASE(JSOP_ENUMELEM
)
1949 /* Funky: the value to set is under the [obj, id] pair. */
1950 rval
= FETCH_OPND(-3);
1951 FETCH_OBJECT(cx
, -2, lval
, obj
);
1952 FETCH_ELEMENT_ID(obj
, -1, id
);
1953 if (!obj
->setProperty(cx
, id
, &rval
))
1956 END_CASE(JSOP_ENUMELEM
)
1958 BEGIN_CASE(JSOP_NEW
)
1959 /* Get immediate argc and find the constructor function. */
1960 argc
= GET_ARGC(regs
.pc
);
1961 vp
= regs
.sp
- (2 + argc
);
1962 JS_ASSERT(vp
>= StackBase(fp
));
1965 * Assign lval, obj, and fun exactly as the code at inline_call: expects to
1966 * find them, to avoid nesting a js_Interpret call via js_InvokeConstructor.
1969 if (VALUE_IS_FUNCTION(cx
, lval
)) {
1970 obj
= JSVAL_TO_OBJECT(lval
);
1971 fun
= GET_FUNCTION_PRIVATE(cx
, obj
);
1972 if (FUN_INTERPRETED(fun
)) {
1973 /* Root as we go using vp[1]. */
1974 if (!obj
->getProperty(cx
,
1975 ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
),
1980 obj2
= NewObject(cx
, &js_ObjectClass
,
1981 JSVAL_IS_OBJECT(rval
) ? JSVAL_TO_OBJECT(rval
) : NULL
,
1986 if (fun
->u
.i
.script
->isEmpty()) {
1987 *vp
= OBJECT_TO_JSVAL(obj2
);
1992 vp
[1] = OBJECT_TO_JSVAL(obj2
);
1993 flags
= JSFRAME_CONSTRUCTING
;
1998 if (!js_InvokeConstructor(cx
, argc
, JS_FALSE
, vp
))
2001 CHECK_INTERRUPT_HANDLER();
2002 TRACE_0(NativeCallComplete
);
2007 BEGIN_CASE(JSOP_CALL
)
2008 BEGIN_CASE(JSOP_EVAL
)
2009 BEGIN_CASE(JSOP_APPLY
)
2010 argc
= GET_ARGC(regs
.pc
);
2011 vp
= regs
.sp
- (argc
+ 2);
2014 if (VALUE_IS_FUNCTION(cx
, lval
)) {
2015 obj
= JSVAL_TO_OBJECT(lval
);
2016 fun
= GET_FUNCTION_PRIVATE(cx
, obj
);
2018 /* Clear frame flags since this is not a constructor call. */
2020 if (FUN_INTERPRETED(fun
))
2023 uintN nframeslots
, nvars
, missing
;
2027 JSInlineFrame
*newifp
;
2028 JSInterpreterHook hook
;
2030 script
= fun
->u
.i
.script
;
2031 if (script
->isEmpty()) {
2032 script
= fp
->script
;
2038 /* Restrict recursion of lightweight functions. */
2039 if (inlineCallCount
>= JS_MAX_INLINE_CALL_COUNT
) {
2040 js_ReportOverRecursed(cx
);
2041 script
= fp
->script
;
2045 /* Compute the total number of stack slots needed by fun. */
2046 nframeslots
= JS_HOWMANY(sizeof(JSInlineFrame
), sizeof(jsval
));
2047 atoms
= script
->atomMap
.vector
;
2048 nbytes
= (nframeslots
+ script
->nslots
) * sizeof(jsval
);
2050 /* Allocate missing expected args adjacent to actuals. */
2051 JSArena
*a
= cx
->stackPool
.getCurrent();
2052 newmark
= (void *) a
->getAvail();
2053 if (fun
->nargs
<= argc
) {
2056 newsp
= vp
+ 2 + fun
->nargs
;
2057 JS_ASSERT(newsp
> regs
.sp
);
2058 if ((jsuword
) newsp
<= a
->getLimit()) {
2059 if ((jsuword
) newsp
> a
->getAvail())
2060 a
->setAvail(jsuword(newsp
));
2061 jsval
*argsp
= newsp
;
2063 *--argsp
= JSVAL_VOID
;
2064 } while (argsp
!= regs
.sp
);
2067 missing
= fun
->nargs
- argc
;
2068 nbytes
+= (2 + fun
->nargs
) * sizeof(jsval
);
2072 /* Allocate the inline frame with its slots and operands. */
2073 if (a
->getAvail() + nbytes
<= a
->getLimit()) {
2074 newsp
= (jsval
*) a
->getAvail();
2075 a
->setAvail(a
->getAvail() + nbytes
);
2076 JS_ASSERT(missing
== 0);
2078 cx
->stackPool
.allocateCast
<jsval
*>(newsp
, nbytes
);
2080 js_ReportOutOfScriptQuota(cx
);
2081 goto bad_inline_call
;
2085 * Move args if the missing ones overflow arena a, then push
2086 * undefined for the missing args.
2089 memcpy(newsp
, vp
, (2 + argc
) * sizeof(jsval
));
2091 newsp
= vp
+ 2 + argc
;
2093 *newsp
++ = JSVAL_VOID
;
2094 } while (--missing
!= 0);
2098 /* Claim space for the stack frame and initialize it. */
2099 newifp
= (JSInlineFrame
*) newsp
;
2100 newsp
+= nframeslots
;
2101 newifp
->frame
.callobj
= NULL
;
2102 newifp
->frame
.argsobj
= NULL
;
2103 newifp
->frame
.script
= script
;
2104 newifp
->frame
.fun
= fun
;
2105 newifp
->frame
.argc
= argc
;
2106 newifp
->frame
.argv
= vp
+ 2;
2107 newifp
->frame
.rval
= JSVAL_VOID
;
2108 newifp
->frame
.down
= fp
;
2109 newifp
->frame
.annotation
= NULL
;
2110 newifp
->frame
.scopeChain
= parent
= obj
->getParent();
2111 newifp
->frame
.flags
= flags
;
2112 newifp
->frame
.blockChain
= NULL
;
2113 if (script
->staticLevel
< JS_DISPLAY_SIZE
) {
2114 JSStackFrame
**disp
= &cx
->display
[script
->staticLevel
];
2115 newifp
->frame
.displaySave
= *disp
;
2116 *disp
= &newifp
->frame
;
2118 newifp
->mark
= newmark
;
2120 /* Compute the 'this' parameter now that argv is set. */
2121 JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun
->flags
));
2122 newifp
->frame
.thisv
= vp
[1];
2124 newifp
->frame
.regs
= NULL
;
2125 newifp
->frame
.imacpc
= NULL
;
2126 newifp
->frame
.slots
= newsp
;
2128 /* Push void to initialize local variables. */
2129 nvars
= fun
->u
.i
.nvars
;
2131 *newsp
++ = JSVAL_VOID
;
2133 /* Scope with a call object parented by callee's parent. */
2134 if (JSFUN_HEAVYWEIGHT_TEST(fun
->flags
) &&
2135 !js_GetCallObject(cx
, &newifp
->frame
)) {
2136 goto bad_inline_call
;
2139 /* Switch version if currentVersion wasn't overridden. */
2140 newifp
->callerVersion
= (JSVersion
) cx
->version
;
2141 if (JS_LIKELY(cx
->version
== currentVersion
)) {
2142 currentVersion
= (JSVersion
) script
->version
;
2143 if (currentVersion
!= cx
->version
)
2144 js_SetVersion(cx
, currentVersion
);
2147 /* Push the frame and set interpreter registers. */
2148 newifp
->callerRegs
= regs
;
2149 fp
->regs
= &newifp
->callerRegs
;
2151 regs
.pc
= script
->code
;
2152 newifp
->frame
.regs
= ®s
;
2153 cx
->fp
= fp
= &newifp
->frame
;
2155 /* Call the debugger hook if present. */
2156 hook
= cx
->debugHooks
->callHook
;
2158 newifp
->hookData
= hook(cx
, &newifp
->frame
, JS_TRUE
, 0,
2159 cx
->debugHooks
->callHookData
);
2160 CHECK_INTERRUPT_HANDLER();
2162 newifp
->hookData
= NULL
;
2166 JS_RUNTIME_METER(rt
, inlineCalls
);
2168 DTrace::enterJSFun(cx
, fp
, fun
, fp
->down
, fp
->argc
, fp
->argv
);
2171 if (TraceRecorder
*tr
= TRACE_RECORDER(cx
)) {
2172 AbortableRecordingStatus status
= tr
->record_EnterFrame(inlineCallCount
);
2173 RESTORE_INTERP_VARS();
2174 if (StatusAbortsRecorderIfActive(status
)) {
2175 if (TRACE_RECORDER(cx
)) {
2176 JS_ASSERT(TRACE_RECORDER(cx
) == tr
);
2177 AbortRecording(cx
, "record_EnterFrame failed");
2179 if (status
== ARECORD_ERROR
)
2182 } else if (fp
->script
== fp
->down
->script
&&
2183 *fp
->down
->regs
->pc
== JSOP_CALL
&&
2184 *fp
->regs
->pc
== JSOP_TRACE
) {
2185 MONITOR_BRANCH(Record_EnterFrame
);
2189 /* Load first op and dispatch it (safe since JSOP_STOP). */
2190 op
= (JSOp
) *regs
.pc
;
2194 JS_ASSERT(fp
->regs
== ®s
);
2195 script
= fp
->script
;
2196 atoms
= script
->atomMap
.vector
;
2197 js_FreeRawStack(cx
, newmark
);
2201 if (fun
->flags
& JSFUN_FAST_NATIVE
) {
2202 DTrace::enterJSFun(cx
, NULL
, fun
, fp
, argc
, vp
+ 2, &lval
);
2204 JS_ASSERT(fun
->u
.n
.extra
== 0);
2205 JS_ASSERT(JSVAL_IS_OBJECT(vp
[1]) ||
2206 PRIMITIVE_THIS_TEST(fun
, vp
[1]));
2207 ok
= ((JSFastNative
) fun
->u
.n
.native
)(cx
, argc
, vp
);
2208 DTrace::exitJSFun(cx
, NULL
, fun
, *vp
, &lval
);
2212 TRACE_0(NativeCallComplete
);
2217 ok
= js_Invoke(cx
, argc
, vp
, 0);
2219 CHECK_INTERRUPT_HANDLER();
2222 JS_RUNTIME_METER(rt
, nonInlineCalls
);
2223 TRACE_0(NativeCallComplete
);
2228 BEGIN_CASE(JSOP_SETCALL
)
2229 argc
= GET_ARGC(regs
.pc
);
2230 vp
= regs
.sp
- argc
- 2;
2231 if (js_Invoke(cx
, argc
, vp
, 0))
2232 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_LEFTSIDE_OF_ASS
);
2234 END_CASE(JSOP_SETCALL
)
2236 BEGIN_CASE(JSOP_NAME
)
2237 BEGIN_CASE(JSOP_CALLNAME
)
2239 PropertyCacheEntry
*entry
;
2241 obj
= fp
->scopeChain
;
2243 JS_PROPERTY_CACHE(cx
).test(cx
, regs
.pc
, obj
, obj2
, entry
, atom
);
2245 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj
, obj2
, entry
);
2246 if (entry
->vword
.isObject()) {
2247 rval
= entry
->vword
.toJsval();
2251 if (entry
->vword
.isSlot()) {
2252 slot
= entry
->vword
.toSlot();
2253 JS_ASSERT(slot
< obj2
->scope()->freeslot
);
2254 rval
= obj2
->lockedGetSlot(slot
);
2258 JS_ASSERT(entry
->vword
.isSprop());
2259 sprop
= entry
->vword
.toSprop();
2263 id
= ATOM_TO_JSID(atom
);
2264 if (!js_FindPropertyHelper(cx
, id
, true, &obj
, &obj2
, &prop
))
2267 /* Kludge to allow (typeof foo == "undefined") tests. */
2268 endpc
= script
->code
+ script
->length
;
2269 op2
= js_GetOpcode(cx
, script
, regs
.pc
+ JSOP_NAME_LENGTH
);
2270 if (op2
== JSOP_TYPEOF
) {
2271 PUSH_OPND(JSVAL_VOID
);
2272 len
= JSOP_NAME_LENGTH
;
2275 goto atom_not_defined
;
2278 /* Take the slow path if prop was not found in a native object. */
2279 if (!obj
->isNative() || !obj2
->isNative()) {
2280 obj2
->dropProperty(cx
, prop
);
2281 if (!obj
->getProperty(cx
, id
, &rval
))
2284 sprop
= (JSScopeProperty
*)prop
;
2286 NATIVE_GET(cx
, obj
, obj2
, sprop
, JSGET_METHOD_BARRIER
, &rval
);
2287 obj2
->dropProperty(cx
, (JSProperty
*) sprop
);
2292 if (op
== JSOP_CALLNAME
)
2293 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
2297 BEGIN_CASE(JSOP_UINT16
)
2298 i
= (jsint
) GET_UINT16(regs
.pc
);
2299 rval
= INT_TO_JSVAL(i
);
2301 END_CASE(JSOP_UINT16
)
2303 BEGIN_CASE(JSOP_UINT24
)
2304 i
= (jsint
) GET_UINT24(regs
.pc
);
2305 rval
= INT_TO_JSVAL(i
);
2307 END_CASE(JSOP_UINT24
)
2309 BEGIN_CASE(JSOP_INT8
)
2310 i
= GET_INT8(regs
.pc
);
2311 rval
= INT_TO_JSVAL(i
);
2315 BEGIN_CASE(JSOP_INT32
)
2316 i
= GET_INT32(regs
.pc
);
2317 rval
= INT_TO_JSVAL(i
);
2319 END_CASE(JSOP_INT32
)
2321 BEGIN_CASE(JSOP_INDEXBASE
)
2323 * Here atoms can exceed script->atomMap.length as we use atoms as a
2324 * segment register for object literals as well.
2326 atoms
+= GET_INDEXBASE(regs
.pc
);
2327 END_CASE(JSOP_INDEXBASE
)
2329 BEGIN_CASE(JSOP_INDEXBASE1
)
2330 BEGIN_CASE(JSOP_INDEXBASE2
)
2331 BEGIN_CASE(JSOP_INDEXBASE3
)
2332 atoms
+= (op
- JSOP_INDEXBASE1
+ 1) << 16;
2333 END_CASE(JSOP_INDEXBASE3
)
2335 BEGIN_CASE(JSOP_RESETBASE0
)
2336 BEGIN_CASE(JSOP_RESETBASE
)
2337 atoms
= script
->atomMap
.vector
;
2338 END_CASE(JSOP_RESETBASE
)
2340 BEGIN_CASE(JSOP_DOUBLE
)
2341 JS_ASSERT(!fp
->imacpc
);
2342 JS_ASSERT(size_t(atoms
- script
->atomMap
.vector
) < script
->atomMap
.length
);
2345 BEGIN_CASE(JSOP_STRING
)
2347 PUSH_OPND(ATOM_KEY(atom
));
2348 END_CASE(JSOP_DOUBLE
)
2350 BEGIN_CASE(JSOP_OBJECT
)
2352 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
2353 END_CASE(JSOP_OBJECT
)
2355 BEGIN_CASE(JSOP_REGEXP
) {
2357 * Push a regexp object cloned from the regexp literal object mapped by the
2358 * bytecode at pc. ES5 finally fixed this bad old ES3 design flaw which was
2359 * flouted by many browser-based implementations.
2361 * We avoid the js_GetScopeChain call here and pass fp->scopeChain as
2362 * js_GetClassPrototype uses the latter only to locate the global.
2364 index
= GET_FULL_INDEX(0);
2366 if (!js_GetClassPrototype(cx
, fp
->scopeChain
, JSProto_RegExp
, &proto
))
2369 obj
= js_CloneRegExpObject(cx
, script
->getRegExp(index
), proto
);
2372 rval
= OBJECT_TO_JSVAL(obj
);
2375 END_CASE(JSOP_REGEXP
)
2377 BEGIN_CASE(JSOP_ZERO
)
2378 PUSH_OPND(JSVAL_ZERO
);
2381 BEGIN_CASE(JSOP_ONE
)
2382 PUSH_OPND(JSVAL_ONE
);
2385 BEGIN_CASE(JSOP_NULL
)
2386 PUSH_OPND(JSVAL_NULL
);
2389 BEGIN_CASE(JSOP_FALSE
)
2390 PUSH_OPND(JSVAL_FALSE
);
2391 END_CASE(JSOP_FALSE
)
2393 BEGIN_CASE(JSOP_TRUE
)
2394 PUSH_OPND(JSVAL_TRUE
);
2397 BEGIN_CASE(JSOP_TABLESWITCH
)
2399 len
= GET_JUMP_OFFSET(pc2
);
2402 * ECMAv2+ forbids conversion of discriminant, so we will skip to the
2403 * default case if the discriminant isn't already an int jsval. (This
2404 * opcode is emitted only for dense jsint-domain switches.)
2407 if (JSVAL_IS_INT(rval
)) {
2408 i
= JSVAL_TO_INT(rval
);
2409 } else if (JSVAL_IS_DOUBLE(rval
) && *JSVAL_TO_DOUBLE(rval
) == 0) {
2410 /* Treat -0 (double) as 0. */
2416 pc2
+= JUMP_OFFSET_LEN
;
2417 low
= GET_JUMP_OFFSET(pc2
);
2418 pc2
+= JUMP_OFFSET_LEN
;
2419 high
= GET_JUMP_OFFSET(pc2
);
2422 if ((jsuint
)i
< (jsuint
)(high
- low
+ 1)) {
2423 pc2
+= JUMP_OFFSET_LEN
+ JUMP_OFFSET_LEN
* i
;
2424 off
= (jsint
) GET_JUMP_OFFSET(pc2
);
2430 BEGIN_CASE(JSOP_TABLESWITCHX
)
2432 len
= GET_JUMPX_OFFSET(pc2
);
2435 * ECMAv2+ forbids conversion of discriminant, so we will skip to the
2436 * default case if the discriminant isn't already an int jsval. (This
2437 * opcode is emitted only for dense jsint-domain switches.)
2440 if (JSVAL_IS_INT(rval
)) {
2441 i
= JSVAL_TO_INT(rval
);
2442 } else if (JSVAL_IS_DOUBLE(rval
) && *JSVAL_TO_DOUBLE(rval
) == 0) {
2443 /* Treat -0 (double) as 0. */
2449 pc2
+= JUMPX_OFFSET_LEN
;
2450 low
= GET_JUMP_OFFSET(pc2
);
2451 pc2
+= JUMP_OFFSET_LEN
;
2452 high
= GET_JUMP_OFFSET(pc2
);
2455 if ((jsuint
)i
< (jsuint
)(high
- low
+ 1)) {
2456 pc2
+= JUMP_OFFSET_LEN
+ JUMPX_OFFSET_LEN
* i
;
2457 off
= (jsint
) GET_JUMPX_OFFSET(pc2
);
2463 BEGIN_CASE(JSOP_LOOKUPSWITCHX
)
2464 off
= JUMPX_OFFSET_LEN
;
2465 goto do_lookup_switch
;
2467 BEGIN_CASE(JSOP_LOOKUPSWITCH
)
2468 off
= JUMP_OFFSET_LEN
;
2472 * JSOP_LOOKUPSWITCH and JSOP_LOOKUPSWITCHX are never used if any atom
2473 * index in it would exceed 64K limit.
2475 JS_ASSERT(!fp
->imacpc
);
2476 JS_ASSERT(atoms
== script
->atomMap
.vector
);
2480 if (!JSVAL_IS_PRIMITIVE(lval
))
2481 goto end_lookup_switch
;
2484 npairs
= (jsint
) GET_UINT16(pc2
);
2486 JS_ASSERT(npairs
); /* empty switch uses JSOP_TABLESWITCH */
2488 #define SEARCH_PAIRS(MATCH_CODE) \
2490 JS_ASSERT(GET_INDEX(pc2) < script->atomMap.length); \
2491 atom = atoms[GET_INDEX(pc2)]; \
2492 rval = ATOM_KEY(atom); \
2498 if (--npairs == 0) { \
2504 if (JSVAL_IS_STRING(lval
)) {
2505 str
= JSVAL_TO_STRING(lval
);
2507 match
= (JSVAL_IS_STRING(rval
) &&
2508 ((str2
= JSVAL_TO_STRING(rval
)) == str
||
2509 js_EqualStrings(str2
, str
)));
2511 } else if (JSVAL_IS_DOUBLE(lval
)) {
2512 d
= *JSVAL_TO_DOUBLE(lval
);
2514 match
= (JSVAL_IS_DOUBLE(rval
) &&
2515 *JSVAL_TO_DOUBLE(rval
) == d
);
2519 match
= (lval
== rval
);
2525 len
= (op
== JSOP_LOOKUPSWITCH
)
2526 ? GET_JUMP_OFFSET(pc2
)
2527 : GET_JUMPX_OFFSET(pc2
);
2530 BEGIN_CASE(JSOP_TRAP
)
2532 JSTrapStatus status
;
2534 status
= JS_HandleTrap(cx
, script
, regs
.pc
, &rval
);
2543 cx
->throwing
= JS_TRUE
;
2544 cx
->exception
= rval
;
2549 JS_ASSERT(status
== JSTRAP_CONTINUE
);
2550 CHECK_INTERRUPT_HANDLER();
2551 JS_ASSERT(JSVAL_IS_INT(rval
));
2552 op
= (JSOp
) JSVAL_TO_INT(rval
);
2553 JS_ASSERT((uintN
)op
< (uintN
)JSOP_LIMIT
);
2557 BEGIN_CASE(JSOP_ARGUMENTS
)
2558 if (!js_GetArgsValue(cx
, fp
, &rval
))
2561 END_CASE(JSOP_ARGUMENTS
)
2563 BEGIN_CASE(JSOP_ARGSUB
)
2564 id
= INT_TO_JSID(GET_ARGNO(regs
.pc
));
2565 if (!js_GetArgsProperty(cx
, fp
, id
, &rval
))
2568 END_CASE(JSOP_ARGSUB
)
2570 BEGIN_CASE(JSOP_ARGCNT
)
2571 id
= ATOM_TO_JSID(rt
->atomState
.lengthAtom
);
2572 if (!js_GetArgsProperty(cx
, fp
, id
, &rval
))
2575 END_CASE(JSOP_ARGCNT
)
2577 BEGIN_CASE(JSOP_GETARG
)
2578 BEGIN_CASE(JSOP_CALLARG
)
2579 slot
= GET_ARGNO(regs
.pc
);
2580 JS_ASSERT(slot
< fp
->fun
->nargs
);
2581 METER_SLOT_OP(op
, slot
);
2582 PUSH_OPND(fp
->argv
[slot
]);
2583 if (op
== JSOP_CALLARG
)
2584 PUSH_OPND(JSVAL_NULL
);
2585 END_CASE(JSOP_GETARG
)
2587 BEGIN_CASE(JSOP_SETARG
)
2588 slot
= GET_ARGNO(regs
.pc
);
2589 JS_ASSERT(slot
< fp
->fun
->nargs
);
2590 METER_SLOT_OP(op
, slot
);
2591 vp
= &fp
->argv
[slot
];
2592 *vp
= FETCH_OPND(-1);
2593 END_SET_CASE(JSOP_SETARG
)
2595 BEGIN_CASE(JSOP_GETLOCAL
)
2596 slot
= GET_SLOTNO(regs
.pc
);
2597 JS_ASSERT(slot
< script
->nslots
);
2598 PUSH_OPND(fp
->slots
[slot
]);
2599 END_CASE(JSOP_GETLOCAL
)
2601 BEGIN_CASE(JSOP_CALLLOCAL
)
2602 slot
= GET_SLOTNO(regs
.pc
);
2603 JS_ASSERT(slot
< script
->nslots
);
2604 PUSH_OPND(fp
->slots
[slot
]);
2605 PUSH_OPND(JSVAL_NULL
);
2606 END_CASE(JSOP_CALLLOCAL
)
2608 BEGIN_CASE(JSOP_SETLOCAL
)
2609 slot
= GET_SLOTNO(regs
.pc
);
2610 JS_ASSERT(slot
< script
->nslots
);
2611 vp
= &fp
->slots
[slot
];
2612 *vp
= FETCH_OPND(-1);
2613 END_SET_CASE(JSOP_SETLOCAL
)
2615 BEGIN_CASE(JSOP_GETUPVAR
)
2616 BEGIN_CASE(JSOP_CALLUPVAR
)
2618 JSUpvarArray
*uva
= script
->upvars();
2620 index
= GET_UINT16(regs
.pc
);
2621 JS_ASSERT(index
< uva
->length
);
2623 rval
= js_GetUpvar(cx
, script
->staticLevel
, uva
->vector
[index
]);
2626 if (op
== JSOP_CALLUPVAR
)
2627 PUSH_OPND(JSVAL_NULL
);
2629 END_CASE(JSOP_GETUPVAR
)
2631 BEGIN_CASE(JSOP_GETUPVAR_DBG
)
2632 BEGIN_CASE(JSOP_CALLUPVAR_DBG
)
2634 JS_ASSERT(FUN_KIND(fun
) == JSFUN_INTERPRETED
);
2635 JS_ASSERT(fun
->u
.i
.wrapper
);
2637 /* Scope for tempPool mark and local names allocation in it. */
2639 void *mark
= cx
->tempPool
.getMark();
2640 jsuword
*names
= js_GetLocalNameArray(cx
, fun
, &cx
->tempPool
);
2644 index
= fun
->countArgsAndVars() + GET_UINT16(regs
.pc
);
2645 atom
= JS_LOCAL_NAME_TO_ATOM(names
[index
]);
2646 id
= ATOM_TO_JSID(atom
);
2648 ok
= js_FindProperty(cx
, id
, &obj
, &obj2
, &prop
);
2649 cx
->tempPool
.release(mark
);
2655 goto atom_not_defined
;
2657 /* Minimize footprint with generic code instead of NATIVE_GET. */
2658 obj2
->dropProperty(cx
, prop
);
2660 PUSH_OPND(JSVAL_NULL
);
2661 if (!obj
->getProperty(cx
, id
, vp
))
2664 if (op
== JSOP_CALLUPVAR_DBG
)
2665 PUSH_OPND(JSVAL_NULL
);
2666 END_CASE(JSOP_GETUPVAR_DBG
)
2668 BEGIN_CASE(JSOP_GETDSLOT
)
2669 BEGIN_CASE(JSOP_CALLDSLOT
)
2670 JS_ASSERT(fp
->argv
);
2671 obj
= JSVAL_TO_OBJECT(fp
->argv
[-2]);
2673 JS_ASSERT(obj
->dslots
);
2675 index
= GET_UINT16(regs
.pc
);
2676 JS_ASSERT(JS_INITIAL_NSLOTS
+ index
< jsatomid(obj
->dslots
[-1]));
2677 JS_ASSERT_IF(obj
->scope()->object
== obj
,
2678 JS_INITIAL_NSLOTS
+ index
< obj
->scope()->freeslot
);
2680 PUSH_OPND(obj
->dslots
[index
]);
2681 if (op
== JSOP_CALLDSLOT
)
2682 PUSH_OPND(JSVAL_NULL
);
2683 END_CASE(JSOP_GETDSLOT
)
2685 BEGIN_CASE(JSOP_GETGVAR
)
2686 BEGIN_CASE(JSOP_CALLGVAR
)
2687 slot
= GET_SLOTNO(regs
.pc
);
2688 JS_ASSERT(slot
< GlobalVarCount(fp
));
2689 METER_SLOT_OP(op
, slot
);
2690 lval
= fp
->slots
[slot
];
2691 if (JSVAL_IS_NULL(lval
)) {
2692 op
= (op
== JSOP_GETGVAR
) ? JSOP_NAME
: JSOP_CALLNAME
;
2695 JS_ASSERT(fp
->varobj(cx
) == cx
->activeCallStack()->getInitialVarObj());
2696 obj
= cx
->activeCallStack()->getInitialVarObj();
2697 slot
= JSVAL_TO_INT(lval
);
2698 rval
= obj
->getSlotMT(cx
, slot
);
2700 if (op
== JSOP_CALLGVAR
)
2701 PUSH_OPND(JSVAL_NULL
);
2702 END_CASE(JSOP_GETGVAR
)
2704 BEGIN_CASE(JSOP_SETGVAR
)
2705 slot
= GET_SLOTNO(regs
.pc
);
2706 JS_ASSERT(slot
< GlobalVarCount(fp
));
2707 METER_SLOT_OP(op
, slot
);
2708 rval
= FETCH_OPND(-1);
2709 JS_ASSERT(fp
->varobj(cx
) == cx
->activeCallStack()->getInitialVarObj());
2710 obj
= cx
->activeCallStack()->getInitialVarObj();
2711 lval
= fp
->slots
[slot
];
2712 if (JSVAL_IS_NULL(lval
)) {
2714 * Inline-clone and deoptimize JSOP_SETNAME code here because
2715 * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval]
2716 * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME.
2719 if (TRACE_RECORDER(cx
))
2720 AbortRecording(cx
, "SETGVAR with NULL slot");
2723 id
= ATOM_TO_JSID(atom
);
2724 if (!obj
->setProperty(cx
, id
, &rval
))
2727 slot
= JSVAL_TO_INT(lval
);
2728 JS_LOCK_OBJ(cx
, obj
);
2729 JSScope
*scope
= obj
->scope();
2730 if (!scope
->methodWriteBarrier(cx
, slot
, rval
)) {
2731 JS_UNLOCK_SCOPE(cx
, scope
);
2734 obj
->lockedSetSlot(slot
, rval
);
2735 JS_UNLOCK_SCOPE(cx
, scope
);
2737 END_SET_CASE(JSOP_SETGVAR
)
2739 BEGIN_CASE(JSOP_DEFCONST
)
2740 BEGIN_CASE(JSOP_DEFVAR
)
2741 index
= GET_INDEX(regs
.pc
);
2742 atom
= atoms
[index
];
2745 * index is relative to atoms at this point but for global var
2746 * code below we need the absolute value.
2748 index
+= atoms
- script
->atomMap
.vector
;
2749 obj
= fp
->varobj(cx
);
2750 JS_ASSERT(obj
->map
->ops
->defineProperty
== js_DefineProperty
);
2751 attrs
= JSPROP_ENUMERATE
;
2752 if (!(fp
->flags
& JSFRAME_EVAL
))
2753 attrs
|= JSPROP_PERMANENT
;
2754 if (op
== JSOP_DEFCONST
)
2755 attrs
|= JSPROP_READONLY
;
2757 /* Lookup id in order to check for redeclaration problems. */
2758 id
= ATOM_TO_JSID(atom
);
2760 if (op
== JSOP_DEFVAR
) {
2762 * Redundant declaration of a |var|, even one for a non-writable
2763 * property like |undefined| in ES5, does nothing.
2765 if (!obj
->lookupProperty(cx
, id
, &obj2
, &prop
))
2768 if (!js_CheckRedeclaration(cx
, obj
, id
, attrs
, &obj2
, &prop
))
2772 /* Bind a variable only if it's not yet defined. */
2774 if (!js_DefineNativeProperty(cx
, obj
, id
, JSVAL_VOID
, JS_PropertyStub
, JS_PropertyStub
,
2775 attrs
, 0, 0, &prop
)) {
2783 * Try to optimize a property we either just created, or found
2784 * directly in the global object, that is permanent, has a slot,
2785 * and has stub getter and setter, into a "fast global" accessed
2786 * by the JSOP_*GVAR opcodes.
2789 index
< GlobalVarCount(fp
) &&
2792 sprop
= (JSScopeProperty
*) prop
;
2793 if (!sprop
->configurable() &&
2794 SPROP_HAS_VALID_SLOT(sprop
, obj
->scope()) &&
2795 sprop
->hasDefaultGetterOrIsMethod() &&
2796 sprop
->hasDefaultSetter()) {
2798 * Fast globals use frame variables to map the global name's atom
2799 * index to the permanent varobj slot number, tagged as a jsval.
2800 * The atom index for the global's name literal is identical to its
2803 fp
->slots
[index
] = INT_TO_JSVAL(sprop
->slot
);
2807 obj2
->dropProperty(cx
, prop
);
2808 END_CASE(JSOP_DEFVAR
)
2810 BEGIN_CASE(JSOP_DEFFUN
)
2812 JSPropertyOp getter
, setter
;
2819 * A top-level function defined in Global or Eval code (see ECMA-262
2820 * Ed. 3), or else a SpiderMonkey extension: a named function statement in
2821 * a compound statement (not at the top statement level of global code, or
2822 * at the top level of a function body).
2825 obj
= FUN_OBJECT(fun
);
2827 if (FUN_NULL_CLOSURE(fun
)) {
2829 * Even a null closure needs a parent for principals finding.
2830 * FIXME: bug 476950, although debugger users may also demand some kind
2831 * of scope link for debugger-assisted eval-in-frame.
2833 obj2
= fp
->scopeChain
;
2835 JS_ASSERT(!FUN_FLAT_CLOSURE(fun
));
2838 * Inline js_GetScopeChain a bit to optimize for the case of a
2839 * top-level function.
2841 if (!fp
->blockChain
) {
2842 obj2
= fp
->scopeChain
;
2844 obj2
= js_GetScopeChain(cx
, fp
);
2851 * If static link is not current scope, clone fun's object to link to the
2852 * current scope via parent. We do this to enable sharing of compiled
2853 * functions among multiple equivalent scopes, amortizing the cost of
2854 * compilation over a number of executions. Examples include XUL scripts
2855 * and event handlers shared among Firefox or other Mozilla app chrome
2856 * windows, and user-defined JS functions precompiled and then shared among
2857 * requests in server-side JS.
2859 if (obj
->getParent() != obj2
) {
2860 obj
= CloneFunctionObject(cx
, fun
, obj2
);
2866 * Protect obj from any GC hiding below JSObject::setProperty or
2867 * JSObject::defineProperty. All paths from here must flow through the
2868 * fp->scopeChain code below the parent->defineProperty call.
2870 MUST_FLOW_THROUGH("restore_scope");
2871 fp
->scopeChain
= obj
;
2873 rval
= OBJECT_TO_JSVAL(obj
);
2876 * ECMA requires functions defined when entering Eval code to be
2879 attrs
= (fp
->flags
& JSFRAME_EVAL
)
2881 : JSPROP_ENUMERATE
| JSPROP_PERMANENT
;
2884 * Load function flags that are also property attributes. Getters and
2885 * setters do not need a slot, their value is stored elsewhere in the
2886 * property itself, not in obj slots.
2888 getter
= setter
= JS_PropertyStub
;
2889 flags
= JSFUN_GSFLAG2ATTR(fun
->flags
);
2891 /* Function cannot be both getter a setter. */
2892 JS_ASSERT(flags
== JSPROP_GETTER
|| flags
== JSPROP_SETTER
);
2893 attrs
|= flags
| JSPROP_SHARED
;
2895 if (flags
== JSPROP_GETTER
)
2896 getter
= CastAsPropertyOp(obj
);
2898 setter
= CastAsPropertyOp(obj
);
2902 * We define the function as a property of the variable object and not the
2903 * current scope chain even for the case of function expression statements
2904 * and functions defined by eval inside let or with blocks.
2906 parent
= fp
->varobj(cx
);
2910 * Check for a const property of the same name -- or any kind of property
2911 * if executing with the strict option. We check here at runtime as well
2912 * as at compile-time, to handle eval as well as multiple HTML script tags.
2914 id
= ATOM_TO_JSID(fun
->atom
);
2916 ok
= js_CheckRedeclaration(cx
, parent
, id
, attrs
, &pobj
, &prop
);
2921 * We deviate from 10.1.2 in ECMA 262 v3 and under eval use for function
2922 * declarations JSObject::setProperty, not JSObject::defineProperty, to
2923 * preserve the JSOP_PERMANENT attribute of existing properties and make
2924 * sure that such properties cannot be deleted.
2926 * We also use JSObject::setProperty for the existing properties of Call
2927 * objects with matching attributes to preserve the native getters and
2928 * setters that store the value of the property in the interpreter frame,
2931 doSet
= (attrs
== JSPROP_ENUMERATE
);
2932 JS_ASSERT_IF(doSet
, fp
->flags
& JSFRAME_EVAL
);
2934 if (parent
== pobj
&&
2935 parent
->getClass() == &js_CallClass
&&
2936 (old
= ((JSScopeProperty
*) prop
)->attributes(),
2937 !(old
& (JSPROP_GETTER
|JSPROP_SETTER
)) &&
2938 (old
& (JSPROP_ENUMERATE
|JSPROP_PERMANENT
)) == attrs
)) {
2940 * js_CheckRedeclaration must reject attempts to add a getter or
2941 * setter to an existing property without a getter or setter.
2943 JS_ASSERT(!(attrs
& ~(JSPROP_ENUMERATE
|JSPROP_PERMANENT
)));
2944 JS_ASSERT(!(old
& JSPROP_READONLY
));
2947 pobj
->dropProperty(cx
, prop
);
2950 ? parent
->setProperty(cx
, id
, &rval
)
2951 : parent
->defineProperty(cx
, id
, rval
, getter
, setter
, attrs
);
2954 /* Restore fp->scopeChain now that obj is defined in fp->callobj. */
2955 fp
->scopeChain
= obj2
;
2959 END_CASE(JSOP_DEFFUN
)
2961 BEGIN_CASE(JSOP_DEFFUN_FC
)
2962 BEGIN_CASE(JSOP_DEFFUN_DBGFC
)
2965 obj
= (op
== JSOP_DEFFUN_FC
)
2966 ? js_NewFlatClosure(cx
, fun
)
2967 : js_NewDebuggableFlatClosure(cx
, fun
);
2970 rval
= OBJECT_TO_JSVAL(obj
);
2972 attrs
= (fp
->flags
& JSFRAME_EVAL
)
2974 : JSPROP_ENUMERATE
| JSPROP_PERMANENT
;
2976 flags
= JSFUN_GSFLAG2ATTR(fun
->flags
);
2978 attrs
|= flags
| JSPROP_SHARED
;
2982 parent
= fp
->varobj(cx
);
2985 id
= ATOM_TO_JSID(fun
->atom
);
2986 ok
= js_CheckRedeclaration(cx
, parent
, id
, attrs
, NULL
, NULL
);
2988 if (attrs
== JSPROP_ENUMERATE
) {
2989 JS_ASSERT(fp
->flags
& JSFRAME_EVAL
);
2990 ok
= parent
->setProperty(cx
, id
, &rval
);
2992 JS_ASSERT(attrs
& JSPROP_PERMANENT
);
2994 ok
= parent
->defineProperty(cx
, id
, rval
,
2995 (flags
& JSPROP_GETTER
)
2996 ? CastAsPropertyOp(obj
)
2998 (flags
& JSPROP_SETTER
)
2999 ? CastAsPropertyOp(obj
)
3007 END_CASE(JSOP_DEFFUN_FC
)
3009 BEGIN_CASE(JSOP_DEFLOCALFUN
)
3011 * Define a local function (i.e., one nested at the top level of another
3012 * function), parented by the current scope chain, stored in a local
3013 * variable slot that the compiler allocated. This is an optimization over
3014 * JSOP_DEFFUN that avoids requiring a call object for the outer function's
3017 LOAD_FUNCTION(SLOTNO_LEN
);
3018 JS_ASSERT(FUN_INTERPRETED(fun
));
3019 JS_ASSERT(!FUN_FLAT_CLOSURE(fun
));
3020 obj
= FUN_OBJECT(fun
);
3022 if (FUN_NULL_CLOSURE(fun
)) {
3023 obj
= CloneFunctionObject(cx
, fun
, fp
->scopeChain
);
3027 parent
= js_GetScopeChain(cx
, fp
);
3031 if (obj
->getParent() != parent
) {
3033 if (TRACE_RECORDER(cx
))
3034 AbortRecording(cx
, "DEFLOCALFUN for closure");
3036 obj
= CloneFunctionObject(cx
, fun
, parent
);
3042 slot
= GET_SLOTNO(regs
.pc
);
3043 TRACE_2(DefLocalFunSetSlot
, slot
, obj
);
3045 fp
->slots
[slot
] = OBJECT_TO_JSVAL(obj
);
3046 END_CASE(JSOP_DEFLOCALFUN
)
3048 BEGIN_CASE(JSOP_DEFLOCALFUN_FC
)
3049 LOAD_FUNCTION(SLOTNO_LEN
);
3051 obj
= js_NewFlatClosure(cx
, fun
);
3055 slot
= GET_SLOTNO(regs
.pc
);
3056 TRACE_2(DefLocalFunSetSlot
, slot
, obj
);
3058 fp
->slots
[slot
] = OBJECT_TO_JSVAL(obj
);
3059 END_CASE(JSOP_DEFLOCALFUN_FC
)
3061 BEGIN_CASE(JSOP_DEFLOCALFUN_DBGFC
)
3062 LOAD_FUNCTION(SLOTNO_LEN
);
3064 obj
= js_NewDebuggableFlatClosure(cx
, fun
);
3068 slot
= GET_SLOTNO(regs
.pc
);
3069 fp
->slots
[slot
] = OBJECT_TO_JSVAL(obj
);
3070 END_CASE(JSOP_DEFLOCALFUN_DBGFC
)
3072 BEGIN_CASE(JSOP_LAMBDA
)
3073 /* Load the specified function object literal. */
3075 obj
= FUN_OBJECT(fun
);
3077 /* do-while(0) so we can break instead of using a goto. */
3079 if (FUN_NULL_CLOSURE(fun
)) {
3080 parent
= fp
->scopeChain
;
3082 if (obj
->getParent() == parent
) {
3083 op
= JSOp(regs
.pc
[JSOP_LAMBDA_LENGTH
]);
3086 * Optimize ({method: function () { ... }, ...}) and
3087 * this.method = function () { ... }; bytecode sequences.
3089 if (op
== JSOP_SETMETHOD
) {
3091 op2
= JSOp(regs
.pc
[JSOP_LAMBDA_LENGTH
+ JSOP_SETMETHOD_LENGTH
]);
3092 JS_ASSERT(op2
== JSOP_POP
|| op2
== JSOP_POPV
);
3095 lval
= FETCH_OPND(-1);
3096 if (JSVAL_IS_OBJECT(lval
) &&
3097 (obj2
= JSVAL_TO_OBJECT(lval
)) &&
3098 obj2
->getClass() == &js_ObjectClass
) {
3101 } else if (op
== JSOP_INITMETHOD
) {
3102 lval
= FETCH_OPND(-1);
3103 JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval
));
3104 obj2
= JSVAL_TO_OBJECT(lval
);
3105 JS_ASSERT(obj2
->getClass() == &js_ObjectClass
);
3106 JS_ASSERT(obj2
->scope()->object
== obj2
);
3111 parent
= js_GetScopeChain(cx
, fp
);
3116 obj
= CloneFunctionObject(cx
, fun
, parent
);
3121 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
3122 END_CASE(JSOP_LAMBDA
)
3124 BEGIN_CASE(JSOP_LAMBDA_FC
)
3127 obj
= js_NewFlatClosure(cx
, fun
);
3131 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
3132 END_CASE(JSOP_LAMBDA_FC
)
3134 BEGIN_CASE(JSOP_LAMBDA_DBGFC
)
3137 obj
= js_NewDebuggableFlatClosure(cx
, fun
);
3141 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
3142 END_CASE(JSOP_LAMBDA_DBGFC
)
3144 BEGIN_CASE(JSOP_CALLEE
)
3145 PUSH_OPND(fp
->argv
[-2]);
3146 END_CASE(JSOP_CALLEE
)
3148 BEGIN_CASE(JSOP_GETTER
)
3149 BEGIN_CASE(JSOP_SETTER
)
3151 op2
= (JSOp
) *++regs
.pc
;
3153 case JSOP_INDEXBASE
:
3154 atoms
+= GET_INDEXBASE(regs
.pc
);
3155 regs
.pc
+= JSOP_INDEXBASE_LENGTH
- 1;
3156 goto do_getter_setter
;
3157 case JSOP_INDEXBASE1
:
3158 case JSOP_INDEXBASE2
:
3159 case JSOP_INDEXBASE3
:
3160 atoms
+= (op2
- JSOP_INDEXBASE1
+ 1) << 16;
3161 goto do_getter_setter
;
3166 id
= ATOM_TO_JSID(atom
);
3167 rval
= FETCH_OPND(-1);
3172 rval
= FETCH_OPND(-1);
3176 FETCH_OBJECT(cx
, i
- 1, lval
, obj
);
3180 JS_ASSERT(regs
.sp
- StackBase(fp
) >= 2);
3181 rval
= FETCH_OPND(-1);
3184 id
= ATOM_TO_JSID(atom
);
3188 JS_ASSERT(op2
== JSOP_INITELEM
);
3190 JS_ASSERT(regs
.sp
- StackBase(fp
) >= 3);
3191 rval
= FETCH_OPND(-1);
3195 lval
= FETCH_OPND(i
-1);
3196 JS_ASSERT(JSVAL_IS_OBJECT(lval
));
3197 obj
= JSVAL_TO_OBJECT(lval
);
3201 /* Ensure that id has a type suitable for use with obj. */
3203 FETCH_ELEMENT_ID(obj
, i
, id
);
3205 if (!js_IsCallable(rval
)) {
3206 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3207 JSMSG_BAD_GETTER_OR_SETTER
,
3215 * Getters and setters are just like watchpoints from an access control
3218 if (!obj
->checkAccess(cx
, id
, JSACC_WATCH
, &rtmp
, &attrs
))
3221 if (op
== JSOP_GETTER
) {
3222 getter
= CastAsPropertyOp(JSVAL_TO_OBJECT(rval
));
3223 setter
= JS_PropertyStub
;
3224 attrs
= JSPROP_GETTER
;
3226 getter
= JS_PropertyStub
;
3227 setter
= CastAsPropertyOp(JSVAL_TO_OBJECT(rval
));
3228 attrs
= JSPROP_SETTER
;
3230 attrs
|= JSPROP_ENUMERATE
| JSPROP_SHARED
;
3232 /* Check for a readonly or permanent property of the same name. */
3233 if (!js_CheckRedeclaration(cx
, obj
, id
, attrs
, NULL
, NULL
))
3236 if (!obj
->defineProperty(cx
, id
, JSVAL_VOID
, getter
, setter
, attrs
))
3240 if (js_CodeSpec
[op2
].ndefs
> js_CodeSpec
[op2
].nuses
) {
3241 JS_ASSERT(js_CodeSpec
[op2
].ndefs
== js_CodeSpec
[op2
].nuses
+ 1);
3242 STORE_OPND(-1, rval
);
3244 len
= js_CodeSpec
[op2
].length
;
3247 BEGIN_CASE(JSOP_HOLE
)
3248 PUSH_OPND(JSVAL_HOLE
);
3251 BEGIN_CASE(JSOP_NEWARRAY
)
3252 len
= GET_UINT16(regs
.pc
);
3253 cx
->fp
->assertValidStackDepth(len
);
3254 obj
= js_NewArrayObject(cx
, len
, regs
.sp
- len
, JS_TRUE
);
3258 STORE_OPND(-1, OBJECT_TO_JSVAL(obj
));
3259 END_CASE(JSOP_NEWARRAY
)
3261 BEGIN_CASE(JSOP_NEWINIT
)
3262 i
= GET_INT8(regs
.pc
);
3263 JS_ASSERT(i
== JSProto_Array
|| i
== JSProto_Object
);
3264 if (i
== JSProto_Array
) {
3265 obj
= js_NewArrayObject(cx
, 0, NULL
);
3269 obj
= NewObject(cx
, &js_ObjectClass
, NULL
, NULL
);
3273 if (regs
.pc
[JSOP_NEWINIT_LENGTH
] != JSOP_ENDINIT
) {
3274 JS_LOCK_OBJ(cx
, obj
);
3275 JSScope
*scope
= js_GetMutableScope(cx
, obj
);
3277 JS_UNLOCK_OBJ(cx
, obj
);
3282 * We cannot assume that js_GetMutableScope above creates a scope
3283 * owned by cx and skip JS_UNLOCK_SCOPE. A new object debugger
3284 * hook may add properties to the newly created object, suspend
3285 * the current request and share the object with other threads.
3287 JS_UNLOCK_SCOPE(cx
, scope
);
3291 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
3292 CHECK_INTERRUPT_HANDLER();
3293 END_CASE(JSOP_NEWINIT
)
3295 BEGIN_CASE(JSOP_ENDINIT
)
3296 /* Re-set the newborn root to the top of this object tree. */
3297 JS_ASSERT(regs
.sp
- StackBase(fp
) >= 1);
3298 lval
= FETCH_OPND(-1);
3299 JS_ASSERT(JSVAL_IS_OBJECT(lval
));
3300 cx
->weakRoots
.finalizableNewborns
[FINALIZE_OBJECT
] = JSVAL_TO_OBJECT(lval
);
3301 END_CASE(JSOP_ENDINIT
)
3303 BEGIN_CASE(JSOP_INITPROP
)
3304 BEGIN_CASE(JSOP_INITMETHOD
)
3306 /* Load the property's initial value into rval. */
3307 JS_ASSERT(regs
.sp
- StackBase(fp
) >= 2);
3308 rval
= FETCH_OPND(-1);
3310 /* Load the object being initialized into lval/obj. */
3311 lval
= FETCH_OPND(-2);
3312 obj
= JSVAL_TO_OBJECT(lval
);
3313 JS_ASSERT(obj
->isNative());
3314 JS_ASSERT(!obj
->getClass()->reserveSlots
);
3316 JSScope
*scope
= obj
->scope();
3317 PropertyCacheEntry
*entry
;
3320 * Probe the property cache.
3322 * We can not assume that the object created by JSOP_NEWINIT is still
3323 * single-threaded as the debugger can access it from other threads.
3326 * On a hit, if the cached sprop has a non-default setter, it must be
3327 * __proto__. If sprop->parent != scope->lastProperty(), there is a
3328 * repeated property name. The fast path does not handle these two cases.
3330 if (CX_OWNS_OBJECT_TITLE(cx
, obj
) &&
3331 JS_PROPERTY_CACHE(cx
).testForInit(rt
, regs
.pc
, obj
, scope
, &sprop
, &entry
) &&
3332 sprop
->hasDefaultSetter() &&
3333 sprop
->parent
== scope
->lastProperty())
3335 /* Fast path. Property cache hit. */
3337 JS_ASSERT(slot
== scope
->freeslot
);
3338 if (slot
< obj
->numSlots()) {
3341 if (!js_AllocSlot(cx
, obj
, &slot
))
3343 JS_ASSERT(slot
== sprop
->slot
);
3346 JS_ASSERT(!scope
->lastProperty() ||
3347 scope
->shape
== scope
->lastProperty()->shape
);
3349 JSScopeProperty
*sprop2
=
3350 scope
->addProperty(cx
, sprop
->id
, sprop
->getter(), sprop
->setter(), slot
,
3351 sprop
->attributes(), sprop
->getFlags(), sprop
->shortid
);
3353 js_FreeSlot(cx
, obj
, slot
);
3356 JS_ASSERT(sprop2
== sprop
);
3358 JS_ASSERT(!scope
->isSharedEmpty());
3359 scope
->extend(cx
, sprop
);
3363 * No method change check here because here we are adding a new
3364 * property, not updating an existing slot's value that might
3365 * contain a method of a branded scope.
3367 TRACE_2(SetPropHit
, entry
, sprop
);
3368 obj
->lockedSetSlot(slot
, rval
);
3370 PCMETER(JS_PROPERTY_CACHE(cx
).inipcmisses
++);
3372 /* Get the immediate property name into id. */
3374 id
= ATOM_TO_JSID(atom
);
3376 /* Set the property named by obj[id] to rval. */
3377 if (!js_CheckRedeclaration(cx
, obj
, id
, JSPROP_INITIALIZER
,
3382 uintN defineHow
= (op
== JSOP_INITMETHOD
)
3383 ? JSDNP_CACHE_RESULT
| JSDNP_SET_METHOD
3384 : JSDNP_CACHE_RESULT
;
3385 if (!(JS_UNLIKELY(atom
== cx
->runtime
->atomState
.protoAtom
)
3386 ? js_SetPropertyHelper(cx
, obj
, id
, defineHow
, &rval
)
3387 : js_DefineNativeProperty(cx
, obj
, id
, rval
, NULL
, NULL
,
3388 JSPROP_ENUMERATE
, 0, 0, NULL
,
3394 /* Common tail for property cache hit and miss cases. */
3397 END_CASE(JSOP_INITPROP
);
3399 BEGIN_CASE(JSOP_INITELEM
)
3400 /* Pop the element's value into rval. */
3401 JS_ASSERT(regs
.sp
- StackBase(fp
) >= 3);
3402 rval
= FETCH_OPND(-1);
3404 /* Find the object being initialized at top of stack. */
3405 lval
= FETCH_OPND(-3);
3406 JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval
));
3407 obj
= JSVAL_TO_OBJECT(lval
);
3409 /* Fetch id now that we have obj. */
3410 FETCH_ELEMENT_ID(obj
, -2, id
);
3413 * Check for property redeclaration strict warning (we may be in an object
3414 * initialiser, not an array initialiser).
3416 if (!js_CheckRedeclaration(cx
, obj
, id
, JSPROP_INITIALIZER
, NULL
, NULL
))
3420 * If rval is a hole, do not call JSObject::defineProperty. In this case,
3421 * obj must be an array, so if the current op is the last element
3422 * initialiser, set the array length to one greater than id.
3424 if (rval
== JSVAL_HOLE
) {
3425 JS_ASSERT(obj
->isArray());
3426 JS_ASSERT(JSID_IS_INT(id
));
3427 JS_ASSERT(jsuint(JSID_TO_INT(id
)) < JS_ARGS_LENGTH_MAX
);
3428 if (js_GetOpcode(cx
, script
, regs
.pc
+ JSOP_INITELEM_LENGTH
) == JSOP_ENDINIT
&&
3429 !js_SetLengthProperty(cx
, obj
, (jsuint
) (JSID_TO_INT(id
) + 1))) {
3433 if (!obj
->defineProperty(cx
, id
, rval
, NULL
, NULL
, JSPROP_ENUMERATE
))
3437 END_CASE(JSOP_INITELEM
)
3439 #if JS_HAS_SHARP_VARS
3441 BEGIN_CASE(JSOP_DEFSHARP
)
3442 slot
= GET_UINT16(regs
.pc
);
3443 JS_ASSERT(slot
+ 1 < fp
->script
->nfixed
);
3444 lval
= fp
->slots
[slot
];
3445 if (!JSVAL_IS_PRIMITIVE(lval
)) {
3446 obj
= JSVAL_TO_OBJECT(lval
);
3448 JS_ASSERT(JSVAL_IS_VOID(lval
));
3449 obj
= js_NewArrayObject(cx
, 0, NULL
);
3452 fp
->slots
[slot
] = OBJECT_TO_JSVAL(obj
);
3454 i
= (jsint
) GET_UINT16(regs
.pc
+ UINT16_LEN
);
3455 id
= INT_TO_JSID(i
);
3456 rval
= FETCH_OPND(-1);
3457 if (JSVAL_IS_PRIMITIVE(rval
)) {
3459 JS_snprintf(numBuf
, sizeof numBuf
, "%u", (unsigned) i
);
3460 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3461 JSMSG_BAD_SHARP_DEF
, numBuf
);
3464 if (!obj
->defineProperty(cx
, id
, rval
, NULL
, NULL
, JSPROP_ENUMERATE
))
3466 END_CASE(JSOP_DEFSHARP
)
3468 BEGIN_CASE(JSOP_USESHARP
)
3469 slot
= GET_UINT16(regs
.pc
);
3470 JS_ASSERT(slot
+ 1 < fp
->script
->nfixed
);
3471 lval
= fp
->slots
[slot
];
3472 i
= (jsint
) GET_UINT16(regs
.pc
+ UINT16_LEN
);
3473 if (JSVAL_IS_VOID(lval
)) {
3476 obj
= JSVAL_TO_OBJECT(fp
->slots
[slot
]);
3477 id
= INT_TO_JSID(i
);
3478 if (!obj
->getProperty(cx
, id
, &rval
))
3481 if (!JSVAL_IS_OBJECT(rval
)) {
3484 JS_snprintf(numBuf
, sizeof numBuf
, "%u", (unsigned) i
);
3485 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3486 JSMSG_BAD_SHARP_USE
, numBuf
);
3490 END_CASE(JSOP_USESHARP
)
3492 BEGIN_CASE(JSOP_SHARPINIT
)
3493 slot
= GET_UINT16(regs
.pc
);
3494 JS_ASSERT(slot
+ 1 < fp
->script
->nfixed
);
3495 vp
= &fp
->slots
[slot
];
3499 * We peek ahead safely here because empty initialisers get zero
3500 * JSOP_SHARPINIT ops, and non-empty ones get two: the first comes
3501 * immediately after JSOP_NEWINIT followed by one or more property
3502 * initialisers; and the second comes directly before JSOP_ENDINIT.
3504 if (regs
.pc
[JSOP_SHARPINIT_LENGTH
] != JSOP_ENDINIT
) {
3505 rval
= JSVAL_IS_VOID(rval
) ? JSVAL_ONE
: rval
+ 2;
3507 JS_ASSERT(JSVAL_IS_INT(rval
));
3509 if (rval
== JSVAL_ZERO
)
3513 END_CASE(JSOP_SHARPINIT
)
3515 #endif /* JS_HAS_SHARP_VARS */
3517 BEGIN_CASE(JSOP_GOSUB
)
3519 i
= (regs
.pc
- script
->main
) + JSOP_GOSUB_LENGTH
;
3520 PUSH(INT_TO_JSVAL(i
));
3521 len
= GET_JUMP_OFFSET(regs
.pc
);
3524 BEGIN_CASE(JSOP_GOSUBX
)
3526 i
= (regs
.pc
- script
->main
) + JSOP_GOSUBX_LENGTH
;
3527 len
= GET_JUMPX_OFFSET(regs
.pc
);
3528 PUSH(INT_TO_JSVAL(i
));
3531 BEGIN_CASE(JSOP_RETSUB
)
3532 /* Pop [exception or hole, retsub pc-index]. */
3535 JS_ASSERT(JSVAL_IS_BOOLEAN(lval
));
3536 if (JSVAL_TO_BOOLEAN(lval
)) {
3538 * Exception was pending during finally, throw it *before* we adjust
3539 * pc, because pc indexes into script->trynotes. This turns out not to
3540 * be necessary, but it seems clearer. And it points out a FIXME:
3541 * 350509, due to Igor Bukanov.
3543 cx
->throwing
= JS_TRUE
;
3544 cx
->exception
= rval
;
3547 JS_ASSERT(JSVAL_IS_INT(rval
));
3548 len
= JSVAL_TO_INT(rval
);
3549 regs
.pc
= script
->main
;
3552 BEGIN_CASE(JSOP_EXCEPTION
)
3553 JS_ASSERT(cx
->throwing
);
3554 PUSH(cx
->exception
);
3555 cx
->throwing
= JS_FALSE
;
3557 END_CASE(JSOP_EXCEPTION
)
3559 BEGIN_CASE(JSOP_FINALLY
)
3561 END_CASE(JSOP_FINALLY
)
3563 BEGIN_CASE(JSOP_THROWING
)
3564 JS_ASSERT(!cx
->throwing
);
3565 cx
->throwing
= JS_TRUE
;
3566 cx
->exception
= POP_OPND();
3567 END_CASE(JSOP_THROWING
)
3569 BEGIN_CASE(JSOP_THROW
)
3570 JS_ASSERT(!cx
->throwing
);
3572 cx
->throwing
= JS_TRUE
;
3573 cx
->exception
= POP_OPND();
3574 /* let the code at error try to catch the exception. */
3577 BEGIN_CASE(JSOP_SETLOCALPOP
)
3579 * The stack must have a block with at least one local slot below the
3582 JS_ASSERT((size_t) (regs
.sp
- StackBase(fp
)) >= 2);
3583 slot
= GET_UINT16(regs
.pc
);
3584 JS_ASSERT(slot
+ 1 < script
->nslots
);
3585 fp
->slots
[slot
] = POP_OPND();
3586 END_CASE(JSOP_SETLOCALPOP
)
3588 BEGIN_CASE(JSOP_IFPRIMTOP
)
3590 * If the top of stack is of primitive type, jump to our target. Otherwise
3591 * advance to the next opcode.
3593 JS_ASSERT(regs
.sp
> StackBase(fp
));
3594 rval
= FETCH_OPND(-1);
3595 if (JSVAL_IS_PRIMITIVE(rval
)) {
3596 len
= GET_JUMP_OFFSET(regs
.pc
);
3599 END_CASE(JSOP_IFPRIMTOP
)
3601 BEGIN_CASE(JSOP_PRIMTOP
)
3602 JS_ASSERT(regs
.sp
> StackBase(fp
));
3603 lval
= FETCH_OPND(-1);
3604 i
= GET_INT8(regs
.pc
);
3605 if (!JSVAL_IS_PRIMITIVE(lval
)) {
3606 lval
= FETCH_OPND(-2);
3607 js_ReportValueError2(cx
, JSMSG_CANT_CONVERT_TO
, -2, lval
, NULL
,
3608 (i
== JSTYPE_VOID
) ? "primitive type" : JS_TYPE_STR(i
));
3611 END_CASE(JSOP_PRIMTOP
)
3613 BEGIN_CASE(JSOP_OBJTOP
)
3614 lval
= FETCH_OPND(-1);
3615 if (JSVAL_IS_PRIMITIVE(lval
)) {
3616 js_ReportValueError(cx
, GET_UINT16(regs
.pc
), -1, lval
, NULL
);
3619 END_CASE(JSOP_OBJTOP
)
3621 BEGIN_CASE(JSOP_INSTANCEOF
)
3622 rval
= FETCH_OPND(-1);
3623 if (JSVAL_IS_PRIMITIVE(rval
) ||
3624 !(obj
= JSVAL_TO_OBJECT(rval
))->map
->ops
->hasInstance
) {
3625 js_ReportValueError(cx
, JSMSG_BAD_INSTANCEOF_RHS
,
3629 lval
= FETCH_OPND(-2);
3631 if (!obj
->map
->ops
->hasInstance(cx
, obj
, lval
, &cond
))
3634 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond
));
3635 END_CASE(JSOP_INSTANCEOF
)
3637 #if JS_HAS_DEBUGGER_KEYWORD
3638 BEGIN_CASE(JSOP_DEBUGGER
)
3640 JSDebuggerHandler handler
= cx
->debugHooks
->debuggerHandler
;
3642 switch (handler(cx
, script
, regs
.pc
, &rval
, cx
->debugHooks
->debuggerHandlerData
)) {
3645 case JSTRAP_CONTINUE
:
3652 cx
->throwing
= JS_TRUE
;
3653 cx
->exception
= rval
;
3657 CHECK_INTERRUPT_HANDLER();
3660 END_CASE(JSOP_DEBUGGER
)
3661 #endif /* JS_HAS_DEBUGGER_KEYWORD */
3663 #if JS_HAS_XML_SUPPORT
3664 BEGIN_CASE(JSOP_DEFXMLNS
)
3666 if (!js_SetDefaultXMLNamespace(cx
, rval
))
3668 END_CASE(JSOP_DEFXMLNS
)
3670 BEGIN_CASE(JSOP_ANYNAME
)
3671 if (!js_GetAnyName(cx
, &rval
))
3674 END_CASE(JSOP_ANYNAME
)
3676 BEGIN_CASE(JSOP_QNAMEPART
)
3678 PUSH_OPND(ATOM_KEY(atom
));
3679 END_CASE(JSOP_QNAMEPART
)
3681 BEGIN_CASE(JSOP_QNAMECONST
)
3683 rval
= ATOM_KEY(atom
);
3684 lval
= FETCH_OPND(-1);
3685 obj
= js_ConstructXMLQNameObject(cx
, lval
, rval
);
3688 STORE_OPND(-1, OBJECT_TO_JSVAL(obj
));
3689 END_CASE(JSOP_QNAMECONST
)
3691 BEGIN_CASE(JSOP_QNAME
)
3692 rval
= FETCH_OPND(-1);
3693 lval
= FETCH_OPND(-2);
3694 obj
= js_ConstructXMLQNameObject(cx
, lval
, rval
);
3698 STORE_OPND(-1, OBJECT_TO_JSVAL(obj
));
3699 END_CASE(JSOP_QNAME
)
3701 BEGIN_CASE(JSOP_TOATTRNAME
)
3702 rval
= FETCH_OPND(-1);
3703 if (!js_ToAttributeName(cx
, &rval
))
3705 STORE_OPND(-1, rval
);
3706 END_CASE(JSOP_TOATTRNAME
)
3708 BEGIN_CASE(JSOP_TOATTRVAL
)
3709 rval
= FETCH_OPND(-1);
3710 JS_ASSERT(JSVAL_IS_STRING(rval
));
3711 str
= js_EscapeAttributeValue(cx
, JSVAL_TO_STRING(rval
), JS_FALSE
);
3714 STORE_OPND(-1, STRING_TO_JSVAL(str
));
3715 END_CASE(JSOP_TOATTRVAL
)
3717 BEGIN_CASE(JSOP_ADDATTRNAME
)
3718 BEGIN_CASE(JSOP_ADDATTRVAL
)
3719 rval
= FETCH_OPND(-1);
3720 lval
= FETCH_OPND(-2);
3721 str
= JSVAL_TO_STRING(lval
);
3722 str2
= JSVAL_TO_STRING(rval
);
3723 str
= js_AddAttributePart(cx
, op
== JSOP_ADDATTRNAME
, str
, str2
);
3727 STORE_OPND(-1, STRING_TO_JSVAL(str
));
3728 END_CASE(JSOP_ADDATTRNAME
)
3730 BEGIN_CASE(JSOP_BINDXMLNAME
)
3731 lval
= FETCH_OPND(-1);
3732 if (!js_FindXMLProperty(cx
, lval
, &obj
, &id
))
3734 STORE_OPND(-1, OBJECT_TO_JSVAL(obj
));
3735 PUSH_OPND(ID_TO_VALUE(id
));
3736 END_CASE(JSOP_BINDXMLNAME
)
3738 BEGIN_CASE(JSOP_SETXMLNAME
)
3739 obj
= JSVAL_TO_OBJECT(FETCH_OPND(-3));
3740 rval
= FETCH_OPND(-1);
3741 FETCH_ELEMENT_ID(obj
, -2, id
);
3742 if (!obj
->setProperty(cx
, id
, &rval
))
3744 rval
= FETCH_OPND(-1);
3746 STORE_OPND(-1, rval
);
3747 END_CASE(JSOP_SETXMLNAME
)
3749 BEGIN_CASE(JSOP_CALLXMLNAME
)
3750 BEGIN_CASE(JSOP_XMLNAME
)
3751 lval
= FETCH_OPND(-1);
3752 if (!js_FindXMLProperty(cx
, lval
, &obj
, &id
))
3754 if (!obj
->getProperty(cx
, id
, &rval
))
3756 STORE_OPND(-1, rval
);
3757 if (op
== JSOP_CALLXMLNAME
)
3758 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
3759 END_CASE(JSOP_XMLNAME
)
3761 BEGIN_CASE(JSOP_DESCENDANTS
)
3762 BEGIN_CASE(JSOP_DELDESC
)
3763 FETCH_OBJECT(cx
, -2, lval
, obj
);
3764 rval
= FETCH_OPND(-1);
3765 if (!js_GetXMLDescendants(cx
, obj
, rval
, &rval
))
3768 if (op
== JSOP_DELDESC
) {
3769 regs
.sp
[-1] = rval
; /* set local root */
3770 if (!js_DeleteXMLListElements(cx
, JSVAL_TO_OBJECT(rval
)))
3772 rval
= JSVAL_TRUE
; /* always succeed */
3776 STORE_OPND(-1, rval
);
3777 END_CASE(JSOP_DESCENDANTS
)
3779 BEGIN_CASE(JSOP_FILTER
)
3781 * We push the hole value before jumping to [enditer] so we can detect the
3782 * first iteration and direct js_StepXMLListFilter to initialize filter's
3785 PUSH_OPND(JSVAL_HOLE
);
3786 len
= GET_JUMP_OFFSET(regs
.pc
);
3790 BEGIN_CASE(JSOP_ENDFILTER
)
3791 cond
= (regs
.sp
[-1] != JSVAL_HOLE
);
3793 /* Exit the "with" block left from the previous iteration. */
3796 if (!js_StepXMLListFilter(cx
, cond
))
3798 if (regs
.sp
[-1] != JSVAL_NULL
) {
3800 * Decrease sp after EnterWith returns as we use sp[-1] there to root
3803 JS_ASSERT(VALUE_IS_XML(cx
, regs
.sp
[-1]));
3804 if (!js_EnterWith(cx
, -2))
3807 len
= GET_JUMP_OFFSET(regs
.pc
);
3812 END_CASE(JSOP_ENDFILTER
);
3814 BEGIN_CASE(JSOP_TOXML
)
3815 rval
= FETCH_OPND(-1);
3816 obj
= js_ValueToXMLObject(cx
, rval
);
3819 STORE_OPND(-1, OBJECT_TO_JSVAL(obj
));
3820 END_CASE(JSOP_TOXML
)
3822 BEGIN_CASE(JSOP_TOXMLLIST
)
3823 rval
= FETCH_OPND(-1);
3824 obj
= js_ValueToXMLListObject(cx
, rval
);
3827 STORE_OPND(-1, OBJECT_TO_JSVAL(obj
));
3828 END_CASE(JSOP_TOXMLLIST
)
3830 BEGIN_CASE(JSOP_XMLTAGEXPR
)
3831 rval
= FETCH_OPND(-1);
3832 str
= js_ValueToString(cx
, rval
);
3835 STORE_OPND(-1, STRING_TO_JSVAL(str
));
3836 END_CASE(JSOP_XMLTAGEXPR
)
3838 BEGIN_CASE(JSOP_XMLELTEXPR
)
3839 rval
= FETCH_OPND(-1);
3840 if (VALUE_IS_XML(cx
, rval
)) {
3841 str
= js_ValueToXMLString(cx
, rval
);
3843 str
= js_ValueToString(cx
, rval
);
3845 str
= js_EscapeElementValue(cx
, str
);
3849 STORE_OPND(-1, STRING_TO_JSVAL(str
));
3850 END_CASE(JSOP_XMLELTEXPR
)
3852 BEGIN_CASE(JSOP_XMLOBJECT
)
3854 obj
= js_CloneXMLObject(cx
, obj
);
3857 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
3858 END_CASE(JSOP_XMLOBJECT
)
3860 BEGIN_CASE(JSOP_XMLCDATA
)
3862 str
= ATOM_TO_STRING(atom
);
3863 obj
= js_NewXMLSpecialObject(cx
, JSXML_CLASS_TEXT
, NULL
, str
);
3866 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
3867 END_CASE(JSOP_XMLCDATA
)
3869 BEGIN_CASE(JSOP_XMLCOMMENT
)
3871 str
= ATOM_TO_STRING(atom
);
3872 obj
= js_NewXMLSpecialObject(cx
, JSXML_CLASS_COMMENT
, NULL
, str
);
3875 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
3876 END_CASE(JSOP_XMLCOMMENT
)
3878 BEGIN_CASE(JSOP_XMLPI
)
3880 str
= ATOM_TO_STRING(atom
);
3881 rval
= FETCH_OPND(-1);
3882 str2
= JSVAL_TO_STRING(rval
);
3883 obj
= js_NewXMLSpecialObject(cx
, JSXML_CLASS_PROCESSING_INSTRUCTION
, str
, str2
);
3886 STORE_OPND(-1, OBJECT_TO_JSVAL(obj
));
3887 END_CASE(JSOP_XMLPI
)
3889 BEGIN_CASE(JSOP_GETFUNNS
)
3890 if (!js_GetFunctionNamespace(cx
, &rval
))
3893 END_CASE(JSOP_GETFUNNS
)
3894 #endif /* JS_HAS_XML_SUPPORT */
3896 BEGIN_CASE(JSOP_ENTERBLOCK
)
3898 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj
));
3899 JS_ASSERT(StackBase(fp
) + OBJ_BLOCK_DEPTH(cx
, obj
) == regs
.sp
);
3900 vp
= regs
.sp
+ OBJ_BLOCK_COUNT(cx
, obj
);
3901 JS_ASSERT(regs
.sp
< vp
);
3902 JS_ASSERT(vp
<= fp
->slots
+ script
->nslots
);
3903 while (regs
.sp
< vp
) {
3904 STORE_OPND(0, JSVAL_VOID
);
3909 JS_ASSERT(fp
->blockChain
== obj
->getParent());
3912 * The young end of fp->scopeChain may omit blocks if we haven't closed
3913 * over them, but if there are any closure blocks on fp->scopeChain, they'd
3914 * better be (clones of) ancestors of the block we're entering now;
3915 * anything else we should have popped off fp->scopeChain when we left its
3918 obj2
= fp
->scopeChain
;
3919 while ((clasp
= obj2
->getClass()) == &js_WithClass
)
3920 obj2
= obj2
->getParent();
3921 if (clasp
== &js_BlockClass
&&
3922 obj2
->getPrivate() == fp
) {
3923 JSObject
*youngestProto
= obj2
->getProto();
3924 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(youngestProto
));
3926 while ((parent
= parent
->getParent()) != youngestProto
)
3931 fp
->blockChain
= obj
;
3932 END_CASE(JSOP_ENTERBLOCK
)
3934 BEGIN_CASE(JSOP_LEAVEBLOCKEXPR
)
3935 BEGIN_CASE(JSOP_LEAVEBLOCK
)
3938 JS_ASSERT(fp
->blockChain
->getClass() == &js_BlockClass
);
3939 uintN blockDepth
= OBJ_BLOCK_DEPTH(cx
, fp
->blockChain
);
3941 JS_ASSERT(blockDepth
<= StackDepth(script
));
3944 * If we're about to leave the dynamic scope of a block that has been
3945 * cloned onto fp->scopeChain, clear its private data, move its locals from
3946 * the stack into the clone, and pop it off the chain.
3948 obj
= fp
->scopeChain
;
3949 if (obj
->getProto() == fp
->blockChain
) {
3950 JS_ASSERT(obj
->getClass() == &js_BlockClass
);
3951 if (!js_PutBlockObject(cx
, JS_TRUE
))
3955 /* Pop the block chain, too. */
3956 fp
->blockChain
= fp
->blockChain
->getParent();
3958 /* Move the result of the expression to the new topmost stack slot. */
3959 if (op
== JSOP_LEAVEBLOCKEXPR
)
3960 rval
= FETCH_OPND(-1);
3961 regs
.sp
-= GET_UINT16(regs
.pc
);
3962 if (op
== JSOP_LEAVEBLOCKEXPR
) {
3963 JS_ASSERT(StackBase(fp
) + blockDepth
== regs
.sp
- 1);
3964 STORE_OPND(-1, rval
);
3966 JS_ASSERT(StackBase(fp
) + blockDepth
== regs
.sp
);
3969 END_CASE(JSOP_LEAVEBLOCK
)
3971 #if JS_HAS_GENERATORS
3972 BEGIN_CASE(JSOP_GENERATOR
)
3973 ASSERT_NOT_THROWING(cx
);
3974 regs
.pc
+= JSOP_GENERATOR_LENGTH
;
3975 obj
= js_NewGenerator(cx
);
3978 JS_ASSERT(!fp
->callobj
&& !fp
->argsobj
);
3979 fp
->rval
= OBJECT_TO_JSVAL(obj
);
3981 if (inlineCallCount
!= 0)
3985 BEGIN_CASE(JSOP_YIELD
)
3986 ASSERT_NOT_THROWING(cx
);
3987 if (FRAME_TO_GENERATOR(fp
)->state
== JSGEN_CLOSING
) {
3988 js_ReportValueError(cx
, JSMSG_BAD_GENERATOR_YIELD
,
3989 JSDVG_SEARCH_STACK
, fp
->argv
[-2], NULL
);
3992 fp
->rval
= FETCH_OPND(-1);
3993 fp
->flags
|= JSFRAME_YIELDING
;
3994 regs
.pc
+= JSOP_YIELD_LENGTH
;
3998 BEGIN_CASE(JSOP_ARRAYPUSH
)
3999 slot
= GET_UINT16(regs
.pc
);
4000 JS_ASSERT(script
->nfixed
<= slot
);
4001 JS_ASSERT(slot
< script
->nslots
);
4002 lval
= fp
->slots
[slot
];
4003 obj
= JSVAL_TO_OBJECT(lval
);
4004 rval
= FETCH_OPND(-1);
4005 if (!js_ArrayCompPush(cx
, obj
, rval
))
4008 END_CASE(JSOP_ARRAYPUSH
)
4009 #endif /* JS_HAS_GENERATORS */
4011 #if JS_THREADED_INTERP
4013 L_JSOP_BACKPATCH_POP
:
4015 # if !JS_HAS_GENERATORS
4021 # if !JS_HAS_SHARP_VARS
4027 # if !JS_HAS_DESTRUCTURING
4028 L_JSOP_ENUMCONSTELEM
:
4031 # if !JS_HAS_XML_SUPPORT
4033 L_JSOP_STARTXMLEXPR
:
4064 #endif /* !JS_THREADED_INTERP */