Removed JSStackFrame::callee (bug 512029, r=brendan).
[mozilla-central.git] / js / src / jsops.cpp
blob95c0a0b058bc1d8ac2e2edbb2ad025f5aa89786b
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
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
25 * Contributor(s):
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 /* This file needs to be included in possibly multiple places. */
43 #if JS_THREADED_INTERP
44 interrupt:
45 #else /* !JS_THREADED_INTERP */
46 case -1:
47 JS_ASSERT(switchMask == -1);
48 #endif /* !JS_THREADED_INTERP */
50 bool moreInterrupts = false;
51 JSTrapHandler handler = cx->debugHooks->interruptHandler;
52 if (handler) {
53 #ifdef JS_TRACER
54 if (TRACE_RECORDER(cx))
55 js_AbortRecording(cx, "interrupt handler");
56 #endif
57 switch (handler(cx, script, regs.pc, &rval,
58 cx->debugHooks->interruptHandlerData)) {
59 case JSTRAP_ERROR:
60 goto error;
61 case JSTRAP_CONTINUE:
62 break;
63 case JSTRAP_RETURN:
64 fp->rval = rval;
65 ok = JS_TRUE;
66 goto forced_return;
67 case JSTRAP_THROW:
68 cx->throwing = JS_TRUE;
69 cx->exception = rval;
70 goto error;
71 default:;
73 moreInterrupts = true;
76 #ifdef JS_TRACER
77 TraceRecorder* tr = TRACE_RECORDER(cx);
78 if (tr) {
79 JSRecordingStatus status = TraceRecorder::monitorRecording(cx, tr, op);
80 switch (status) {
81 case JSRS_CONTINUE:
82 moreInterrupts = true;
83 break;
84 case JSRS_IMACRO:
85 atoms = COMMON_ATOMS_START(&rt->atomState);
86 op = JSOp(*regs.pc);
87 DO_OP(); /* keep interrupting for op. */
88 break;
89 case JSRS_ERROR:
90 // The code at 'error:' aborts the recording.
91 goto error;
92 case JSRS_STOP:
93 break;
94 default:
95 JS_NOT_REACHED("Bad recording status");
98 #endif /* !JS_TRACER */
100 #if JS_THREADED_INTERP
101 #ifdef MOZ_TRACEVIS
102 if (!moreInterrupts)
103 js_ExitTraceVisState(cx, R_ABORT);
104 #endif
105 jumpTable = moreInterrupts ? interruptJumpTable : normalJumpTable;
106 JS_EXTENSION_(goto *normalJumpTable[op]);
107 #else
108 switchMask = moreInterrupts ? -1 : 0;
109 switchOp = intN(op);
110 goto do_switch;
111 #endif
114 /* No-ops for ease of decompilation. */
115 ADD_EMPTY_CASE(JSOP_NOP)
116 ADD_EMPTY_CASE(JSOP_CONDSWITCH)
117 ADD_EMPTY_CASE(JSOP_TRY)
118 #if JS_HAS_XML_SUPPORT
119 ADD_EMPTY_CASE(JSOP_STARTXML)
120 ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
121 #endif
122 END_EMPTY_CASES
124 /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
125 BEGIN_CASE(JSOP_LINENO)
126 END_CASE(JSOP_LINENO)
128 BEGIN_CASE(JSOP_PUSH)
129 PUSH_OPND(JSVAL_VOID);
130 END_CASE(JSOP_PUSH)
132 BEGIN_CASE(JSOP_POP)
133 regs.sp--;
134 END_CASE(JSOP_POP)
136 BEGIN_CASE(JSOP_POPN)
137 regs.sp -= GET_UINT16(regs.pc);
138 #ifdef DEBUG
139 JS_ASSERT(StackBase(fp) <= regs.sp);
140 obj = fp->blockChain;
141 JS_ASSERT_IF(obj,
142 OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
143 <= (size_t) (regs.sp - StackBase(fp)));
144 for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
145 clasp = OBJ_GET_CLASS(cx, obj);
146 if (clasp != &js_BlockClass && clasp != &js_WithClass)
147 continue;
148 if (obj->getAssignedPrivate() != fp)
149 break;
150 JS_ASSERT(StackBase(fp) + OBJ_BLOCK_DEPTH(cx, obj)
151 + ((clasp == &js_BlockClass)
152 ? OBJ_BLOCK_COUNT(cx, obj)
153 : 1)
154 <= regs.sp);
156 #endif
157 END_CASE(JSOP_POPN)
159 BEGIN_CASE(JSOP_SETRVAL)
160 BEGIN_CASE(JSOP_POPV)
161 ASSERT_NOT_THROWING(cx);
162 fp->rval = POP_OPND();
163 END_CASE(JSOP_POPV)
165 BEGIN_CASE(JSOP_ENTERWITH)
166 if (!js_EnterWith(cx, -1))
167 goto error;
170 * We must ensure that different "with" blocks have different
171 * stack depth associated with them. This allows the try handler
172 * search to properly recover the scope chain. Thus we must keep
173 * the stack at least at the current level.
175 * We set sp[-1] to the current "with" object to help asserting
176 * the enter/leave balance in [leavewith].
178 regs.sp[-1] = OBJECT_TO_JSVAL(fp->scopeChain);
179 END_CASE(JSOP_ENTERWITH)
181 BEGIN_CASE(JSOP_LEAVEWITH)
182 JS_ASSERT(regs.sp[-1] == OBJECT_TO_JSVAL(fp->scopeChain));
183 regs.sp--;
184 js_LeaveWith(cx);
185 END_CASE(JSOP_LEAVEWITH)
187 BEGIN_CASE(JSOP_RETURN)
188 fp->rval = POP_OPND();
189 /* FALL THROUGH */
191 BEGIN_CASE(JSOP_RETRVAL) /* fp->rval already set */
192 BEGIN_CASE(JSOP_STOP)
194 * When the inlined frame exits with an exception or an error, ok
195 * will be false after the inline_return label.
197 ASSERT_NOT_THROWING(cx);
198 CHECK_BRANCH();
200 if (fp->imacpc) {
202 * If we are at the end of an imacro, return to its caller in
203 * the current frame.
205 JS_ASSERT(op == JSOP_STOP);
207 end_imacro:
208 JS_ASSERT((uintN)(regs.sp - fp->slots) <= script->nslots);
209 regs.pc = fp->imacpc + js_CodeSpec[*fp->imacpc].length;
210 fp->imacpc = NULL;
211 atoms = script->atomMap.vector;
212 op = JSOp(*regs.pc);
213 DO_OP();
216 JS_ASSERT(regs.sp == StackBase(fp));
217 if ((fp->flags & JSFRAME_CONSTRUCTING) &&
218 JSVAL_IS_PRIMITIVE(fp->rval)) {
219 if (!fp->fun) {
220 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
221 JSMSG_BAD_NEW_RESULT,
222 js_ValueToPrintableString(cx, rval));
223 goto error;
225 fp->rval = OBJECT_TO_JSVAL(fp->thisp);
227 ok = JS_TRUE;
228 if (inlineCallCount)
229 inline_return:
231 JSInlineFrame *ifp = (JSInlineFrame *) fp;
232 void *hookData = ifp->hookData;
234 JS_ASSERT(!fp->blockChain);
235 JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
237 if (script->staticLevel < JS_DISPLAY_SIZE)
238 cx->display[script->staticLevel] = fp->displaySave;
240 if (hookData) {
241 JSInterpreterHook hook;
242 JSBool status;
244 hook = cx->debugHooks->callHook;
245 if (hook) {
247 * Do not pass &ok directly as exposing the address
248 * inhibits optimizations and uninitialised warnings.
250 status = ok;
251 hook(cx, fp, JS_FALSE, &status, hookData);
252 ok = status;
253 CHECK_INTERRUPT_HANDLER();
258 * If fp has a call object, sync values and clear the back-
259 * pointer. This can happen for a lightweight function if it
260 * calls eval unexpectedly (in a way that is hidden from the
261 * compiler). See bug 325540.
263 fp->putActivationObjects(cx);
265 #ifdef INCLUDE_MOZILLA_DTRACE
266 /* DTrace function return, inlines */
267 if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
268 jsdtrace_function_rval(cx, fp, fp->fun, &fp->rval);
269 if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
270 jsdtrace_function_return(cx, fp, fp->fun);
271 #endif
273 /* Restore context version only if callee hasn't set version. */
274 if (JS_LIKELY(cx->version == currentVersion)) {
275 currentVersion = ifp->callerVersion;
276 if (currentVersion != cx->version)
277 js_SetVersion(cx, currentVersion);
281 * If inline-constructing, replace primitive rval with the new
282 * object passed in via |this|, and instrument this constructor
283 * invocation
285 if (fp->flags & JSFRAME_CONSTRUCTING) {
286 if (JSVAL_IS_PRIMITIVE(fp->rval))
287 fp->rval = OBJECT_TO_JSVAL(fp->thisp);
288 JS_RUNTIME_METER(cx->runtime, constructs);
291 /* Restore caller's registers. */
292 regs = ifp->callerRegs;
294 /* Store the return value in the caller's operand frame. */
295 regs.sp -= 1 + (size_t) ifp->frame.argc;
296 regs.sp[-1] = fp->rval;
298 /* Restore cx->fp and release the inline frame's space. */
299 cx->fp = fp = fp->down;
300 JS_ASSERT(fp->regs == &ifp->callerRegs);
301 fp->regs = &regs;
302 JS_ARENA_RELEASE(&cx->stackPool, ifp->mark);
304 /* Restore the calling script's interpreter registers. */
305 script = fp->script;
306 atoms = FrameAtomBase(cx, fp);
308 /* Resume execution in the calling frame. */
309 inlineCallCount--;
310 if (JS_LIKELY(ok)) {
311 TRACE_0(LeaveFrame);
312 JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, script, regs.pc)].length
313 == JSOP_CALL_LENGTH);
314 len = JSOP_CALL_LENGTH;
315 DO_NEXT_OP(len);
317 goto error;
319 goto exit;
321 BEGIN_CASE(JSOP_DEFAULT)
322 (void) POP();
323 /* FALL THROUGH */
324 BEGIN_CASE(JSOP_GOTO)
325 len = GET_JUMP_OFFSET(regs.pc);
326 BRANCH(len);
327 END_CASE(JSOP_GOTO)
329 BEGIN_CASE(JSOP_IFEQ)
330 POP_BOOLEAN(cx, rval, cond);
331 if (cond == JS_FALSE) {
332 len = GET_JUMP_OFFSET(regs.pc);
333 BRANCH(len);
335 END_CASE(JSOP_IFEQ)
337 BEGIN_CASE(JSOP_IFNE)
338 POP_BOOLEAN(cx, rval, cond);
339 if (cond != JS_FALSE) {
340 len = GET_JUMP_OFFSET(regs.pc);
341 BRANCH(len);
343 END_CASE(JSOP_IFNE)
345 BEGIN_CASE(JSOP_OR)
346 POP_BOOLEAN(cx, rval, cond);
347 if (cond == JS_TRUE) {
348 len = GET_JUMP_OFFSET(regs.pc);
349 PUSH_OPND(rval);
350 DO_NEXT_OP(len);
352 END_CASE(JSOP_OR)
354 BEGIN_CASE(JSOP_AND)
355 POP_BOOLEAN(cx, rval, cond);
356 if (cond == JS_FALSE) {
357 len = GET_JUMP_OFFSET(regs.pc);
358 PUSH_OPND(rval);
359 DO_NEXT_OP(len);
361 END_CASE(JSOP_AND)
363 BEGIN_CASE(JSOP_DEFAULTX)
364 (void) POP();
365 /* FALL THROUGH */
366 BEGIN_CASE(JSOP_GOTOX)
367 len = GET_JUMPX_OFFSET(regs.pc);
368 BRANCH(len);
369 END_CASE(JSOP_GOTOX);
371 BEGIN_CASE(JSOP_IFEQX)
372 POP_BOOLEAN(cx, rval, cond);
373 if (cond == JS_FALSE) {
374 len = GET_JUMPX_OFFSET(regs.pc);
375 BRANCH(len);
377 END_CASE(JSOP_IFEQX)
379 BEGIN_CASE(JSOP_IFNEX)
380 POP_BOOLEAN(cx, rval, cond);
381 if (cond != JS_FALSE) {
382 len = GET_JUMPX_OFFSET(regs.pc);
383 BRANCH(len);
385 END_CASE(JSOP_IFNEX)
387 BEGIN_CASE(JSOP_ORX)
388 POP_BOOLEAN(cx, rval, cond);
389 if (cond == JS_TRUE) {
390 len = GET_JUMPX_OFFSET(regs.pc);
391 PUSH_OPND(rval);
392 DO_NEXT_OP(len);
394 END_CASE(JSOP_ORX)
396 BEGIN_CASE(JSOP_ANDX)
397 POP_BOOLEAN(cx, rval, cond);
398 if (cond == JS_FALSE) {
399 len = GET_JUMPX_OFFSET(regs.pc);
400 PUSH_OPND(rval);
401 DO_NEXT_OP(len);
403 END_CASE(JSOP_ANDX)
406 * If the index value at sp[n] is not an int that fits in a jsval, it could
407 * be an object (an XML QName, AttributeName, or AnyName), but only if we are
408 * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a
409 * string atom id.
411 #define FETCH_ELEMENT_ID(obj, n, id) \
412 JS_BEGIN_MACRO \
413 jsval idval_ = FETCH_OPND(n); \
414 if (JSVAL_IS_INT(idval_)) { \
415 id = INT_JSVAL_TO_JSID(idval_); \
416 } else { \
417 if (!js_InternNonIntElementId(cx, obj, idval_, &id)) \
418 goto error; \
419 regs.sp[n] = ID_TO_VALUE(id); \
421 JS_END_MACRO
423 #define TRY_BRANCH_AFTER_COND(cond,spdec) \
424 JS_BEGIN_MACRO \
425 uintN diff_; \
426 JS_ASSERT(js_CodeSpec[op].length == 1); \
427 diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ; \
428 if (diff_ <= 1) { \
429 regs.sp -= spdec; \
430 if (cond == (diff_ != 0)) { \
431 ++regs.pc; \
432 len = GET_JUMP_OFFSET(regs.pc); \
433 BRANCH(len); \
435 len = 1 + JSOP_IFEQ_LENGTH; \
436 DO_NEXT_OP(len); \
438 JS_END_MACRO
440 BEGIN_CASE(JSOP_IN)
441 rval = FETCH_OPND(-1);
442 if (JSVAL_IS_PRIMITIVE(rval)) {
443 js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rval, NULL);
444 goto error;
446 obj = JSVAL_TO_OBJECT(rval);
447 FETCH_ELEMENT_ID(obj, -2, id);
448 if (!obj->lookupProperty(cx, id, &obj2, &prop))
449 goto error;
450 cond = prop != NULL;
451 if (prop)
452 obj2->dropProperty(cx, prop);
453 TRY_BRANCH_AFTER_COND(cond, 2);
454 regs.sp--;
455 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));
456 END_CASE(JSOP_IN)
458 BEGIN_CASE(JSOP_ITER)
459 JS_ASSERT(regs.sp > StackBase(fp));
460 flags = regs.pc[1];
461 if (!js_ValueToIterator(cx, flags, &regs.sp[-1]))
462 goto error;
463 CHECK_INTERRUPT_HANDLER();
464 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));
465 PUSH(JSVAL_VOID);
466 END_CASE(JSOP_ITER)
468 BEGIN_CASE(JSOP_NEXTITER)
469 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
470 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-2]));
471 if (!js_CallIteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-2]), &regs.sp[-1]))
472 goto error;
473 CHECK_INTERRUPT_HANDLER();
474 rval = BOOLEAN_TO_JSVAL(regs.sp[-1] != JSVAL_HOLE);
475 PUSH(rval);
476 END_CASE(JSOP_NEXTITER)
478 BEGIN_CASE(JSOP_ENDITER)
480 * Decrease the stack pointer even when !ok -- see comments in the
481 * exception capturing code for details.
483 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
484 ok = js_CloseIterator(cx, regs.sp[-2]);
485 regs.sp -= 2;
486 if (!ok)
487 goto error;
488 END_CASE(JSOP_ENDITER)
490 BEGIN_CASE(JSOP_FORARG)
491 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
492 slot = GET_ARGNO(regs.pc);
493 JS_ASSERT(slot < fp->fun->nargs);
494 fp->argv[slot] = regs.sp[-1];
495 END_CASE(JSOP_FORARG)
497 BEGIN_CASE(JSOP_FORLOCAL)
498 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
499 slot = GET_SLOTNO(regs.pc);
500 JS_ASSERT(slot < fp->script->nslots);
501 fp->slots[slot] = regs.sp[-1];
502 END_CASE(JSOP_FORLOCAL)
504 BEGIN_CASE(JSOP_FORNAME)
505 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
506 LOAD_ATOM(0);
507 id = ATOM_TO_JSID(atom);
508 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
509 goto error;
510 if (prop)
511 obj2->dropProperty(cx, prop);
512 ok = obj->setProperty(cx, id, &regs.sp[-1]);
513 if (!ok)
514 goto error;
515 END_CASE(JSOP_FORNAME)
517 BEGIN_CASE(JSOP_FORPROP)
518 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
519 LOAD_ATOM(0);
520 id = ATOM_TO_JSID(atom);
521 FETCH_OBJECT(cx, -1, lval, obj);
522 ok = obj->setProperty(cx, id, &regs.sp[-2]);
523 if (!ok)
524 goto error;
525 regs.sp--;
526 END_CASE(JSOP_FORPROP)
528 BEGIN_CASE(JSOP_FORELEM)
530 * JSOP_FORELEM simply dups the property identifier at top of stack
531 * and lets the subsequent JSOP_ENUMELEM opcode sequence handle the
532 * left-hand side expression evaluation and assignment. This opcode
533 * exists solely to help the decompiler.
535 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
536 rval = FETCH_OPND(-1);
537 PUSH(rval);
538 END_CASE(JSOP_FORELEM)
540 BEGIN_CASE(JSOP_DUP)
541 JS_ASSERT(regs.sp > StackBase(fp));
542 rval = FETCH_OPND(-1);
543 PUSH(rval);
544 END_CASE(JSOP_DUP)
546 BEGIN_CASE(JSOP_DUP2)
547 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
548 lval = FETCH_OPND(-2);
549 rval = FETCH_OPND(-1);
550 PUSH(lval);
551 PUSH(rval);
552 END_CASE(JSOP_DUP2)
554 BEGIN_CASE(JSOP_SWAP)
555 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
556 lval = FETCH_OPND(-2);
557 rval = FETCH_OPND(-1);
558 STORE_OPND(-1, lval);
559 STORE_OPND(-2, rval);
560 END_CASE(JSOP_SWAP)
562 BEGIN_CASE(JSOP_PICK)
563 i = regs.pc[1];
564 JS_ASSERT(regs.sp - (i+1) >= StackBase(fp));
565 lval = regs.sp[-(i+1)];
566 memmove(regs.sp - (i+1), regs.sp - i, sizeof(jsval)*i);
567 regs.sp[-1] = lval;
568 END_CASE(JSOP_PICK)
570 #define PROPERTY_OP(n, call) \
571 JS_BEGIN_MACRO \
572 /* Fetch the left part and resolve it to a non-null object. */ \
573 FETCH_OBJECT(cx, n, lval, obj); \
575 /* Get or set the property. */ \
576 if (!call) \
577 goto error; \
578 JS_END_MACRO
580 #define ELEMENT_OP(n, call) \
581 JS_BEGIN_MACRO \
582 /* Fetch the left part and resolve it to a non-null object. */ \
583 FETCH_OBJECT(cx, n - 1, lval, obj); \
585 /* Fetch index and convert it to id suitable for use with obj. */ \
586 FETCH_ELEMENT_ID(obj, n, id); \
588 /* Get or set the element. */ \
589 if (!call) \
590 goto error; \
591 JS_END_MACRO
593 #define NATIVE_GET(cx,obj,pobj,sprop,vp) \
594 JS_BEGIN_MACRO \
595 if (SPROP_HAS_STUB_GETTER(sprop)) { \
596 /* Fast path for Object instance properties. */ \
597 JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT || \
598 !SPROP_HAS_STUB_SETTER(sprop)); \
599 *vp = ((sprop)->slot != SPROP_INVALID_SLOT) \
600 ? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot) \
601 : JSVAL_VOID; \
602 } else { \
603 if (!js_NativeGet(cx, obj, pobj, sprop, vp)) \
604 goto error; \
606 JS_END_MACRO
608 #define NATIVE_SET(cx,obj,sprop,entry,vp) \
609 JS_BEGIN_MACRO \
610 TRACE_2(SetPropHit, entry, sprop); \
611 if (SPROP_HAS_STUB_SETTER(sprop) && \
612 (sprop)->slot != SPROP_INVALID_SLOT) { \
613 /* Fast path for, e.g., Object instance properties. */ \
614 LOCKED_OBJ_WRITE_SLOT(cx, obj, (sprop)->slot, *vp); \
615 } else { \
616 if (!js_NativeSet(cx, obj, sprop, vp)) \
617 goto error; \
619 JS_END_MACRO
622 * Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is
623 * the constant length of the SET opcode sequence, and spdec is the constant
624 * by which to decrease the stack pointer to pop all of the SET op's operands.
626 * NB: unlike macros that could conceivably be replaced by functions (ignoring
627 * goto error), where a call should not have to be braced in order to expand
628 * correctly (e.g., in if (cond) FOO(); else BAR()), these three macros lack
629 * JS_{BEGIN,END}_MACRO brackets. They are also indented so as to align with
630 * nearby opcode code.
632 #define SKIP_POP_AFTER_SET(oplen,spdec) \
633 if (regs.pc[oplen] == JSOP_POP) { \
634 regs.sp -= spdec; \
635 regs.pc += oplen + JSOP_POP_LENGTH; \
636 op = (JSOp) *regs.pc; \
637 DO_OP(); \
640 #define END_SET_CASE(OP) \
641 SKIP_POP_AFTER_SET(OP##_LENGTH, 1); \
642 END_CASE(OP)
644 #define END_SET_CASE_STORE_RVAL(OP,spdec) \
645 SKIP_POP_AFTER_SET(OP##_LENGTH, spdec); \
646 rval = FETCH_OPND(-1); \
647 regs.sp -= (spdec) - 1; \
648 STORE_OPND(-1, rval); \
649 END_CASE(OP)
651 BEGIN_CASE(JSOP_SETCONST)
652 LOAD_ATOM(0);
653 obj = fp->varobj;
654 rval = FETCH_OPND(-1);
655 if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), rval,
656 JS_PropertyStub, JS_PropertyStub,
657 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY,
658 NULL)) {
659 goto error;
661 END_SET_CASE(JSOP_SETCONST);
663 #if JS_HAS_DESTRUCTURING
664 BEGIN_CASE(JSOP_ENUMCONSTELEM)
665 rval = FETCH_OPND(-3);
666 FETCH_OBJECT(cx, -2, lval, obj);
667 FETCH_ELEMENT_ID(obj, -1, id);
668 if (!obj->defineProperty(cx, id, rval,
669 JS_PropertyStub, JS_PropertyStub,
670 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY,
671 NULL)) {
672 goto error;
674 regs.sp -= 3;
675 END_CASE(JSOP_ENUMCONSTELEM)
676 #endif
678 BEGIN_CASE(JSOP_BINDNAME)
679 do {
680 JSPropCacheEntry *entry;
683 * We can skip the property lookup for the global object. If
684 * the property does not exist anywhere on the scope chain,
685 * JSOP_SETNAME adds the property to the global.
687 * As a consequence of this optimization for the global object
688 * we run its JSRESOLVE_ASSIGNING-tolerant resolve hooks only
689 * in JSOP_SETNAME, after the interpreter evaluates the right-
690 * hand-side of the assignment, and not here.
692 * This should be transparent to the hooks because the script,
693 * instead of name = rhs, could have used global.name = rhs
694 * given a global object reference, which also calls the hooks
695 * only after evaluating the rhs. We desire such resolve hook
696 * equivalence between the two forms.
698 obj = fp->scopeChain;
699 if (!OBJ_GET_PARENT(cx, obj))
700 break;
701 if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
702 PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);
703 if (!atom) {
704 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
705 JS_UNLOCK_OBJ(cx, obj2);
706 break;
708 } else {
709 entry = NULL;
710 LOAD_ATOM(0);
712 id = ATOM_TO_JSID(atom);
713 obj = js_FindIdentifierBase(cx, fp->scopeChain, id);
714 if (!obj)
715 goto error;
716 } while (0);
717 PUSH_OPND(OBJECT_TO_JSVAL(obj));
718 END_CASE(JSOP_BINDNAME)
720 BEGIN_CASE(JSOP_IMACOP)
721 JS_ASSERT(JS_UPTRDIFF(fp->imacpc, script->code) < script->length);
722 op = JSOp(*fp->imacpc);
723 DO_OP();
725 #define BITWISE_OP(OP) \
726 JS_BEGIN_MACRO \
727 FETCH_INT(cx, -2, i); \
728 FETCH_INT(cx, -1, j); \
729 i = i OP j; \
730 regs.sp--; \
731 STORE_INT(cx, -1, i); \
732 JS_END_MACRO
734 BEGIN_CASE(JSOP_BITOR)
735 BITWISE_OP(|);
736 END_CASE(JSOP_BITOR)
738 BEGIN_CASE(JSOP_BITXOR)
739 BITWISE_OP(^);
740 END_CASE(JSOP_BITXOR)
742 BEGIN_CASE(JSOP_BITAND)
743 BITWISE_OP(&);
744 END_CASE(JSOP_BITAND)
746 #define RELATIONAL_OP(OP) \
747 JS_BEGIN_MACRO \
748 rval = FETCH_OPND(-1); \
749 lval = FETCH_OPND(-2); \
750 /* Optimize for two int-tagged operands (typical loop control). */ \
751 if ((lval & rval) & JSVAL_INT) { \
752 cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval); \
753 } else { \
754 if (!JSVAL_IS_PRIMITIVE(lval)) \
755 DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \
756 if (!JSVAL_IS_PRIMITIVE(rval)) \
757 DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \
758 if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { \
759 str = JSVAL_TO_STRING(lval); \
760 str2 = JSVAL_TO_STRING(rval); \
761 cond = js_CompareStrings(str, str2) OP 0; \
762 } else { \
763 VALUE_TO_NUMBER(cx, -2, lval, d); \
764 VALUE_TO_NUMBER(cx, -1, rval, d2); \
765 cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \
768 TRY_BRANCH_AFTER_COND(cond, 2); \
769 regs.sp--; \
770 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
771 JS_END_MACRO
774 * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies
775 * because they begin if/else chains, so callers must not put semicolons after
776 * the call expressions!
778 #if JS_HAS_XML_SUPPORT
779 #define XML_EQUALITY_OP(OP) \
780 if ((ltmp == JSVAL_OBJECT && \
781 (obj2 = JSVAL_TO_OBJECT(lval)) && \
782 OBJECT_IS_XML(cx, obj2)) || \
783 (rtmp == JSVAL_OBJECT && \
784 (obj2 = JSVAL_TO_OBJECT(rval)) && \
785 OBJECT_IS_XML(cx, obj2))) { \
786 if (JSVAL_IS_OBJECT(rval) && obj2 == JSVAL_TO_OBJECT(rval)) \
787 rval = lval; \
788 if (!js_TestXMLEquality(cx, obj2, rval, &cond)) \
789 goto error; \
790 cond = cond OP JS_TRUE; \
791 } else
793 #define EXTENDED_EQUALITY_OP(OP) \
794 if (ltmp == JSVAL_OBJECT && \
795 (obj2 = JSVAL_TO_OBJECT(lval)) && \
796 ((clasp = OBJ_GET_CLASS(cx, obj2))->flags & JSCLASS_IS_EXTENDED)) { \
797 JSExtendedClass *xclasp; \
799 xclasp = (JSExtendedClass *) clasp; \
800 if (!xclasp->equality(cx, obj2, rval, &cond)) \
801 goto error; \
802 cond = cond OP JS_TRUE; \
803 } else
804 #else
805 #define XML_EQUALITY_OP(OP) /* nothing */
806 #define EXTENDED_EQUALITY_OP(OP) /* nothing */
807 #endif
809 #define EQUALITY_OP(OP, IFNAN) \
810 JS_BEGIN_MACRO \
811 rval = FETCH_OPND(-1); \
812 lval = FETCH_OPND(-2); \
813 ltmp = JSVAL_TAG(lval); \
814 rtmp = JSVAL_TAG(rval); \
815 XML_EQUALITY_OP(OP) \
816 if (ltmp == rtmp) { \
817 if (ltmp == JSVAL_STRING) { \
818 str = JSVAL_TO_STRING(lval); \
819 str2 = JSVAL_TO_STRING(rval); \
820 cond = js_EqualStrings(str, str2) OP JS_TRUE; \
821 } else if (ltmp == JSVAL_DOUBLE) { \
822 d = *JSVAL_TO_DOUBLE(lval); \
823 d2 = *JSVAL_TO_DOUBLE(rval); \
824 cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \
825 } else { \
826 EXTENDED_EQUALITY_OP(OP) \
827 /* Handle all undefined (=>NaN) and int combinations. */ \
828 cond = lval OP rval; \
830 } else { \
831 if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) { \
832 cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) OP 1; \
833 } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) { \
834 cond = 1 OP 0; \
835 } else { \
836 if (ltmp == JSVAL_OBJECT) { \
837 DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval); \
838 ltmp = JSVAL_TAG(lval); \
839 } else if (rtmp == JSVAL_OBJECT) { \
840 DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); \
841 rtmp = JSVAL_TAG(rval); \
843 if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) { \
844 str = JSVAL_TO_STRING(lval); \
845 str2 = JSVAL_TO_STRING(rval); \
846 cond = js_EqualStrings(str, str2) OP JS_TRUE; \
847 } else { \
848 VALUE_TO_NUMBER(cx, -2, lval, d); \
849 VALUE_TO_NUMBER(cx, -1, rval, d2); \
850 cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \
854 TRY_BRANCH_AFTER_COND(cond, 2); \
855 regs.sp--; \
856 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
857 JS_END_MACRO
859 BEGIN_CASE(JSOP_EQ)
860 EQUALITY_OP(==, JS_FALSE);
861 END_CASE(JSOP_EQ)
863 BEGIN_CASE(JSOP_NE)
864 EQUALITY_OP(!=, JS_TRUE);
865 END_CASE(JSOP_NE)
867 #define STRICT_EQUALITY_OP(OP) \
868 JS_BEGIN_MACRO \
869 rval = FETCH_OPND(-1); \
870 lval = FETCH_OPND(-2); \
871 cond = js_StrictlyEqual(cx, lval, rval) OP JS_TRUE; \
872 regs.sp--; \
873 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
874 JS_END_MACRO
876 BEGIN_CASE(JSOP_STRICTEQ)
877 STRICT_EQUALITY_OP(==);
878 END_CASE(JSOP_STRICTEQ)
880 BEGIN_CASE(JSOP_STRICTNE)
881 STRICT_EQUALITY_OP(!=);
882 END_CASE(JSOP_STRICTNE)
884 BEGIN_CASE(JSOP_CASE)
885 STRICT_EQUALITY_OP(==);
886 (void) POP();
887 if (cond) {
888 len = GET_JUMP_OFFSET(regs.pc);
889 BRANCH(len);
891 PUSH(lval);
892 END_CASE(JSOP_CASE)
894 BEGIN_CASE(JSOP_CASEX)
895 STRICT_EQUALITY_OP(==);
896 (void) POP();
897 if (cond) {
898 len = GET_JUMPX_OFFSET(regs.pc);
899 BRANCH(len);
901 PUSH(lval);
902 END_CASE(JSOP_CASEX)
904 BEGIN_CASE(JSOP_LT)
905 RELATIONAL_OP(<);
906 END_CASE(JSOP_LT)
908 BEGIN_CASE(JSOP_LE)
909 RELATIONAL_OP(<=);
910 END_CASE(JSOP_LE)
912 BEGIN_CASE(JSOP_GT)
913 RELATIONAL_OP(>);
914 END_CASE(JSOP_GT)
916 BEGIN_CASE(JSOP_GE)
917 RELATIONAL_OP(>=);
918 END_CASE(JSOP_GE)
920 #undef EQUALITY_OP
921 #undef RELATIONAL_OP
923 #define SIGNED_SHIFT_OP(OP) \
924 JS_BEGIN_MACRO \
925 FETCH_INT(cx, -2, i); \
926 FETCH_INT(cx, -1, j); \
927 i = i OP (j & 31); \
928 regs.sp--; \
929 STORE_INT(cx, -1, i); \
930 JS_END_MACRO
932 BEGIN_CASE(JSOP_LSH)
933 SIGNED_SHIFT_OP(<<);
934 END_CASE(JSOP_LSH)
936 BEGIN_CASE(JSOP_RSH)
937 SIGNED_SHIFT_OP(>>);
938 END_CASE(JSOP_RSH)
940 BEGIN_CASE(JSOP_URSH)
942 uint32 u;
944 FETCH_UINT(cx, -2, u);
945 FETCH_INT(cx, -1, j);
946 u >>= (j & 31);
947 regs.sp--;
948 STORE_UINT(cx, -1, u);
950 END_CASE(JSOP_URSH)
952 #undef BITWISE_OP
953 #undef SIGNED_SHIFT_OP
955 BEGIN_CASE(JSOP_ADD)
956 rval = FETCH_OPND(-1);
957 lval = FETCH_OPND(-2);
958 #if JS_HAS_XML_SUPPORT
959 if (!JSVAL_IS_PRIMITIVE(lval) &&
960 (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) &&
961 VALUE_IS_XML(cx, rval)) {
962 if (!js_ConcatenateXML(cx, obj2, rval, &rval))
963 goto error;
964 regs.sp--;
965 STORE_OPND(-1, rval);
966 } else
967 #endif
969 if (!JSVAL_IS_PRIMITIVE(lval))
970 DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval);
971 if (!JSVAL_IS_PRIMITIVE(rval))
972 DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval);
973 if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) {
974 if (cond) {
975 str = JSVAL_TO_STRING(lval);
976 str2 = js_ValueToString(cx, rval);
977 if (!str2)
978 goto error;
979 regs.sp[-1] = STRING_TO_JSVAL(str2);
980 } else {
981 str2 = JSVAL_TO_STRING(rval);
982 str = js_ValueToString(cx, lval);
983 if (!str)
984 goto error;
985 regs.sp[-2] = STRING_TO_JSVAL(str);
987 str = js_ConcatStrings(cx, str, str2);
988 if (!str)
989 goto error;
990 regs.sp--;
991 STORE_OPND(-1, STRING_TO_JSVAL(str));
992 } else {
993 VALUE_TO_NUMBER(cx, -2, lval, d);
994 VALUE_TO_NUMBER(cx, -1, rval, d2);
995 d += d2;
996 regs.sp--;
997 STORE_NUMBER(cx, -1, d);
1000 END_CASE(JSOP_ADD)
1002 #define BINARY_OP(OP) \
1003 JS_BEGIN_MACRO \
1004 FETCH_NUMBER(cx, -2, d); \
1005 FETCH_NUMBER(cx, -1, d2); \
1006 d = d OP d2; \
1007 regs.sp--; \
1008 STORE_NUMBER(cx, -1, d); \
1009 JS_END_MACRO
1011 BEGIN_CASE(JSOP_SUB)
1012 BINARY_OP(-);
1013 END_CASE(JSOP_SUB)
1015 BEGIN_CASE(JSOP_MUL)
1016 BINARY_OP(*);
1017 END_CASE(JSOP_MUL)
1019 BEGIN_CASE(JSOP_DIV)
1020 FETCH_NUMBER(cx, -1, d2);
1021 FETCH_NUMBER(cx, -2, d);
1022 regs.sp--;
1023 if (d2 == 0) {
1024 #ifdef XP_WIN
1025 /* XXX MSVC miscompiles such that (NaN == 0) */
1026 if (JSDOUBLE_IS_NaN(d2))
1027 rval = DOUBLE_TO_JSVAL(rt->jsNaN);
1028 else
1029 #endif
1030 if (d == 0 || JSDOUBLE_IS_NaN(d))
1031 rval = DOUBLE_TO_JSVAL(rt->jsNaN);
1032 else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
1033 rval = DOUBLE_TO_JSVAL(rt->jsNegativeInfinity);
1034 else
1035 rval = DOUBLE_TO_JSVAL(rt->jsPositiveInfinity);
1036 STORE_OPND(-1, rval);
1037 } else {
1038 d /= d2;
1039 STORE_NUMBER(cx, -1, d);
1041 END_CASE(JSOP_DIV)
1043 BEGIN_CASE(JSOP_MOD)
1044 FETCH_NUMBER(cx, -1, d2);
1045 FETCH_NUMBER(cx, -2, d);
1046 regs.sp--;
1047 if (d2 == 0) {
1048 STORE_OPND(-1, DOUBLE_TO_JSVAL(rt->jsNaN));
1049 } else {
1050 d = js_fmod(d, d2);
1051 STORE_NUMBER(cx, -1, d);
1053 END_CASE(JSOP_MOD)
1055 BEGIN_CASE(JSOP_NOT)
1056 POP_BOOLEAN(cx, rval, cond);
1057 PUSH_OPND(BOOLEAN_TO_JSVAL(!cond));
1058 END_CASE(JSOP_NOT)
1060 BEGIN_CASE(JSOP_BITNOT)
1061 FETCH_INT(cx, -1, i);
1062 i = ~i;
1063 STORE_INT(cx, -1, i);
1064 END_CASE(JSOP_BITNOT)
1066 BEGIN_CASE(JSOP_NEG)
1068 * When the operand is int jsval, INT_FITS_IN_JSVAL(i) implies
1069 * INT_FITS_IN_JSVAL(-i) unless i is 0 or JSVAL_INT_MIN when the
1070 * results, -0.0 or JSVAL_INT_MAX + 1, are jsdouble values.
1072 rval = FETCH_OPND(-1);
1073 if (JSVAL_IS_INT(rval) &&
1074 rval != INT_TO_JSVAL(JSVAL_INT_MIN) &&
1075 (i = JSVAL_TO_INT(rval)) != 0) {
1076 JS_STATIC_ASSERT(!INT_FITS_IN_JSVAL(-JSVAL_INT_MIN));
1077 i = -i;
1078 JS_ASSERT(INT_FITS_IN_JSVAL(i));
1079 regs.sp[-1] = INT_TO_JSVAL(i);
1080 } else {
1081 if (JSVAL_IS_DOUBLE(rval)) {
1082 d = *JSVAL_TO_DOUBLE(rval);
1083 } else {
1084 d = js_ValueToNumber(cx, &regs.sp[-1]);
1085 if (JSVAL_IS_NULL(regs.sp[-1]))
1086 goto error;
1087 JS_ASSERT(JSVAL_IS_NUMBER(regs.sp[-1]) ||
1088 regs.sp[-1] == JSVAL_TRUE);
1090 #ifdef HPUX
1092 * Negation of a zero doesn't produce a negative
1093 * zero on HPUX. Perform the operation by bit
1094 * twiddling.
1096 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
1097 #else
1098 d = -d;
1099 #endif
1100 if (!js_NewNumberInRootedValue(cx, d, &regs.sp[-1]))
1101 goto error;
1103 END_CASE(JSOP_NEG)
1105 BEGIN_CASE(JSOP_POS)
1106 rval = FETCH_OPND(-1);
1107 if (!JSVAL_IS_NUMBER(rval)) {
1108 d = js_ValueToNumber(cx, &regs.sp[-1]);
1109 rval = regs.sp[-1];
1110 if (JSVAL_IS_NULL(rval))
1111 goto error;
1112 if (rval == JSVAL_TRUE) {
1113 if (!js_NewNumberInRootedValue(cx, d, &regs.sp[-1]))
1114 goto error;
1115 } else {
1116 JS_ASSERT(JSVAL_IS_NUMBER(rval));
1119 END_CASE(JSOP_POS)
1121 BEGIN_CASE(JSOP_DELNAME)
1122 LOAD_ATOM(0);
1123 id = ATOM_TO_JSID(atom);
1124 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
1125 goto error;
1127 /* ECMA says to return true if name is undefined or inherited. */
1128 PUSH_OPND(JSVAL_TRUE);
1129 if (prop) {
1130 obj2->dropProperty(cx, prop);
1131 if (!obj->deleteProperty(cx, id, &regs.sp[-1]))
1132 goto error;
1134 END_CASE(JSOP_DELNAME)
1136 BEGIN_CASE(JSOP_DELPROP)
1137 LOAD_ATOM(0);
1138 id = ATOM_TO_JSID(atom);
1139 PROPERTY_OP(-1, obj->deleteProperty(cx, id, &rval));
1140 STORE_OPND(-1, rval);
1141 END_CASE(JSOP_DELPROP)
1143 BEGIN_CASE(JSOP_DELELEM)
1144 ELEMENT_OP(-1, obj->deleteProperty(cx, id, &rval));
1145 regs.sp--;
1146 STORE_OPND(-1, rval);
1147 END_CASE(JSOP_DELELEM)
1149 BEGIN_CASE(JSOP_TYPEOFEXPR)
1150 BEGIN_CASE(JSOP_TYPEOF)
1151 rval = FETCH_OPND(-1);
1152 type = JS_TypeOfValue(cx, rval);
1153 atom = rt->atomState.typeAtoms[type];
1154 STORE_OPND(-1, ATOM_KEY(atom));
1155 END_CASE(JSOP_TYPEOF)
1157 BEGIN_CASE(JSOP_VOID)
1158 STORE_OPND(-1, JSVAL_VOID);
1159 END_CASE(JSOP_VOID)
1161 BEGIN_CASE(JSOP_INCELEM)
1162 BEGIN_CASE(JSOP_DECELEM)
1163 BEGIN_CASE(JSOP_ELEMINC)
1164 BEGIN_CASE(JSOP_ELEMDEC)
1166 * Delay fetching of id until we have the object to ensure
1167 * the proper evaluation order. See bug 372331.
1169 id = 0;
1170 i = -2;
1171 goto fetch_incop_obj;
1173 BEGIN_CASE(JSOP_INCPROP)
1174 BEGIN_CASE(JSOP_DECPROP)
1175 BEGIN_CASE(JSOP_PROPINC)
1176 BEGIN_CASE(JSOP_PROPDEC)
1177 LOAD_ATOM(0);
1178 id = ATOM_TO_JSID(atom);
1179 i = -1;
1181 fetch_incop_obj:
1182 FETCH_OBJECT(cx, i, lval, obj);
1183 if (id == 0)
1184 FETCH_ELEMENT_ID(obj, -1, id);
1185 goto do_incop;
1187 BEGIN_CASE(JSOP_INCNAME)
1188 BEGIN_CASE(JSOP_DECNAME)
1189 BEGIN_CASE(JSOP_NAMEINC)
1190 BEGIN_CASE(JSOP_NAMEDEC)
1192 JSPropCacheEntry *entry;
1194 obj = fp->scopeChain;
1195 if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
1196 PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);
1197 if (!atom) {
1198 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
1199 if (obj == obj2 && PCVAL_IS_SLOT(entry->vword)) {
1200 slot = PCVAL_TO_SLOT(entry->vword);
1201 JS_ASSERT(slot < OBJ_SCOPE(obj)->freeslot);
1202 rval = LOCKED_OBJ_GET_SLOT(obj, slot);
1203 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {
1204 rtmp = rval;
1205 rval += (js_CodeSpec[op].format & JOF_INC) ? 2 : -2;
1206 if (!(js_CodeSpec[op].format & JOF_POST))
1207 rtmp = rval;
1208 LOCKED_OBJ_SET_SLOT(obj, slot, rval);
1209 JS_UNLOCK_OBJ(cx, obj);
1210 PUSH_OPND(rtmp);
1211 len = JSOP_INCNAME_LENGTH;
1212 DO_NEXT_OP(len);
1215 JS_UNLOCK_OBJ(cx, obj2);
1216 LOAD_ATOM(0);
1218 } else {
1219 LOAD_ATOM(0);
1221 id = ATOM_TO_JSID(atom);
1222 if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
1223 goto error;
1224 if (!prop)
1225 goto atom_not_defined;
1226 obj2->dropProperty(cx, prop);
1229 do_incop:
1231 const JSCodeSpec *cs;
1232 jsval v;
1235 * We need a root to store the value to leave on the stack until
1236 * we have done with obj->setProperty.
1238 PUSH_OPND(JSVAL_NULL);
1239 if (!obj->getProperty(cx, id, &regs.sp[-1]))
1240 goto error;
1242 cs = &js_CodeSpec[op];
1243 JS_ASSERT(cs->ndefs == 1);
1244 JS_ASSERT((cs->format & JOF_TMPSLOT_MASK) == JOF_TMPSLOT2);
1245 v = regs.sp[-1];
1246 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(v))) {
1247 jsval incr;
1249 incr = (cs->format & JOF_INC) ? 2 : -2;
1250 if (cs->format & JOF_POST) {
1251 regs.sp[-1] = v + incr;
1252 } else {
1253 v += incr;
1254 regs.sp[-1] = v;
1256 fp->flags |= JSFRAME_ASSIGNING;
1257 ok = obj->setProperty(cx, id, &regs.sp[-1]);
1258 fp->flags &= ~JSFRAME_ASSIGNING;
1259 if (!ok)
1260 goto error;
1263 * We must set regs.sp[-1] to v for both post and pre increments
1264 * as the setter overwrites regs.sp[-1].
1266 regs.sp[-1] = v;
1267 } else {
1268 /* We need an extra root for the result. */
1269 PUSH_OPND(JSVAL_NULL);
1270 if (!js_DoIncDec(cx, cs, &regs.sp[-2], &regs.sp[-1]))
1271 goto error;
1272 fp->flags |= JSFRAME_ASSIGNING;
1273 ok = obj->setProperty(cx, id, &regs.sp[-1]);
1274 fp->flags &= ~JSFRAME_ASSIGNING;
1275 if (!ok)
1276 goto error;
1277 regs.sp--;
1280 if (cs->nuses == 0) {
1281 /* regs.sp[-1] already contains the result of name increment. */
1282 } else {
1283 rtmp = regs.sp[-1];
1284 regs.sp -= cs->nuses;
1285 regs.sp[-1] = rtmp;
1287 len = cs->length;
1288 DO_NEXT_OP(len);
1292 jsval incr, incr2;
1294 /* Position cases so the most frequent i++ does not need a jump. */
1295 BEGIN_CASE(JSOP_DECARG)
1296 incr = -2; incr2 = -2; goto do_arg_incop;
1297 BEGIN_CASE(JSOP_ARGDEC)
1298 incr = -2; incr2 = 0; goto do_arg_incop;
1299 BEGIN_CASE(JSOP_INCARG)
1300 incr = 2; incr2 = 2; goto do_arg_incop;
1301 BEGIN_CASE(JSOP_ARGINC)
1302 incr = 2; incr2 = 0;
1304 do_arg_incop:
1305 slot = GET_ARGNO(regs.pc);
1306 JS_ASSERT(slot < fp->fun->nargs);
1307 METER_SLOT_OP(op, slot);
1308 vp = fp->argv + slot;
1309 goto do_int_fast_incop;
1311 BEGIN_CASE(JSOP_DECLOCAL)
1312 incr = -2; incr2 = -2; goto do_local_incop;
1313 BEGIN_CASE(JSOP_LOCALDEC)
1314 incr = -2; incr2 = 0; goto do_local_incop;
1315 BEGIN_CASE(JSOP_INCLOCAL)
1316 incr = 2; incr2 = 2; goto do_local_incop;
1317 BEGIN_CASE(JSOP_LOCALINC)
1318 incr = 2; incr2 = 0;
1321 * do_local_incop comes right before do_int_fast_incop as we want to
1322 * avoid an extra jump for variable cases as local++ is more frequent
1323 * than arg++.
1325 do_local_incop:
1326 slot = GET_SLOTNO(regs.pc);
1327 JS_ASSERT(slot < fp->script->nslots);
1328 vp = fp->slots + slot;
1329 METER_SLOT_OP(op, slot);
1330 vp = fp->slots + slot;
1332 do_int_fast_incop:
1333 rval = *vp;
1334 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {
1335 *vp = rval + incr;
1336 JS_ASSERT(JSOP_INCARG_LENGTH == js_CodeSpec[op].length);
1337 SKIP_POP_AFTER_SET(JSOP_INCARG_LENGTH, 0);
1338 PUSH_OPND(rval + incr2);
1339 } else {
1340 PUSH_OPND(rval);
1341 if (!js_DoIncDec(cx, &js_CodeSpec[op], &regs.sp[-1], vp))
1342 goto error;
1344 len = JSOP_INCARG_LENGTH;
1345 JS_ASSERT(len == js_CodeSpec[op].length);
1346 DO_NEXT_OP(len);
1349 /* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */
1350 #define FAST_GLOBAL_INCREMENT_OP(SLOWOP,INCR,INCR2) \
1351 op2 = SLOWOP; \
1352 incr = INCR; \
1353 incr2 = INCR2; \
1354 goto do_global_incop
1357 jsval incr, incr2;
1359 BEGIN_CASE(JSOP_DECGVAR)
1360 FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, -2, -2);
1361 BEGIN_CASE(JSOP_GVARDEC)
1362 FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, -2, 0);
1363 BEGIN_CASE(JSOP_INCGVAR)
1364 FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, 2, 2);
1365 BEGIN_CASE(JSOP_GVARINC)
1366 FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, 2, 0);
1368 #undef FAST_GLOBAL_INCREMENT_OP
1370 do_global_incop:
1371 JS_ASSERT((js_CodeSpec[op].format & JOF_TMPSLOT_MASK) ==
1372 JOF_TMPSLOT2);
1373 slot = GET_SLOTNO(regs.pc);
1374 JS_ASSERT(slot < GlobalVarCount(fp));
1375 METER_SLOT_OP(op, slot);
1376 lval = fp->slots[slot];
1377 if (JSVAL_IS_NULL(lval)) {
1378 op = op2;
1379 DO_OP();
1381 slot = JSVAL_TO_INT(lval);
1382 rval = OBJ_GET_SLOT(cx, fp->varobj, slot);
1383 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {
1384 PUSH_OPND(rval + incr2);
1385 rval += incr;
1386 } else {
1387 PUSH_OPND(rval);
1388 PUSH_OPND(JSVAL_NULL); /* Extra root */
1389 if (!js_DoIncDec(cx, &js_CodeSpec[op], &regs.sp[-2], &regs.sp[-1]))
1390 goto error;
1391 rval = regs.sp[-1];
1392 --regs.sp;
1394 OBJ_SET_SLOT(cx, fp->varobj, slot, rval);
1395 len = JSOP_INCGVAR_LENGTH; /* all gvar incops are same length */
1396 JS_ASSERT(len == js_CodeSpec[op].length);
1397 DO_NEXT_OP(len);
1400 #define COMPUTE_THIS(cx, fp, obj) \
1401 JS_BEGIN_MACRO \
1402 if (!(obj = js_ComputeThisForFrame(cx, fp))) \
1403 goto error; \
1404 JS_END_MACRO
1406 BEGIN_CASE(JSOP_THIS)
1407 COMPUTE_THIS(cx, fp, obj);
1408 PUSH_OPND(OBJECT_TO_JSVAL(obj));
1409 END_CASE(JSOP_THIS)
1411 BEGIN_CASE(JSOP_GETTHISPROP)
1412 i = 0;
1413 COMPUTE_THIS(cx, fp, obj);
1414 PUSH(JSVAL_NULL);
1415 goto do_getprop_with_obj;
1417 #undef COMPUTE_THIS
1419 BEGIN_CASE(JSOP_GETARGPROP)
1420 i = ARGNO_LEN;
1421 slot = GET_ARGNO(regs.pc);
1422 JS_ASSERT(slot < fp->fun->nargs);
1423 PUSH_OPND(fp->argv[slot]);
1424 goto do_getprop_body;
1426 BEGIN_CASE(JSOP_GETLOCALPROP)
1427 i = SLOTNO_LEN;
1428 slot = GET_SLOTNO(regs.pc);
1429 JS_ASSERT(slot < script->nslots);
1430 PUSH_OPND(fp->slots[slot]);
1431 goto do_getprop_body;
1433 BEGIN_CASE(JSOP_GETPROP)
1434 BEGIN_CASE(JSOP_GETXPROP)
1435 i = 0;
1437 do_getprop_body:
1438 lval = FETCH_OPND(-1);
1440 do_getprop_with_lval:
1441 VALUE_TO_OBJECT(cx, -1, lval, obj);
1443 do_getprop_with_obj:
1444 do {
1445 JSObject *aobj;
1446 JSPropCacheEntry *entry;
1448 aobj = js_GetProtoIfDenseArray(cx, obj);
1449 if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) {
1450 PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom);
1451 if (!atom) {
1452 ASSERT_VALID_PROPERTY_CACHE_HIT(i, aobj, obj2, entry);
1453 if (PCVAL_IS_OBJECT(entry->vword)) {
1454 rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);
1455 } else if (PCVAL_IS_SLOT(entry->vword)) {
1456 slot = PCVAL_TO_SLOT(entry->vword);
1457 JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
1458 rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
1459 } else {
1460 JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
1461 sprop = PCVAL_TO_SPROP(entry->vword);
1462 NATIVE_GET(cx, obj, obj2, sprop, &rval);
1464 JS_UNLOCK_OBJ(cx, obj2);
1465 break;
1467 } else {
1468 entry = NULL;
1469 if (i < 0)
1470 atom = rt->atomState.lengthAtom;
1471 else
1472 LOAD_ATOM(i);
1474 id = ATOM_TO_JSID(atom);
1475 if (entry
1476 ? !js_GetPropertyHelper(cx, obj, id, true, &rval)
1477 : !obj->getProperty(cx, id, &rval)) {
1478 goto error;
1480 } while (0);
1482 STORE_OPND(-1, rval);
1483 JS_ASSERT(JSOP_GETPROP_LENGTH + i == js_CodeSpec[op].length);
1484 len = JSOP_GETPROP_LENGTH + i;
1485 END_VARLEN_CASE
1487 BEGIN_CASE(JSOP_LENGTH)
1488 lval = FETCH_OPND(-1);
1489 if (JSVAL_IS_STRING(lval)) {
1490 str = JSVAL_TO_STRING(lval);
1491 regs.sp[-1] = INT_TO_JSVAL(str->length());
1492 } else if (!JSVAL_IS_PRIMITIVE(lval) &&
1493 (obj = JSVAL_TO_OBJECT(lval), OBJ_IS_ARRAY(cx, obj))) {
1494 jsuint length;
1497 * We know that the array is created with only its 'length'
1498 * private data in a fixed slot at JSSLOT_ARRAY_LENGTH. See
1499 * also JSOP_ARRAYPUSH, far below.
1501 length = obj->fslots[JSSLOT_ARRAY_LENGTH];
1502 if (length <= JSVAL_INT_MAX) {
1503 regs.sp[-1] = INT_TO_JSVAL(length);
1504 } else if (!js_NewDoubleInRootedValue(cx, (jsdouble) length,
1505 &regs.sp[-1])) {
1506 goto error;
1508 } else {
1509 i = -2;
1510 goto do_getprop_with_lval;
1512 END_CASE(JSOP_LENGTH)
1514 BEGIN_CASE(JSOP_CALLPROP)
1516 JSObject *aobj;
1517 JSPropCacheEntry *entry;
1519 lval = FETCH_OPND(-1);
1520 if (!JSVAL_IS_PRIMITIVE(lval)) {
1521 obj = JSVAL_TO_OBJECT(lval);
1522 } else {
1523 if (JSVAL_IS_STRING(lval)) {
1524 i = JSProto_String;
1525 } else if (JSVAL_IS_NUMBER(lval)) {
1526 i = JSProto_Number;
1527 } else if (JSVAL_IS_BOOLEAN(lval)) {
1528 i = JSProto_Boolean;
1529 } else {
1530 JS_ASSERT(JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval));
1531 js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
1532 goto error;
1535 if (!js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj))
1536 goto error;
1539 aobj = js_GetProtoIfDenseArray(cx, obj);
1540 if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) {
1541 PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom);
1542 if (!atom) {
1543 ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry);
1544 if (PCVAL_IS_OBJECT(entry->vword)) {
1545 rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);
1546 } else if (PCVAL_IS_SLOT(entry->vword)) {
1547 slot = PCVAL_TO_SLOT(entry->vword);
1548 JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
1549 rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
1550 } else {
1551 JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
1552 sprop = PCVAL_TO_SPROP(entry->vword);
1553 NATIVE_GET(cx, obj, obj2, sprop, &rval);
1555 JS_UNLOCK_OBJ(cx, obj2);
1556 STORE_OPND(-1, rval);
1557 PUSH_OPND(lval);
1558 goto end_callprop;
1560 } else {
1561 entry = NULL;
1562 LOAD_ATOM(0);
1566 * Cache miss: use the immediate atom that was loaded for us under
1567 * PROPERTY_CACHE_TEST.
1569 id = ATOM_TO_JSID(atom);
1570 PUSH(JSVAL_NULL);
1571 if (!JSVAL_IS_PRIMITIVE(lval)) {
1572 if (!js_GetMethod(cx, obj, id, !!entry, &rval))
1573 goto error;
1574 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
1575 STORE_OPND(-2, rval);
1576 } else {
1577 JS_ASSERT(obj->map->ops->getProperty == js_GetProperty);
1578 if (!js_GetPropertyHelper(cx, obj, id, true, &rval))
1579 goto error;
1580 STORE_OPND(-1, lval);
1581 STORE_OPND(-2, rval);
1584 end_callprop:
1585 /* Wrap primitive lval in object clothing if necessary. */
1586 if (JSVAL_IS_PRIMITIVE(lval)) {
1587 /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */
1588 if (!VALUE_IS_FUNCTION(cx, rval) ||
1589 (obj = JSVAL_TO_OBJECT(rval),
1590 fun = GET_FUNCTION_PRIVATE(cx, obj),
1591 !PRIMITIVE_THIS_TEST(fun, lval))) {
1592 if (!js_PrimitiveToObject(cx, &regs.sp[-1]))
1593 goto error;
1596 #if JS_HAS_NO_SUCH_METHOD
1597 if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) {
1598 LOAD_ATOM(0);
1599 regs.sp[-2] = ATOM_KEY(atom);
1600 if (!js_OnUnknownMethod(cx, regs.sp - 2))
1601 goto error;
1603 #endif
1605 END_CASE(JSOP_CALLPROP)
1607 BEGIN_CASE(JSOP_SETNAME)
1608 BEGIN_CASE(JSOP_SETPROP)
1609 rval = FETCH_OPND(-1);
1610 lval = FETCH_OPND(-2);
1611 JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval) || op == JSOP_SETPROP);
1612 VALUE_TO_OBJECT(cx, -2, lval, obj);
1614 do {
1615 JSPropCacheEntry *entry;
1617 entry = NULL;
1618 atom = NULL;
1619 if (JS_LIKELY(obj->map->ops->setProperty == js_SetProperty)) {
1620 JSPropertyCache *cache = &JS_PROPERTY_CACHE(cx);
1621 uint32 kshape = OBJ_SHAPE(obj);
1624 * Open-code PROPERTY_CACHE_TEST, specializing for two
1625 * important set-property cases. First:
1627 * function f(a, b, c) {
1628 * var o = {p:a, q:b, r:c};
1629 * return o;
1632 * or similar real-world cases, which evolve a newborn
1633 * native object predicatably through some bounded number
1634 * of property additions. And second:
1636 * o.p = x;
1638 * in a frequently executed method or loop body, where p
1639 * will (possibly after the first iteration) always exist
1640 * in native object o.
1642 entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)];
1643 PCMETER(cache->pctestentry = entry);
1644 PCMETER(cache->tests++);
1645 PCMETER(cache->settests++);
1646 if (entry->kpc == regs.pc && entry->kshape == kshape) {
1647 JS_ASSERT(PCVCAP_TAG(entry->vcap) <= 1);
1648 if (JS_LOCK_OBJ_IF_SHAPE(cx, obj, kshape)) {
1649 JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
1650 sprop = PCVAL_TO_SPROP(entry->vword);
1651 JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
1652 JS_ASSERT_IF(!(sprop->attrs & JSPROP_SHARED),
1653 PCVCAP_TAG(entry->vcap) == 0);
1655 JSScope *scope = OBJ_SCOPE(obj);
1656 JS_ASSERT(!scope->sealed());
1659 * Fastest path: check whether the cached sprop is
1660 * already in scope and call NATIVE_SET and break
1661 * to get out of the do-while(0). But we can call
1662 * NATIVE_SET only if obj owns scope or sprop is
1663 * shared.
1665 bool checkForAdd;
1666 if (sprop->attrs & JSPROP_SHARED) {
1667 if (PCVCAP_TAG(entry->vcap) == 0 ||
1668 ((obj2 = OBJ_GET_PROTO(cx, obj)) &&
1669 OBJ_IS_NATIVE(obj2) &&
1670 OBJ_SHAPE(obj2) == PCVCAP_SHAPE(entry->vcap))) {
1671 goto fast_set_propcache_hit;
1674 /* The cache entry doesn't apply. vshape mismatch. */
1675 checkForAdd = false;
1676 } else if (scope->owned()) {
1677 if (sprop == scope->lastProp || scope->has(sprop)) {
1678 fast_set_propcache_hit:
1679 PCMETER(cache->pchits++);
1680 PCMETER(cache->setpchits++);
1681 NATIVE_SET(cx, obj, sprop, entry, &rval);
1682 JS_UNLOCK_SCOPE(cx, scope);
1683 break;
1685 checkForAdd =
1686 !(sprop->attrs & JSPROP_SHARED) &&
1687 sprop->parent == scope->lastProp &&
1688 !scope->hadMiddleDelete();
1689 } else {
1690 scope = js_GetMutableScope(cx, obj);
1691 if (!scope) {
1692 JS_UNLOCK_OBJ(cx, obj);
1693 goto error;
1695 checkForAdd = !sprop->parent;
1698 if (checkForAdd &&
1699 SPROP_HAS_STUB_SETTER(sprop) &&
1700 (slot = sprop->slot) == scope->freeslot) {
1702 * Fast path: adding a plain old property that
1703 * was once at the frontier of the property
1704 * tree, whose slot is next to claim among the
1705 * allocated slots in obj, where scope->table
1706 * has not been created yet.
1708 * We may want to remove hazard conditions
1709 * above and inline compensation code here,
1710 * depending on real-world workloads.
1712 JS_ASSERT(!(LOCKED_OBJ_GET_CLASS(obj)->flags &
1713 JSCLASS_SHARE_ALL_PROPERTIES));
1715 PCMETER(cache->pchits++);
1716 PCMETER(cache->addpchits++);
1719 * Beware classes such as Function that use
1720 * the reserveSlots hook to allocate a number
1721 * of reserved slots that may vary with obj.
1723 if (slot < STOBJ_NSLOTS(obj) &&
1724 !OBJ_GET_CLASS(cx, obj)->reserveSlots) {
1725 ++scope->freeslot;
1726 } else {
1727 if (!js_AllocSlot(cx, obj, &slot)) {
1728 JS_UNLOCK_SCOPE(cx, scope);
1729 goto error;
1734 * If this obj's number of reserved slots
1735 * differed, or if something created a hash
1736 * table for scope, we must pay the price of
1737 * JSScope::add.
1739 * If slot does not match the cached sprop's
1740 * slot, update the cache entry in the hope
1741 * that obj and other instances with the same
1742 * number of reserved slots are now "hot".
1744 if (slot != sprop->slot || scope->table) {
1745 JSScopeProperty *sprop2 =
1746 scope->add(cx, sprop->id,
1747 sprop->getter, sprop->setter,
1748 slot, sprop->attrs,
1749 sprop->flags, sprop->shortid);
1750 if (!sprop2) {
1751 js_FreeSlot(cx, obj, slot);
1752 JS_UNLOCK_SCOPE(cx, scope);
1753 goto error;
1755 if (sprop2 != sprop) {
1756 PCMETER(cache->slotchanges++);
1757 JS_ASSERT(slot != sprop->slot &&
1758 slot == sprop2->slot &&
1759 sprop2->id == sprop->id);
1760 entry->vword = SPROP_TO_PCVAL(sprop2);
1762 sprop = sprop2;
1763 } else {
1764 scope->extend(cx, sprop);
1767 LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, rval);
1768 TRACE_2(SetPropHit, entry, sprop);
1769 LOCKED_OBJ_SET_SLOT(obj, slot, rval);
1770 JS_UNLOCK_SCOPE(cx, scope);
1773 * Purge the property cache of the id we may
1774 * have just shadowed in obj's scope and proto
1775 * chains. We do this after unlocking obj's
1776 * scope to avoid lock nesting.
1778 js_PurgeScopeChain(cx, obj, sprop->id);
1779 break;
1781 JS_UNLOCK_SCOPE(cx, scope);
1782 PCMETER(cache->setpcmisses++);
1786 atom = js_FullTestPropertyCache(cx, regs.pc, &obj, &obj2,
1787 &entry);
1788 if (atom) {
1789 PCMETER(cache->misses++);
1790 PCMETER(cache->setmisses++);
1791 } else {
1792 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
1793 sprop = NULL;
1794 if (obj == obj2) {
1795 JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
1796 sprop = PCVAL_TO_SPROP(entry->vword);
1797 JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
1798 JS_ASSERT(!OBJ_SCOPE(obj2)->sealed());
1799 NATIVE_SET(cx, obj, sprop, entry, &rval);
1801 JS_UNLOCK_OBJ(cx, obj2);
1802 if (sprop)
1803 break;
1807 if (!atom)
1808 LOAD_ATOM(0);
1809 id = ATOM_TO_JSID(atom);
1810 if (entry) {
1811 if (!js_SetPropertyHelper(cx, obj, id, true, &rval))
1812 goto error;
1813 } else {
1814 if (!obj->setProperty(cx, id, &rval))
1815 goto error;
1816 ABORT_RECORDING(cx, "Non-native set");
1818 } while (0);
1819 END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2);
1821 BEGIN_CASE(JSOP_GETELEM)
1822 /* Open-coded ELEMENT_OP optimized for strings and dense arrays. */
1823 lval = FETCH_OPND(-2);
1824 rval = FETCH_OPND(-1);
1825 if (JSVAL_IS_STRING(lval) && JSVAL_IS_INT(rval)) {
1826 str = JSVAL_TO_STRING(lval);
1827 i = JSVAL_TO_INT(rval);
1828 if ((size_t)i < str->length()) {
1829 str = js_GetUnitString(cx, str, (size_t)i);
1830 if (!str)
1831 goto error;
1832 rval = STRING_TO_JSVAL(str);
1833 goto end_getelem;
1837 VALUE_TO_OBJECT(cx, -2, lval, obj);
1838 if (JSVAL_IS_INT(rval)) {
1839 if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
1840 jsuint length;
1842 length = js_DenseArrayCapacity(obj);
1843 i = JSVAL_TO_INT(rval);
1844 if ((jsuint)i < length &&
1845 i < obj->fslots[JSSLOT_ARRAY_LENGTH]) {
1846 rval = obj->dslots[i];
1847 if (rval != JSVAL_HOLE)
1848 goto end_getelem;
1850 /* Reload rval from the stack in the rare hole case. */
1851 rval = FETCH_OPND(-1);
1854 id = INT_JSVAL_TO_JSID(rval);
1855 } else {
1856 if (!js_InternNonIntElementId(cx, obj, rval, &id))
1857 goto error;
1860 if (!obj->getProperty(cx, id, &rval))
1861 goto error;
1862 end_getelem:
1863 regs.sp--;
1864 STORE_OPND(-1, rval);
1865 END_CASE(JSOP_GETELEM)
1867 BEGIN_CASE(JSOP_CALLELEM)
1868 ELEMENT_OP(-1, js_GetMethod(cx, obj, id, false, &rval));
1869 #if JS_HAS_NO_SUCH_METHOD
1870 if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) {
1871 regs.sp[-2] = regs.sp[-1];
1872 regs.sp[-1] = OBJECT_TO_JSVAL(obj);
1873 if (!js_OnUnknownMethod(cx, regs.sp - 2))
1874 goto error;
1875 } else
1876 #endif
1878 STORE_OPND(-2, rval);
1879 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
1881 END_CASE(JSOP_CALLELEM)
1883 BEGIN_CASE(JSOP_SETELEM)
1884 rval = FETCH_OPND(-1);
1885 FETCH_OBJECT(cx, -3, lval, obj);
1886 FETCH_ELEMENT_ID(obj, -2, id);
1887 do {
1888 if (OBJ_IS_DENSE_ARRAY(cx, obj) && JSID_IS_INT(id)) {
1889 jsuint length;
1891 length = js_DenseArrayCapacity(obj);
1892 i = JSID_TO_INT(id);
1893 if ((jsuint)i < length) {
1894 if (obj->dslots[i] == JSVAL_HOLE) {
1895 if (js_PrototypeHasIndexedProperties(cx, obj))
1896 break;
1897 if (i >= obj->fslots[JSSLOT_ARRAY_LENGTH])
1898 obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1;
1899 obj->fslots[JSSLOT_ARRAY_COUNT]++;
1901 obj->dslots[i] = rval;
1902 goto end_setelem;
1905 } while (0);
1906 if (!obj->setProperty(cx, id, &rval))
1907 goto error;
1908 end_setelem:
1909 END_SET_CASE_STORE_RVAL(JSOP_SETELEM, 3)
1911 BEGIN_CASE(JSOP_ENUMELEM)
1912 /* Funky: the value to set is under the [obj, id] pair. */
1913 rval = FETCH_OPND(-3);
1914 FETCH_OBJECT(cx, -2, lval, obj);
1915 FETCH_ELEMENT_ID(obj, -1, id);
1916 if (!obj->setProperty(cx, id, &rval))
1917 goto error;
1918 regs.sp -= 3;
1919 END_CASE(JSOP_ENUMELEM)
1921 BEGIN_CASE(JSOP_NEW)
1922 /* Get immediate argc and find the constructor function. */
1923 argc = GET_ARGC(regs.pc);
1924 vp = regs.sp - (2 + argc);
1925 JS_ASSERT(vp >= StackBase(fp));
1928 * Assign lval, obj, and fun exactly as the code at inline_call:
1929 * expects to find them, to avoid nesting a js_Interpret call via
1930 * js_InvokeConstructor.
1932 lval = *vp;
1933 if (VALUE_IS_FUNCTION(cx, lval)) {
1934 obj = JSVAL_TO_OBJECT(lval);
1935 fun = GET_FUNCTION_PRIVATE(cx, obj);
1936 if (FUN_INTERPRETED(fun)) {
1937 /* Root as we go using vp[1]. */
1938 if (!obj->getProperty(cx,
1939 ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom),
1940 &vp[1])) {
1941 goto error;
1943 rval = vp[1];
1944 obj2 = js_NewObject(cx, &js_ObjectClass,
1945 JSVAL_IS_OBJECT(rval)
1946 ? JSVAL_TO_OBJECT(rval)
1947 : NULL,
1948 OBJ_GET_PARENT(cx, obj));
1949 if (!obj2)
1950 goto error;
1951 vp[1] = OBJECT_TO_JSVAL(obj2);
1952 flags = JSFRAME_CONSTRUCTING;
1953 goto inline_call;
1957 if (!js_InvokeConstructor(cx, argc, JS_FALSE, vp))
1958 goto error;
1959 regs.sp = vp + 1;
1960 CHECK_INTERRUPT_HANDLER();
1961 TRACE_0(NativeCallComplete);
1962 END_CASE(JSOP_NEW)
1964 BEGIN_CASE(JSOP_CALL)
1965 BEGIN_CASE(JSOP_EVAL)
1966 BEGIN_CASE(JSOP_APPLY)
1967 argc = GET_ARGC(regs.pc);
1968 vp = regs.sp - (argc + 2);
1970 lval = *vp;
1971 if (VALUE_IS_FUNCTION(cx, lval)) {
1972 obj = JSVAL_TO_OBJECT(lval);
1973 fun = GET_FUNCTION_PRIVATE(cx, obj);
1975 /* Clear frame flags since this is not a constructor call. */
1976 flags = 0;
1977 if (FUN_INTERPRETED(fun))
1978 inline_call:
1980 uintN nframeslots, nvars, missing;
1981 JSArena *a;
1982 jsuword nbytes;
1983 void *newmark;
1984 jsval *newsp;
1985 JSInlineFrame *newifp;
1986 JSInterpreterHook hook;
1988 /* Restrict recursion of lightweight functions. */
1989 if (inlineCallCount == MAX_INLINE_CALL_COUNT) {
1990 js_ReportOverRecursed(cx);
1991 goto error;
1994 /* Compute the total number of stack slots needed by fun. */
1995 nframeslots = JS_HOWMANY(sizeof(JSInlineFrame),
1996 sizeof(jsval));
1997 script = fun->u.i.script;
1998 atoms = script->atomMap.vector;
1999 nbytes = (nframeslots + script->nslots) * sizeof(jsval);
2001 /* Allocate missing expected args adjacent to actuals. */
2002 a = cx->stackPool.current;
2003 newmark = (void *) a->avail;
2004 if (fun->nargs <= argc) {
2005 missing = 0;
2006 } else {
2007 newsp = vp + 2 + fun->nargs;
2008 JS_ASSERT(newsp > regs.sp);
2009 if ((jsuword) newsp <= a->limit) {
2010 if ((jsuword) newsp > a->avail)
2011 a->avail = (jsuword) newsp;
2012 jsval *argsp = newsp;
2013 do {
2014 *--argsp = JSVAL_VOID;
2015 } while (argsp != regs.sp);
2016 missing = 0;
2017 } else {
2018 missing = fun->nargs - argc;
2019 nbytes += (2 + fun->nargs) * sizeof(jsval);
2023 /* Allocate the inline frame with its slots and operands. */
2024 if (a->avail + nbytes <= a->limit) {
2025 newsp = (jsval *) a->avail;
2026 a->avail += nbytes;
2027 JS_ASSERT(missing == 0);
2028 } else {
2029 JS_ARENA_ALLOCATE_CAST(newsp, jsval *, &cx->stackPool,
2030 nbytes);
2031 if (!newsp) {
2032 js_ReportOutOfScriptQuota(cx);
2033 goto bad_inline_call;
2037 * Move args if the missing ones overflow arena a, then
2038 * push undefined for the missing args.
2040 if (missing) {
2041 memcpy(newsp, vp, (2 + argc) * sizeof(jsval));
2042 vp = newsp;
2043 newsp = vp + 2 + argc;
2044 do {
2045 *newsp++ = JSVAL_VOID;
2046 } while (--missing != 0);
2050 /* Claim space for the stack frame and initialize it. */
2051 newifp = (JSInlineFrame *) newsp;
2052 newsp += nframeslots;
2053 newifp->frame.callobj = NULL;
2054 newifp->frame.argsobj = NULL;
2055 newifp->frame.varobj = NULL;
2056 newifp->frame.script = script;
2057 newifp->frame.fun = fun;
2058 newifp->frame.argc = argc;
2059 newifp->frame.argv = vp + 2;
2060 newifp->frame.rval = JSVAL_VOID;
2061 newifp->frame.down = fp;
2062 newifp->frame.annotation = NULL;
2063 newifp->frame.scopeChain = parent = OBJ_GET_PARENT(cx, obj);
2064 newifp->frame.sharpDepth = 0;
2065 newifp->frame.sharpArray = NULL;
2066 newifp->frame.flags = flags;
2067 newifp->frame.dormantNext = NULL;
2068 newifp->frame.xmlNamespace = NULL;
2069 newifp->frame.blockChain = NULL;
2070 if (script->staticLevel < JS_DISPLAY_SIZE) {
2071 JSStackFrame **disp = &cx->display[script->staticLevel];
2072 newifp->frame.displaySave = *disp;
2073 *disp = &newifp->frame;
2075 newifp->mark = newmark;
2077 /* Compute the 'this' parameter now that argv is set. */
2078 JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags));
2079 JS_ASSERT(JSVAL_IS_OBJECT(vp[1]));
2080 newifp->frame.thisp = (JSObject *)vp[1];
2082 newifp->frame.regs = NULL;
2083 newifp->frame.imacpc = NULL;
2084 newifp->frame.slots = newsp;
2086 /* Push void to initialize local variables. */
2087 nvars = fun->u.i.nvars;
2088 while (nvars--)
2089 *newsp++ = JSVAL_VOID;
2091 /* Scope with a call object parented by callee's parent. */
2092 if (JSFUN_HEAVYWEIGHT_TEST(fun->flags) &&
2093 !js_GetCallObject(cx, &newifp->frame)) {
2094 goto bad_inline_call;
2097 /* Switch version if currentVersion wasn't overridden. */
2098 newifp->callerVersion = (JSVersion) cx->version;
2099 if (JS_LIKELY(cx->version == currentVersion)) {
2100 currentVersion = (JSVersion) script->version;
2101 if (currentVersion != cx->version)
2102 js_SetVersion(cx, currentVersion);
2105 /* Push the frame and set interpreter registers. */
2106 newifp->callerRegs = regs;
2107 fp->regs = &newifp->callerRegs;
2108 regs.sp = newsp;
2109 regs.pc = script->code;
2110 newifp->frame.regs = &regs;
2111 cx->fp = fp = &newifp->frame;
2113 /* Call the debugger hook if present. */
2114 hook = cx->debugHooks->callHook;
2115 if (hook) {
2116 newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0,
2117 cx->debugHooks->callHookData);
2118 CHECK_INTERRUPT_HANDLER();
2119 } else {
2120 newifp->hookData = NULL;
2123 TRACE_0(EnterFrame);
2125 inlineCallCount++;
2126 JS_RUNTIME_METER(rt, inlineCalls);
2128 #ifdef INCLUDE_MOZILLA_DTRACE
2129 /* DTrace function entry, inlines */
2130 if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
2131 jsdtrace_function_entry(cx, fp, fun);
2132 if (JAVASCRIPT_FUNCTION_INFO_ENABLED())
2133 jsdtrace_function_info(cx, fp, fp->down, fun);
2134 if (JAVASCRIPT_FUNCTION_ARGS_ENABLED())
2135 jsdtrace_function_args(cx, fp, fun, fp->argc, fp->argv);
2136 #endif
2138 /* Load first op and dispatch it (safe since JSOP_STOP). */
2139 op = (JSOp) *regs.pc;
2140 DO_OP();
2142 bad_inline_call:
2143 JS_ASSERT(fp->regs == &regs);
2144 script = fp->script;
2145 atoms = script->atomMap.vector;
2146 js_FreeRawStack(cx, newmark);
2147 goto error;
2150 if (fun->flags & JSFUN_FAST_NATIVE) {
2151 #ifdef INCLUDE_MOZILLA_DTRACE
2152 /* DTrace function entry, non-inlines */
2153 if (VALUE_IS_FUNCTION(cx, lval)) {
2154 if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
2155 jsdtrace_function_entry(cx, NULL, fun);
2156 if (JAVASCRIPT_FUNCTION_INFO_ENABLED())
2157 jsdtrace_function_info(cx, NULL, fp, fun);
2158 if (JAVASCRIPT_FUNCTION_ARGS_ENABLED())
2159 jsdtrace_function_args(cx, fp, fun, argc, vp+2);
2161 #endif
2163 JS_ASSERT(fun->u.n.extra == 0);
2164 JS_ASSERT(JSVAL_IS_OBJECT(vp[1]) ||
2165 PRIMITIVE_THIS_TEST(fun, vp[1]));
2166 ok = ((JSFastNative) fun->u.n.native)(cx, argc, vp);
2167 #ifdef INCLUDE_MOZILLA_DTRACE
2168 if (VALUE_IS_FUNCTION(cx, lval)) {
2169 if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
2170 jsdtrace_function_rval(cx, NULL, fun, vp);
2171 if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
2172 jsdtrace_function_return(cx, NULL, fun);
2174 #endif
2175 regs.sp = vp + 1;
2176 if (!ok) {
2178 * If we are executing the JSOP_NEXTITER imacro and a Stopiteration
2179 * exception is raised, transform it into a JSVAL_HOLE return value.
2180 * The tracer generates equivalent code by calling CatchStopIteration_tn.
2182 if (fp->imacpc && *fp->imacpc == JSOP_NEXTITER &&
2183 cx->throwing && js_ValueIsStopIteration(cx->exception)) {
2184 // pc may point to JSOP_DUP here due to bug 474854.
2185 JS_ASSERT(*regs.pc == JSOP_CALL || *regs.pc == JSOP_DUP);
2186 cx->throwing = JS_FALSE;
2187 cx->exception = JSVAL_VOID;
2188 regs.sp[-1] = JSVAL_HOLE;
2189 } else {
2190 goto error;
2193 TRACE_0(NativeCallComplete);
2194 goto end_call;
2198 ok = js_Invoke(cx, argc, vp, 0);
2199 regs.sp = vp + 1;
2200 CHECK_INTERRUPT_HANDLER();
2201 if (!ok)
2202 goto error;
2203 JS_RUNTIME_METER(rt, nonInlineCalls);
2204 TRACE_0(NativeCallComplete);
2206 end_call:
2207 END_CASE(JSOP_CALL)
2209 BEGIN_CASE(JSOP_SETCALL)
2210 argc = GET_ARGC(regs.pc);
2211 vp = regs.sp - argc - 2;
2212 if (js_Invoke(cx, argc, vp, 0))
2213 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_LEFTSIDE_OF_ASS);
2214 goto error;
2215 END_CASE(JSOP_SETCALL)
2217 BEGIN_CASE(JSOP_NAME)
2218 BEGIN_CASE(JSOP_CALLNAME)
2220 JSPropCacheEntry *entry;
2222 obj = fp->scopeChain;
2223 if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
2224 PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);
2225 if (!atom) {
2226 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
2227 if (PCVAL_IS_OBJECT(entry->vword)) {
2228 rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);
2229 JS_UNLOCK_OBJ(cx, obj2);
2230 goto do_push_rval;
2233 if (PCVAL_IS_SLOT(entry->vword)) {
2234 slot = PCVAL_TO_SLOT(entry->vword);
2235 JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
2236 rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
2237 JS_UNLOCK_OBJ(cx, obj2);
2238 goto do_push_rval;
2241 JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
2242 sprop = PCVAL_TO_SPROP(entry->vword);
2243 goto do_native_get;
2245 } else {
2246 LOAD_ATOM(0);
2249 id = ATOM_TO_JSID(atom);
2250 if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
2251 goto error;
2252 if (!prop) {
2253 /* Kludge to allow (typeof foo == "undefined") tests. */
2254 endpc = script->code + script->length;
2255 op2 = js_GetOpcode(cx, script, regs.pc + JSOP_NAME_LENGTH);
2256 if (op2 == JSOP_TYPEOF) {
2257 PUSH_OPND(JSVAL_VOID);
2258 len = JSOP_NAME_LENGTH;
2259 DO_NEXT_OP(len);
2261 goto atom_not_defined;
2264 /* Take the slow path if prop was not found in a native object. */
2265 if (!OBJ_IS_NATIVE(obj) || !OBJ_IS_NATIVE(obj2)) {
2266 obj2->dropProperty(cx, prop);
2267 if (!obj->getProperty(cx, id, &rval))
2268 goto error;
2269 } else {
2270 sprop = (JSScopeProperty *)prop;
2271 do_native_get:
2272 NATIVE_GET(cx, obj, obj2, sprop, &rval);
2273 obj2->dropProperty(cx, (JSProperty *) sprop);
2276 do_push_rval:
2277 PUSH_OPND(rval);
2278 if (op == JSOP_CALLNAME)
2279 PUSH_OPND(OBJECT_TO_JSVAL(obj));
2281 END_CASE(JSOP_NAME)
2283 BEGIN_CASE(JSOP_UINT16)
2284 i = (jsint) GET_UINT16(regs.pc);
2285 rval = INT_TO_JSVAL(i);
2286 PUSH_OPND(rval);
2287 END_CASE(JSOP_UINT16)
2289 BEGIN_CASE(JSOP_UINT24)
2290 i = (jsint) GET_UINT24(regs.pc);
2291 rval = INT_TO_JSVAL(i);
2292 PUSH_OPND(rval);
2293 END_CASE(JSOP_UINT24)
2295 BEGIN_CASE(JSOP_INT8)
2296 i = GET_INT8(regs.pc);
2297 rval = INT_TO_JSVAL(i);
2298 PUSH_OPND(rval);
2299 END_CASE(JSOP_INT8)
2301 BEGIN_CASE(JSOP_INT32)
2302 i = GET_INT32(regs.pc);
2303 rval = INT_TO_JSVAL(i);
2304 PUSH_OPND(rval);
2305 END_CASE(JSOP_INT32)
2307 BEGIN_CASE(JSOP_INDEXBASE)
2309 * Here atoms can exceed script->atomMap.length as we use atoms
2310 * as a segment register for object literals as well.
2312 atoms += GET_INDEXBASE(regs.pc);
2313 END_CASE(JSOP_INDEXBASE)
2315 BEGIN_CASE(JSOP_INDEXBASE1)
2316 BEGIN_CASE(JSOP_INDEXBASE2)
2317 BEGIN_CASE(JSOP_INDEXBASE3)
2318 atoms += (op - JSOP_INDEXBASE1 + 1) << 16;
2319 END_CASE(JSOP_INDEXBASE3)
2321 BEGIN_CASE(JSOP_RESETBASE0)
2322 BEGIN_CASE(JSOP_RESETBASE)
2323 atoms = script->atomMap.vector;
2324 END_CASE(JSOP_RESETBASE)
2326 BEGIN_CASE(JSOP_DOUBLE)
2327 BEGIN_CASE(JSOP_STRING)
2328 LOAD_ATOM(0);
2329 PUSH_OPND(ATOM_KEY(atom));
2330 END_CASE(JSOP_DOUBLE)
2332 BEGIN_CASE(JSOP_OBJECT)
2333 LOAD_OBJECT(0);
2334 PUSH_OPND(OBJECT_TO_JSVAL(obj));
2335 END_CASE(JSOP_OBJECT)
2337 BEGIN_CASE(JSOP_REGEXP)
2339 JSObject *funobj;
2342 * Push a regexp object for the atom mapped by the bytecode at pc,
2343 * cloning the literal's regexp object if necessary, to simulate in
2344 * the pre-compile/execute-later case what ECMA specifies for the
2345 * compile-and-go case: that scanning each regexp literal creates
2346 * a single corresponding RegExp object.
2348 * To support pre-compilation transparently, we must handle the
2349 * case where a regexp object literal is used in a different global
2350 * at execution time from the global with which it was scanned at
2351 * compile time. We do this by re-wrapping the JSRegExp private
2352 * data struct with a cloned object having the right prototype and
2353 * parent, and having its own lastIndex property value storage.
2355 * Unlike JSOP_DEFFUN and other prolog bytecodes that may clone
2356 * literal objects, we don't want to pay a script prolog execution
2357 * price for all regexp literals in a script (many may not be used
2358 * by a particular execution of that script, depending on control
2359 * flow), so we initialize lazily here.
2361 * XXX This code is specific to regular expression objects. If we
2362 * need a similar op for other kinds of object literals, we should
2363 * push cloning down under JSObjectOps and reuse code here.
2365 index = GET_FULL_INDEX(0);
2366 JS_ASSERT(index < JS_SCRIPT_REGEXPS(script)->length);
2368 slot = index;
2369 if (fp->fun) {
2371 * We're in function code, not global or eval code (in eval
2372 * code, JSOP_REGEXP is never emitted). The cloned funobj
2373 * contains JS_SCRIPT_REGEXPS(script)->length reserved slots
2374 * for the cloned regexps; see fun_reserveSlots, jsfun.c.
2376 funobj = JSVAL_TO_OBJECT(fp->argv[-2]);
2377 slot += JSCLASS_RESERVED_SLOTS(&js_FunctionClass);
2378 if (script->upvarsOffset != 0)
2379 slot += JS_SCRIPT_UPVARS(script)->length;
2380 if (!JS_GetReservedSlot(cx, funobj, slot, &rval))
2381 goto error;
2382 if (JSVAL_IS_VOID(rval))
2383 rval = JSVAL_NULL;
2384 } else {
2386 * We're in global code. The code generator reserved a slot
2387 * for the regexp among script->nfixed slots. All such slots
2388 * are initialized to null, not void, for faster testing in
2389 * JSOP_*GVAR cases. To simplify index calculations we count
2390 * regexps in the reverse order down from script->nslots - 1.
2392 JS_ASSERT(slot < script->nfixed);
2393 slot = script->nfixed - slot - 1;
2394 rval = fp->slots[slot];
2395 #ifdef __GNUC__
2396 funobj = NULL; /* suppress bogus gcc warnings */
2397 #endif
2400 if (JSVAL_IS_NULL(rval)) {
2401 /* Compute the current global object in obj2. */
2402 obj2 = fp->scopeChain;
2403 while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL)
2404 obj2 = parent;
2407 * If obj's parent is not obj2, we must clone obj so that it
2408 * has the right parent, and therefore, the right prototype.
2410 * Yes, this means we assume that the correct RegExp.prototype
2411 * to which regexp instances (including literals) delegate can
2412 * be distinguished solely by the instance's parent, which was
2413 * set to the parent of the RegExp constructor function object
2414 * when the instance was created. In other words,
2416 * (/x/.__parent__ == RegExp.__parent__) implies
2417 * (/x/.__proto__ == RegExp.prototype)
2419 * (unless you assign a different object to RegExp.prototype
2420 * at runtime, in which case, ECMA doesn't specify operation,
2421 * and you get what you deserve).
2423 * This same coupling between instance parent and constructor
2424 * parent turns up everywhere (see jsobj.c's FindClassObject,
2425 * js_ConstructObject, and js_NewObject). It's fundamental to
2426 * the design of the language when you consider multiple global
2427 * objects and separate compilation and execution, even though
2428 * it is not specified fully in ECMA.
2430 JS_GET_SCRIPT_REGEXP(script, index, obj);
2431 if (OBJ_GET_PARENT(cx, obj) != obj2) {
2432 obj = js_CloneRegExpObject(cx, obj, obj2);
2433 if (!obj)
2434 goto error;
2436 rval = OBJECT_TO_JSVAL(obj);
2438 /* Store the regexp object value in its cloneIndex slot. */
2439 if (fp->fun) {
2440 if (!JS_SetReservedSlot(cx, funobj, slot, rval))
2441 goto error;
2442 } else {
2443 fp->slots[slot] = rval;
2447 PUSH_OPND(rval);
2449 END_CASE(JSOP_REGEXP)
2451 BEGIN_CASE(JSOP_ZERO)
2452 PUSH_OPND(JSVAL_ZERO);
2453 END_CASE(JSOP_ZERO)
2455 BEGIN_CASE(JSOP_ONE)
2456 PUSH_OPND(JSVAL_ONE);
2457 END_CASE(JSOP_ONE)
2459 BEGIN_CASE(JSOP_NULL)
2460 PUSH_OPND(JSVAL_NULL);
2461 END_CASE(JSOP_NULL)
2463 BEGIN_CASE(JSOP_FALSE)
2464 PUSH_OPND(JSVAL_FALSE);
2465 END_CASE(JSOP_FALSE)
2467 BEGIN_CASE(JSOP_TRUE)
2468 PUSH_OPND(JSVAL_TRUE);
2469 END_CASE(JSOP_TRUE)
2471 BEGIN_CASE(JSOP_TABLESWITCH)
2472 pc2 = regs.pc;
2473 len = GET_JUMP_OFFSET(pc2);
2476 * ECMAv2+ forbids conversion of discriminant, so we will skip to
2477 * the default case if the discriminant isn't already an int jsval.
2478 * (This opcode is emitted only for dense jsint-domain switches.)
2480 rval = POP_OPND();
2481 if (JSVAL_IS_INT(rval)) {
2482 i = JSVAL_TO_INT(rval);
2483 } else if (JSVAL_IS_DOUBLE(rval) && *JSVAL_TO_DOUBLE(rval) == 0) {
2484 /* Treat -0 (double) as 0. */
2485 i = 0;
2486 } else {
2487 DO_NEXT_OP(len);
2490 pc2 += JUMP_OFFSET_LEN;
2491 low = GET_JUMP_OFFSET(pc2);
2492 pc2 += JUMP_OFFSET_LEN;
2493 high = GET_JUMP_OFFSET(pc2);
2495 i -= low;
2496 if ((jsuint)i < (jsuint)(high - low + 1)) {
2497 pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i;
2498 off = (jsint) GET_JUMP_OFFSET(pc2);
2499 if (off)
2500 len = off;
2502 END_VARLEN_CASE
2504 BEGIN_CASE(JSOP_TABLESWITCHX)
2505 pc2 = regs.pc;
2506 len = GET_JUMPX_OFFSET(pc2);
2509 * ECMAv2+ forbids conversion of discriminant, so we will skip to
2510 * the default case if the discriminant isn't already an int jsval.
2511 * (This opcode is emitted only for dense jsint-domain switches.)
2513 rval = POP_OPND();
2514 if (JSVAL_IS_INT(rval)) {
2515 i = JSVAL_TO_INT(rval);
2516 } else if (JSVAL_IS_DOUBLE(rval) && *JSVAL_TO_DOUBLE(rval) == 0) {
2517 /* Treat -0 (double) as 0. */
2518 i = 0;
2519 } else {
2520 DO_NEXT_OP(len);
2523 pc2 += JUMPX_OFFSET_LEN;
2524 low = GET_JUMP_OFFSET(pc2);
2525 pc2 += JUMP_OFFSET_LEN;
2526 high = GET_JUMP_OFFSET(pc2);
2528 i -= low;
2529 if ((jsuint)i < (jsuint)(high - low + 1)) {
2530 pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i;
2531 off = (jsint) GET_JUMPX_OFFSET(pc2);
2532 if (off)
2533 len = off;
2535 END_VARLEN_CASE
2537 BEGIN_CASE(JSOP_LOOKUPSWITCHX)
2538 off = JUMPX_OFFSET_LEN;
2539 goto do_lookup_switch;
2541 BEGIN_CASE(JSOP_LOOKUPSWITCH)
2542 off = JUMP_OFFSET_LEN;
2544 do_lookup_switch:
2546 * JSOP_LOOKUPSWITCH and JSOP_LOOKUPSWITCHX are never used if
2547 * any atom index in it would exceed 64K limit.
2549 JS_ASSERT(atoms == script->atomMap.vector);
2550 pc2 = regs.pc;
2551 lval = POP_OPND();
2553 if (!JSVAL_IS_NUMBER(lval) &&
2554 !JSVAL_IS_STRING(lval) &&
2555 !JSVAL_IS_BOOLEAN(lval)) {
2556 goto end_lookup_switch;
2559 pc2 += off;
2560 npairs = (jsint) GET_UINT16(pc2);
2561 pc2 += UINT16_LEN;
2562 JS_ASSERT(npairs); /* empty switch uses JSOP_TABLESWITCH */
2564 #define SEARCH_PAIRS(MATCH_CODE) \
2565 for (;;) { \
2566 JS_ASSERT(GET_INDEX(pc2) < script->atomMap.length); \
2567 atom = atoms[GET_INDEX(pc2)]; \
2568 rval = ATOM_KEY(atom); \
2569 MATCH_CODE \
2570 pc2 += INDEX_LEN; \
2571 if (match) \
2572 break; \
2573 pc2 += off; \
2574 if (--npairs == 0) { \
2575 pc2 = regs.pc; \
2576 break; \
2579 if (JSVAL_IS_STRING(lval)) {
2580 str = JSVAL_TO_STRING(lval);
2581 SEARCH_PAIRS(
2582 match = (JSVAL_IS_STRING(rval) &&
2583 ((str2 = JSVAL_TO_STRING(rval)) == str ||
2584 js_EqualStrings(str2, str)));
2586 } else if (JSVAL_IS_DOUBLE(lval)) {
2587 d = *JSVAL_TO_DOUBLE(lval);
2588 SEARCH_PAIRS(
2589 match = (JSVAL_IS_DOUBLE(rval) &&
2590 *JSVAL_TO_DOUBLE(rval) == d);
2592 } else {
2593 SEARCH_PAIRS(
2594 match = (lval == rval);
2597 #undef SEARCH_PAIRS
2599 end_lookup_switch:
2600 len = (op == JSOP_LOOKUPSWITCH)
2601 ? GET_JUMP_OFFSET(pc2)
2602 : GET_JUMPX_OFFSET(pc2);
2603 END_VARLEN_CASE
2605 BEGIN_CASE(JSOP_TRAP)
2607 JSTrapStatus status;
2609 status = JS_HandleTrap(cx, script, regs.pc, &rval);
2610 switch (status) {
2611 case JSTRAP_ERROR:
2612 goto error;
2613 case JSTRAP_RETURN:
2614 fp->rval = rval;
2615 ok = JS_TRUE;
2616 goto forced_return;
2617 case JSTRAP_THROW:
2618 cx->throwing = JS_TRUE;
2619 cx->exception = rval;
2620 goto error;
2621 default:;
2622 break;
2624 JS_ASSERT(status == JSTRAP_CONTINUE);
2625 CHECK_INTERRUPT_HANDLER();
2626 JS_ASSERT(JSVAL_IS_INT(rval));
2627 op = (JSOp) JSVAL_TO_INT(rval);
2628 JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
2629 DO_OP();
2632 BEGIN_CASE(JSOP_ARGUMENTS)
2633 if (!js_GetArgsValue(cx, fp, &rval))
2634 goto error;
2635 PUSH_OPND(rval);
2636 END_CASE(JSOP_ARGUMENTS)
2638 BEGIN_CASE(JSOP_ARGSUB)
2639 id = INT_TO_JSID(GET_ARGNO(regs.pc));
2640 if (!js_GetArgsProperty(cx, fp, id, &rval))
2641 goto error;
2642 PUSH_OPND(rval);
2643 END_CASE(JSOP_ARGSUB)
2645 BEGIN_CASE(JSOP_ARGCNT)
2646 id = ATOM_TO_JSID(rt->atomState.lengthAtom);
2647 if (!js_GetArgsProperty(cx, fp, id, &rval))
2648 goto error;
2649 PUSH_OPND(rval);
2650 END_CASE(JSOP_ARGCNT)
2652 BEGIN_CASE(JSOP_GETARG)
2653 BEGIN_CASE(JSOP_CALLARG)
2654 slot = GET_ARGNO(regs.pc);
2655 JS_ASSERT(slot < fp->fun->nargs);
2656 METER_SLOT_OP(op, slot);
2657 PUSH_OPND(fp->argv[slot]);
2658 if (op == JSOP_CALLARG)
2659 PUSH_OPND(JSVAL_NULL);
2660 END_CASE(JSOP_GETARG)
2662 BEGIN_CASE(JSOP_SETARG)
2663 slot = GET_ARGNO(regs.pc);
2664 JS_ASSERT(slot < fp->fun->nargs);
2665 METER_SLOT_OP(op, slot);
2666 vp = &fp->argv[slot];
2667 *vp = FETCH_OPND(-1);
2668 END_SET_CASE(JSOP_SETARG)
2670 BEGIN_CASE(JSOP_GETLOCAL)
2671 slot = GET_SLOTNO(regs.pc);
2672 JS_ASSERT(slot < script->nslots);
2673 PUSH_OPND(fp->slots[slot]);
2674 END_CASE(JSOP_GETLOCAL)
2676 BEGIN_CASE(JSOP_CALLLOCAL)
2677 slot = GET_SLOTNO(regs.pc);
2678 JS_ASSERT(slot < script->nslots);
2679 PUSH_OPND(fp->slots[slot]);
2680 PUSH_OPND(JSVAL_NULL);
2681 END_CASE(JSOP_CALLLOCAL)
2683 BEGIN_CASE(JSOP_SETLOCAL)
2684 slot = GET_SLOTNO(regs.pc);
2685 JS_ASSERT(slot < script->nslots);
2686 vp = &fp->slots[slot];
2687 *vp = FETCH_OPND(-1);
2688 END_SET_CASE(JSOP_SETLOCAL)
2690 BEGIN_CASE(JSOP_GETUPVAR)
2691 BEGIN_CASE(JSOP_CALLUPVAR)
2693 JSUpvarArray *uva = JS_SCRIPT_UPVARS(script);
2695 index = GET_UINT16(regs.pc);
2696 JS_ASSERT(index < uva->length);
2698 rval = js_GetUpvar(cx, script->staticLevel, uva->vector[index]);
2699 PUSH_OPND(rval);
2701 if (op == JSOP_CALLUPVAR)
2702 PUSH_OPND(JSVAL_NULL);
2704 END_CASE(JSOP_GETUPVAR)
2706 BEGIN_CASE(JSOP_GETUPVAR_DBG)
2707 BEGIN_CASE(JSOP_CALLUPVAR_DBG)
2708 fun = fp->fun;
2709 JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
2710 JS_ASSERT(fun->u.i.wrapper);
2712 /* Scope for tempPool mark and local names allocation in it. */
2714 void *mark = JS_ARENA_MARK(&cx->tempPool);
2715 jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
2716 if (!names)
2717 goto error;
2719 index = fun->countArgsAndVars() + GET_UINT16(regs.pc);
2720 atom = JS_LOCAL_NAME_TO_ATOM(names[index]);
2721 id = ATOM_TO_JSID(atom);
2723 ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
2724 JS_ARENA_RELEASE(&cx->tempPool, mark);
2725 if (!ok)
2726 goto error;
2729 if (!prop)
2730 goto atom_not_defined;
2732 /* Minimize footprint with generic code instead of NATIVE_GET. */
2733 obj2->dropProperty(cx, prop);
2734 vp = regs.sp;
2735 PUSH_OPND(JSVAL_NULL);
2736 if (!obj->getProperty(cx, id, vp))
2737 goto error;
2739 if (op == JSOP_CALLUPVAR_DBG)
2740 PUSH_OPND(JSVAL_NULL);
2741 END_CASE(JSOP_GETUPVAR_DBG)
2743 BEGIN_CASE(JSOP_GETDSLOT)
2744 BEGIN_CASE(JSOP_CALLDSLOT)
2745 JS_ASSERT(fp->argv);
2746 obj = JSVAL_TO_OBJECT(fp->argv[-2]);
2747 JS_ASSERT(obj);
2748 JS_ASSERT(obj->dslots);
2750 index = GET_UINT16(regs.pc);
2751 JS_ASSERT(JS_INITIAL_NSLOTS + index < jsatomid(obj->dslots[-1]));
2752 JS_ASSERT_IF(OBJ_SCOPE(obj)->object == obj,
2753 JS_INITIAL_NSLOTS + index < OBJ_SCOPE(obj)->freeslot);
2755 PUSH_OPND(obj->dslots[index]);
2756 if (op == JSOP_CALLDSLOT)
2757 PUSH_OPND(JSVAL_NULL);
2758 END_CASE(JSOP_GETDSLOT)
2760 BEGIN_CASE(JSOP_GETGVAR)
2761 BEGIN_CASE(JSOP_CALLGVAR)
2762 slot = GET_SLOTNO(regs.pc);
2763 JS_ASSERT(slot < GlobalVarCount(fp));
2764 METER_SLOT_OP(op, slot);
2765 lval = fp->slots[slot];
2766 if (JSVAL_IS_NULL(lval)) {
2767 op = (op == JSOP_GETGVAR) ? JSOP_NAME : JSOP_CALLNAME;
2768 DO_OP();
2770 obj = fp->varobj;
2771 slot = JSVAL_TO_INT(lval);
2772 rval = OBJ_GET_SLOT(cx, obj, slot);
2773 PUSH_OPND(rval);
2774 if (op == JSOP_CALLGVAR)
2775 PUSH_OPND(OBJECT_TO_JSVAL(obj));
2776 END_CASE(JSOP_GETGVAR)
2778 BEGIN_CASE(JSOP_SETGVAR)
2779 slot = GET_SLOTNO(regs.pc);
2780 JS_ASSERT(slot < GlobalVarCount(fp));
2781 METER_SLOT_OP(op, slot);
2782 rval = FETCH_OPND(-1);
2783 obj = fp->varobj;
2784 lval = fp->slots[slot];
2785 if (JSVAL_IS_NULL(lval)) {
2787 * Inline-clone and deoptimize JSOP_SETNAME code here because
2788 * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval]
2789 * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME.
2791 #ifdef JS_TRACER
2792 if (TRACE_RECORDER(cx))
2793 js_AbortRecording(cx, "SETGVAR with NULL slot");
2794 #endif
2795 LOAD_ATOM(0);
2796 id = ATOM_TO_JSID(atom);
2797 if (!obj->setProperty(cx, id, &rval))
2798 goto error;
2799 } else {
2800 slot = JSVAL_TO_INT(lval);
2801 JS_LOCK_OBJ(cx, obj);
2802 LOCKED_OBJ_WRITE_SLOT(cx, obj, slot, rval);
2803 JS_UNLOCK_OBJ(cx, obj);
2805 END_SET_CASE(JSOP_SETGVAR)
2807 BEGIN_CASE(JSOP_DEFCONST)
2808 BEGIN_CASE(JSOP_DEFVAR)
2809 index = GET_INDEX(regs.pc);
2810 atom = atoms[index];
2813 * index is relative to atoms at this point but for global var
2814 * code below we need the absolute value.
2816 index += atoms - script->atomMap.vector;
2817 obj = fp->varobj;
2818 attrs = JSPROP_ENUMERATE;
2819 if (!(fp->flags & JSFRAME_EVAL))
2820 attrs |= JSPROP_PERMANENT;
2821 if (op == JSOP_DEFCONST)
2822 attrs |= JSPROP_READONLY;
2824 /* Lookup id in order to check for redeclaration problems. */
2825 id = ATOM_TO_JSID(atom);
2826 prop = NULL;
2827 if (!js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop))
2828 goto error;
2830 /* Bind a variable only if it's not yet defined. */
2831 if (!prop) {
2832 if (!obj->defineProperty(cx, id, JSVAL_VOID, JS_PropertyStub, JS_PropertyStub,
2833 attrs, &prop)) {
2834 goto error;
2836 JS_ASSERT(prop);
2837 obj2 = obj;
2841 * Try to optimize a property we either just created, or found
2842 * directly in the global object, that is permanent, has a slot,
2843 * and has stub getter and setter, into a "fast global" accessed
2844 * by the JSOP_*GVAR opcodes.
2846 if (!fp->fun &&
2847 index < GlobalVarCount(fp) &&
2848 obj2 == obj &&
2849 OBJ_IS_NATIVE(obj)) {
2850 sprop = (JSScopeProperty *) prop;
2851 if ((sprop->attrs & JSPROP_PERMANENT) &&
2852 SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) &&
2853 SPROP_HAS_STUB_GETTER(sprop) &&
2854 SPROP_HAS_STUB_SETTER(sprop)) {
2856 * Fast globals use frame variables to map the global
2857 * name's atom index to the permanent fp->varobj slot
2858 * number, tagged as a jsval. The atom index for the
2859 * global's name literal is identical to its variable
2860 * index.
2862 fp->slots[index] = INT_TO_JSVAL(sprop->slot);
2866 obj2->dropProperty(cx, prop);
2867 END_CASE(JSOP_DEFVAR)
2869 BEGIN_CASE(JSOP_DEFFUN)
2871 JSPropertyOp getter, setter;
2872 bool doSet;
2873 JSObject *pobj;
2874 JSProperty *prop;
2875 uint32 old;
2878 * A top-level function defined in Global or Eval code (see
2879 * ECMA-262 Ed. 3), or else a SpiderMonkey extension: a named
2880 * function statement in a compound statement (not at the top
2881 * statement level of global code, or at the top level of a
2882 * function body).
2884 LOAD_FUNCTION(0);
2885 obj = FUN_OBJECT(fun);
2887 if (FUN_NULL_CLOSURE(fun)) {
2889 * Even a null closure needs a parent for principals finding.
2890 * FIXME: bug 476950, although debugger users may also demand
2891 * some kind of scope link for debugger-assisted eval-in-frame.
2893 obj2 = fp->scopeChain;
2894 } else {
2895 JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
2898 * Inline js_GetScopeChain a bit to optimize for the case of a
2899 * top-level function.
2901 if (!fp->blockChain) {
2902 obj2 = fp->scopeChain;
2903 } else {
2904 obj2 = js_GetScopeChain(cx, fp);
2905 if (!obj2)
2906 goto error;
2911 * If static link is not current scope, clone fun's object to link
2912 * to the current scope via parent. We do this to enable sharing of
2913 * compiled functions among multiple equivalent scopes, amortizing
2914 * the cost of compilation over a number of executions. Examples
2915 * include XUL scripts and event handlers shared among Firefox or
2916 * other Mozilla app chrome windows, and user-defined JS functions
2917 * precompiled and then shared among requests in server-side JS.
2919 if (OBJ_GET_PARENT(cx, obj) != obj2) {
2920 obj = js_CloneFunctionObject(cx, fun, obj2);
2921 if (!obj)
2922 goto error;
2926 * Protect obj from any GC hiding below JSObject::setProperty or
2927 * JSObject::defineProperty. All paths from here must flow through
2928 * the "Restore fp->scopeChain" code below the
2929 * parent->defineProperty call.
2931 MUST_FLOW_THROUGH("restore_scope");
2932 fp->scopeChain = obj;
2934 rval = OBJECT_TO_JSVAL(obj);
2937 * ECMA requires functions defined when entering Eval code to be
2938 * impermanent.
2940 attrs = (fp->flags & JSFRAME_EVAL)
2941 ? JSPROP_ENUMERATE
2942 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
2945 * Load function flags that are also property attributes. Getters
2946 * and setters do not need a slot, their value is stored elsewhere
2947 * in the property itself, not in obj slots.
2949 setter = getter = JS_PropertyStub;
2950 flags = JSFUN_GSFLAG2ATTR(fun->flags);
2951 if (flags) {
2952 /* Function cannot be both getter a setter. */
2953 JS_ASSERT(flags == JSPROP_GETTER || flags == JSPROP_SETTER);
2954 attrs |= flags | JSPROP_SHARED;
2955 rval = JSVAL_VOID;
2956 if (flags == JSPROP_GETTER)
2957 getter = js_CastAsPropertyOp(obj);
2958 else
2959 setter = js_CastAsPropertyOp(obj);
2963 * We define the function as a property of the variable object and
2964 * not the current scope chain even for the case of function
2965 * expression statements and functions defined by eval inside let
2966 * or with blocks.
2968 parent = fp->varobj;
2969 JS_ASSERT(parent);
2972 * Check for a const property of the same name -- or any kind
2973 * of property if executing with the strict option. We check
2974 * here at runtime as well as at compile-time, to handle eval
2975 * as well as multiple HTML script tags.
2977 id = ATOM_TO_JSID(fun->atom);
2978 prop = NULL;
2979 ok = js_CheckRedeclaration(cx, parent, id, attrs, &pobj, &prop);
2980 if (!ok)
2981 goto restore_scope;
2984 * We deviate from 10.1.2 in ECMA 262 v3 and under eval use for
2985 * function declarations JSObject::setProperty, not
2986 * JSObject::defineProperty, to preserve the JSOP_PERMANENT
2987 * attribute of existing properties and make sure that such
2988 * properties cannot be deleted.
2990 * We also use JSObject::setProperty for the existing properties of
2991 * Call objects with matching attributes to preserve the native
2992 * getters and setters that store the value of the property in the
2993 * interpreter frame, see bug 467495.
2995 doSet = (attrs == JSPROP_ENUMERATE);
2996 JS_ASSERT_IF(doSet, fp->flags & JSFRAME_EVAL);
2997 if (prop) {
2998 if (parent == pobj &&
2999 OBJ_GET_CLASS(cx, parent) == &js_CallClass &&
3000 (old = ((JSScopeProperty *) prop)->attrs,
3001 !(old & (JSPROP_GETTER|JSPROP_SETTER)) &&
3002 (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs)) {
3004 * js_CheckRedeclaration must reject attempts to add a
3005 * getter or setter to an existing property without a
3006 * getter or setter.
3008 JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT)));
3009 JS_ASSERT(!(old & JSPROP_READONLY));
3010 doSet = JS_TRUE;
3012 pobj->dropProperty(cx, prop);
3014 ok = doSet
3015 ? parent->setProperty(cx, id, &rval)
3016 : parent->defineProperty(cx, id, rval, getter, setter, attrs, NULL);
3018 restore_scope:
3019 /* Restore fp->scopeChain now that obj is defined in fp->varobj. */
3020 fp->scopeChain = obj2;
3021 if (!ok) {
3022 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
3023 goto error;
3026 END_CASE(JSOP_DEFFUN)
3028 BEGIN_CASE(JSOP_DEFFUN_FC)
3029 BEGIN_CASE(JSOP_DEFFUN_DBGFC)
3030 LOAD_FUNCTION(0);
3032 obj = (op == JSOP_DEFFUN_FC)
3033 ? js_NewFlatClosure(cx, fun)
3034 : js_NewDebuggableFlatClosure(cx, fun);
3035 if (!obj)
3036 goto error;
3037 rval = OBJECT_TO_JSVAL(obj);
3039 attrs = (fp->flags & JSFRAME_EVAL)
3040 ? JSPROP_ENUMERATE
3041 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
3043 flags = JSFUN_GSFLAG2ATTR(fun->flags);
3044 if (flags) {
3045 attrs |= flags | JSPROP_SHARED;
3046 rval = JSVAL_VOID;
3049 parent = fp->varobj;
3050 JS_ASSERT(parent);
3052 id = ATOM_TO_JSID(fun->atom);
3053 ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL);
3054 if (ok) {
3055 if (attrs == JSPROP_ENUMERATE) {
3056 JS_ASSERT(fp->flags & JSFRAME_EVAL);
3057 ok = parent->setProperty(cx, id, &rval);
3058 } else {
3059 JS_ASSERT(attrs & JSPROP_PERMANENT);
3061 ok = parent->defineProperty(cx, id, rval,
3062 (flags & JSPROP_GETTER)
3063 ? JS_EXTENSION (JSPropertyOp) obj
3064 : JS_PropertyStub,
3065 (flags & JSPROP_SETTER)
3066 ? JS_EXTENSION (JSPropertyOp) obj
3067 : JS_PropertyStub,
3068 attrs,
3069 NULL);
3073 if (!ok) {
3074 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
3075 goto error;
3077 END_CASE(JSOP_DEFFUN_FC)
3079 BEGIN_CASE(JSOP_DEFLOCALFUN)
3081 * Define a local function (i.e., one nested at the top level of
3082 * another function), parented by the current scope chain, stored
3083 * in a local variable slot that the compiler allocated. This is
3084 * an optimization over JSOP_DEFFUN that avoids requiring a call
3085 * object for the outer function's activation.
3087 LOAD_FUNCTION(SLOTNO_LEN);
3088 JS_ASSERT(FUN_INTERPRETED(fun));
3089 JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
3090 obj = FUN_OBJECT(fun);
3092 if (FUN_NULL_CLOSURE(fun)) {
3093 obj = js_CloneFunctionObject(cx, fun, fp->scopeChain);
3094 if (!obj)
3095 goto error;
3096 } else {
3097 parent = js_GetScopeChain(cx, fp);
3098 if (!parent)
3099 goto error;
3101 if (OBJ_GET_PARENT(cx, obj) != parent) {
3102 #ifdef JS_TRACER
3103 if (TRACE_RECORDER(cx))
3104 js_AbortRecording(cx, "DEFLOCALFUN for closure");
3105 #endif
3106 obj = js_CloneFunctionObject(cx, fun, parent);
3107 if (!obj)
3108 goto error;
3112 slot = GET_SLOTNO(regs.pc);
3113 TRACE_2(DefLocalFunSetSlot, slot, obj);
3115 fp->slots[slot] = OBJECT_TO_JSVAL(obj);
3116 END_CASE(JSOP_DEFLOCALFUN)
3118 BEGIN_CASE(JSOP_DEFLOCALFUN_FC)
3119 LOAD_FUNCTION(SLOTNO_LEN);
3121 obj = js_NewFlatClosure(cx, fun);
3122 if (!obj)
3123 goto error;
3125 slot = GET_SLOTNO(regs.pc);
3126 TRACE_2(DefLocalFunSetSlot, slot, obj);
3128 fp->slots[slot] = OBJECT_TO_JSVAL(obj);
3129 END_CASE(JSOP_DEFLOCALFUN_FC)
3131 BEGIN_CASE(JSOP_DEFLOCALFUN_DBGFC)
3132 LOAD_FUNCTION(SLOTNO_LEN);
3134 obj = js_NewDebuggableFlatClosure(cx, fun);
3135 if (!obj)
3136 goto error;
3138 slot = GET_SLOTNO(regs.pc);
3139 fp->slots[slot] = OBJECT_TO_JSVAL(obj);
3140 END_CASE(JSOP_DEFLOCALFUN_DBGFC)
3142 BEGIN_CASE(JSOP_LAMBDA)
3143 /* Load the specified function object literal. */
3144 LOAD_FUNCTION(0);
3145 obj = FUN_OBJECT(fun);
3147 if (FUN_NULL_CLOSURE(fun)) {
3148 obj = js_CloneFunctionObject(cx, fun, fp->scopeChain);
3149 if (!obj)
3150 goto error;
3151 } else {
3152 parent = js_GetScopeChain(cx, fp);
3153 if (!parent)
3154 goto error;
3157 * FIXME: bug 471214, Cloning here even when the compiler saw
3158 * the right parent is wasteful but we don't fully support
3159 * joined function objects, yet.
3161 obj = js_CloneFunctionObject(cx, fun, parent);
3162 if (!obj)
3163 goto error;
3166 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3167 END_CASE(JSOP_LAMBDA)
3169 BEGIN_CASE(JSOP_LAMBDA_FC)
3170 LOAD_FUNCTION(0);
3172 obj = js_NewFlatClosure(cx, fun);
3173 if (!obj)
3174 goto error;
3176 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3177 END_CASE(JSOP_LAMBDA_FC)
3179 BEGIN_CASE(JSOP_LAMBDA_DBGFC)
3180 LOAD_FUNCTION(0);
3182 obj = js_NewDebuggableFlatClosure(cx, fun);
3183 if (!obj)
3184 goto error;
3186 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3187 END_CASE(JSOP_LAMBDA_DBGFC)
3189 BEGIN_CASE(JSOP_CALLEE)
3190 PUSH_OPND(fp->argv[-2]);
3191 END_CASE(JSOP_CALLEE)
3193 #if JS_HAS_GETTER_SETTER
3194 BEGIN_CASE(JSOP_GETTER)
3195 BEGIN_CASE(JSOP_SETTER)
3196 do_getter_setter:
3197 op2 = (JSOp) *++regs.pc;
3198 switch (op2) {
3199 case JSOP_INDEXBASE:
3200 atoms += GET_INDEXBASE(regs.pc);
3201 regs.pc += JSOP_INDEXBASE_LENGTH - 1;
3202 goto do_getter_setter;
3203 case JSOP_INDEXBASE1:
3204 case JSOP_INDEXBASE2:
3205 case JSOP_INDEXBASE3:
3206 atoms += (op2 - JSOP_INDEXBASE1 + 1) << 16;
3207 goto do_getter_setter;
3209 case JSOP_SETNAME:
3210 case JSOP_SETPROP:
3211 LOAD_ATOM(0);
3212 id = ATOM_TO_JSID(atom);
3213 rval = FETCH_OPND(-1);
3214 i = -1;
3215 goto gs_pop_lval;
3217 case JSOP_SETELEM:
3218 rval = FETCH_OPND(-1);
3219 id = 0;
3220 i = -2;
3221 gs_pop_lval:
3222 FETCH_OBJECT(cx, i - 1, lval, obj);
3223 break;
3225 case JSOP_INITPROP:
3226 JS_ASSERT(regs.sp - StackBase(fp) >= 2);
3227 rval = FETCH_OPND(-1);
3228 i = -1;
3229 LOAD_ATOM(0);
3230 id = ATOM_TO_JSID(atom);
3231 goto gs_get_lval;
3233 default:
3234 JS_ASSERT(op2 == JSOP_INITELEM);
3236 JS_ASSERT(regs.sp - StackBase(fp) >= 3);
3237 rval = FETCH_OPND(-1);
3238 id = 0;
3239 i = -2;
3240 gs_get_lval:
3241 lval = FETCH_OPND(i-1);
3242 JS_ASSERT(JSVAL_IS_OBJECT(lval));
3243 obj = JSVAL_TO_OBJECT(lval);
3244 break;
3247 /* Ensure that id has a type suitable for use with obj. */
3248 if (id == 0)
3249 FETCH_ELEMENT_ID(obj, i, id);
3251 if (JS_TypeOfValue(cx, rval) != JSTYPE_FUNCTION) {
3252 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3253 JSMSG_BAD_GETTER_OR_SETTER,
3254 (op == JSOP_GETTER)
3255 ? js_getter_str
3256 : js_setter_str);
3257 goto error;
3261 * Getters and setters are just like watchpoints from an access
3262 * control point of view.
3264 if (!obj->checkAccess(cx, id, JSACC_WATCH, &rtmp, &attrs))
3265 goto error;
3267 if (op == JSOP_GETTER) {
3268 getter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval);
3269 setter = JS_PropertyStub;
3270 attrs = JSPROP_GETTER;
3271 } else {
3272 getter = JS_PropertyStub;
3273 setter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval);
3274 attrs = JSPROP_SETTER;
3276 attrs |= JSPROP_ENUMERATE | JSPROP_SHARED;
3278 /* Check for a readonly or permanent property of the same name. */
3279 if (!js_CheckRedeclaration(cx, obj, id, attrs, NULL, NULL))
3280 goto error;
3282 if (!obj->defineProperty(cx, id, JSVAL_VOID, getter, setter, attrs, NULL))
3283 goto error;
3285 regs.sp += i;
3286 if (js_CodeSpec[op2].ndefs)
3287 STORE_OPND(-1, rval);
3288 len = js_CodeSpec[op2].length;
3289 DO_NEXT_OP(len);
3290 #endif /* JS_HAS_GETTER_SETTER */
3292 BEGIN_CASE(JSOP_HOLE)
3293 PUSH_OPND(JSVAL_HOLE);
3294 END_CASE(JSOP_HOLE)
3296 BEGIN_CASE(JSOP_NEWARRAY)
3297 len = GET_UINT16(regs.pc);
3298 cx->fp->assertValidStackDepth(len);
3299 obj = js_NewArrayObject(cx, len, regs.sp - len, JS_TRUE);
3300 if (!obj)
3301 goto error;
3302 regs.sp -= len - 1;
3303 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
3304 END_CASE(JSOP_NEWARRAY)
3306 BEGIN_CASE(JSOP_NEWINIT)
3307 i = GET_INT8(regs.pc);
3308 JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
3309 obj = (i == JSProto_Array)
3310 ? js_NewArrayObject(cx, 0, NULL)
3311 : js_NewObject(cx, &js_ObjectClass, NULL, NULL);
3312 if (!obj)
3313 goto error;
3314 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3315 fp->sharpDepth++;
3316 CHECK_INTERRUPT_HANDLER();
3317 END_CASE(JSOP_NEWINIT)
3319 BEGIN_CASE(JSOP_ENDINIT)
3320 if (--fp->sharpDepth == 0)
3321 fp->sharpArray = NULL;
3323 /* Re-set the newborn root to the top of this object tree. */
3324 JS_ASSERT(regs.sp - StackBase(fp) >= 1);
3325 lval = FETCH_OPND(-1);
3326 JS_ASSERT(JSVAL_IS_OBJECT(lval));
3327 cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(lval);
3328 END_CASE(JSOP_ENDINIT)
3330 BEGIN_CASE(JSOP_INITPROP)
3331 /* Load the property's initial value into rval. */
3332 JS_ASSERT(regs.sp - StackBase(fp) >= 2);
3333 rval = FETCH_OPND(-1);
3335 /* Load the object being initialized into lval/obj. */
3336 lval = FETCH_OPND(-2);
3337 obj = JSVAL_TO_OBJECT(lval);
3338 JS_ASSERT(OBJ_IS_NATIVE(obj));
3339 JS_ASSERT(!OBJ_GET_CLASS(cx, obj)->reserveSlots);
3340 JS_ASSERT(!(LOCKED_OBJ_GET_CLASS(obj)->flags &
3341 JSCLASS_SHARE_ALL_PROPERTIES));
3343 do {
3344 JSScope *scope;
3345 uint32 kshape;
3346 JSPropertyCache *cache;
3347 JSPropCacheEntry *entry;
3349 JS_LOCK_OBJ(cx, obj);
3350 scope = OBJ_SCOPE(obj);
3351 JS_ASSERT(!scope->sealed());
3352 kshape = scope->shape;
3353 cache = &JS_PROPERTY_CACHE(cx);
3354 entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)];
3355 PCMETER(cache->pctestentry = entry);
3356 PCMETER(cache->tests++);
3357 PCMETER(cache->initests++);
3359 if (entry->kpc == regs.pc &&
3360 entry->kshape == kshape &&
3361 PCVCAP_SHAPE(entry->vcap) == rt->protoHazardShape) {
3362 JS_ASSERT(PCVCAP_TAG(entry->vcap) == 0);
3364 PCMETER(cache->pchits++);
3365 PCMETER(cache->inipchits++);
3367 JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
3368 sprop = PCVAL_TO_SPROP(entry->vword);
3369 JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
3372 * If this property has a non-stub setter, it must be
3373 * __proto__, __parent__, or another "shared prototype"
3374 * built-in. Force a miss to save code size here and let
3375 * the standard code path take care of business.
3377 if (!SPROP_HAS_STUB_SETTER(sprop))
3378 goto do_initprop_miss;
3380 if (!scope->owned()) {
3381 scope = js_GetMutableScope(cx, obj);
3382 if (!scope) {
3383 JS_UNLOCK_OBJ(cx, obj);
3384 goto error;
3389 * Detect a repeated property name and force a miss to
3390 * share the strict warning code and cope with complexity
3391 * managed by JSScope::add.
3393 if (sprop->parent != scope->lastProp)
3394 goto do_initprop_miss;
3397 * Otherwise this entry must be for a direct property of
3398 * obj, not a proto-property, and there cannot have been
3399 * any deletions of prior properties.
3401 JS_ASSERT(!scope->hadMiddleDelete());
3402 JS_ASSERT_IF(scope->table, !scope->has(sprop));
3404 slot = sprop->slot;
3405 JS_ASSERT(slot == scope->freeslot);
3406 if (slot < STOBJ_NSLOTS(obj)) {
3407 ++scope->freeslot;
3408 } else {
3409 if (!js_AllocSlot(cx, obj, &slot)) {
3410 JS_UNLOCK_SCOPE(cx, scope);
3411 goto error;
3413 JS_ASSERT(slot == sprop->slot);
3416 JS_ASSERT(!scope->lastProp ||
3417 scope->shape == scope->lastProp->shape);
3418 if (scope->table) {
3419 JSScopeProperty *sprop2 =
3420 scope->add(cx, sprop->id,
3421 sprop->getter, sprop->setter,
3422 slot, sprop->attrs,
3423 sprop->flags, sprop->shortid);
3424 if (!sprop2) {
3425 js_FreeSlot(cx, obj, slot);
3426 JS_UNLOCK_SCOPE(cx, scope);
3427 goto error;
3429 JS_ASSERT(sprop2 == sprop);
3430 } else {
3431 JS_ASSERT(scope->owned());
3432 js_LeaveTraceIfGlobalObject(cx, obj);
3433 scope->shape = sprop->shape;
3434 ++scope->entryCount;
3435 scope->lastProp = sprop;
3438 LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, rval);
3439 TRACE_2(SetPropHit, entry, sprop);
3440 LOCKED_OBJ_SET_SLOT(obj, slot, rval);
3441 JS_UNLOCK_SCOPE(cx, scope);
3442 break;
3445 do_initprop_miss:
3446 PCMETER(cache->inipcmisses++);
3447 JS_UNLOCK_SCOPE(cx, scope);
3449 /* Get the immediate property name into id. */
3450 LOAD_ATOM(0);
3451 id = ATOM_TO_JSID(atom);
3453 /* Set the property named by obj[id] to rval. */
3454 if (!js_CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER,
3455 NULL, NULL)) {
3456 goto error;
3459 if (!(JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom)
3460 ? js_SetPropertyHelper(cx, obj, id, true, &rval)
3461 : js_DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
3462 JSPROP_ENUMERATE, 0, 0, NULL,
3463 JSDNP_CACHE_RESULT)))
3464 goto error;
3465 } while (0);
3467 /* Common tail for property cache hit and miss cases. */
3468 regs.sp--;
3469 END_CASE(JSOP_INITPROP);
3471 BEGIN_CASE(JSOP_INITELEM)
3472 /* Pop the element's value into rval. */
3473 JS_ASSERT(regs.sp - StackBase(fp) >= 3);
3474 rval = FETCH_OPND(-1);
3476 /* Find the object being initialized at top of stack. */
3477 lval = FETCH_OPND(-3);
3478 JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval));
3479 obj = JSVAL_TO_OBJECT(lval);
3481 /* Fetch id now that we have obj. */
3482 FETCH_ELEMENT_ID(obj, -2, id);
3485 * Check for property redeclaration strict warning (we may be in
3486 * an object initialiser, not an array initialiser).
3488 if (!js_CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER, NULL, NULL))
3489 goto error;
3492 * If rval is a hole, do not call JSObject::defineProperty. In this case,
3493 * obj must be an array, so if the current op is the last element
3494 * initialiser, set the array length to one greater than id.
3496 if (rval == JSVAL_HOLE) {
3497 JS_ASSERT(OBJ_IS_ARRAY(cx, obj));
3498 JS_ASSERT(JSID_IS_INT(id));
3499 JS_ASSERT(jsuint(JSID_TO_INT(id)) < JS_ARGS_LENGTH_MAX);
3500 if (js_GetOpcode(cx, script, regs.pc + JSOP_INITELEM_LENGTH) == JSOP_ENDINIT &&
3501 !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1))) {
3502 goto error;
3504 } else {
3505 if (!obj->defineProperty(cx, id, rval, NULL, NULL, JSPROP_ENUMERATE, NULL))
3506 goto error;
3508 regs.sp -= 2;
3509 END_CASE(JSOP_INITELEM)
3511 #if JS_HAS_SHARP_VARS
3512 BEGIN_CASE(JSOP_DEFSHARP)
3513 obj = fp->sharpArray;
3514 if (!obj) {
3515 obj = js_NewArrayObject(cx, 0, NULL);
3516 if (!obj)
3517 goto error;
3518 fp->sharpArray = obj;
3520 i = (jsint) GET_UINT16(regs.pc);
3521 id = INT_TO_JSID(i);
3522 rval = FETCH_OPND(-1);
3523 if (JSVAL_IS_PRIMITIVE(rval)) {
3524 char numBuf[12];
3525 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
3526 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3527 JSMSG_BAD_SHARP_DEF, numBuf);
3528 goto error;
3530 if (!obj->defineProperty(cx, id, rval, NULL, NULL, JSPROP_ENUMERATE, NULL))
3531 goto error;
3532 END_CASE(JSOP_DEFSHARP)
3534 BEGIN_CASE(JSOP_USESHARP)
3535 i = (jsint) GET_UINT16(regs.pc);
3536 id = INT_TO_JSID(i);
3537 obj = fp->sharpArray;
3538 if (!obj) {
3539 rval = JSVAL_VOID;
3540 } else {
3541 if (!obj->getProperty(cx, id, &rval))
3542 goto error;
3544 if (!JSVAL_IS_OBJECT(rval)) {
3545 char numBuf[12];
3547 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
3548 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3549 JSMSG_BAD_SHARP_USE, numBuf);
3550 goto error;
3552 PUSH_OPND(rval);
3553 END_CASE(JSOP_USESHARP)
3554 #endif /* JS_HAS_SHARP_VARS */
3556 BEGIN_CASE(JSOP_GOSUB)
3557 PUSH(JSVAL_FALSE);
3558 i = (regs.pc - script->main) + JSOP_GOSUB_LENGTH;
3559 PUSH(INT_TO_JSVAL(i));
3560 len = GET_JUMP_OFFSET(regs.pc);
3561 END_VARLEN_CASE
3563 BEGIN_CASE(JSOP_GOSUBX)
3564 PUSH(JSVAL_FALSE);
3565 i = (regs.pc - script->main) + JSOP_GOSUBX_LENGTH;
3566 len = GET_JUMPX_OFFSET(regs.pc);
3567 PUSH(INT_TO_JSVAL(i));
3568 END_VARLEN_CASE
3570 BEGIN_CASE(JSOP_RETSUB)
3571 /* Pop [exception or hole, retsub pc-index]. */
3572 rval = POP();
3573 lval = POP();
3574 JS_ASSERT(JSVAL_IS_BOOLEAN(lval));
3575 if (JSVAL_TO_BOOLEAN(lval)) {
3577 * Exception was pending during finally, throw it *before* we
3578 * adjust pc, because pc indexes into script->trynotes. This
3579 * turns out not to be necessary, but it seems clearer. And
3580 * it points out a FIXME: 350509, due to Igor Bukanov.
3582 cx->throwing = JS_TRUE;
3583 cx->exception = rval;
3584 goto error;
3586 JS_ASSERT(JSVAL_IS_INT(rval));
3587 len = JSVAL_TO_INT(rval);
3588 regs.pc = script->main;
3589 END_VARLEN_CASE
3591 BEGIN_CASE(JSOP_EXCEPTION)
3592 JS_ASSERT(cx->throwing);
3593 PUSH(cx->exception);
3594 cx->throwing = JS_FALSE;
3595 CHECK_BRANCH();
3596 END_CASE(JSOP_EXCEPTION)
3598 BEGIN_CASE(JSOP_FINALLY)
3599 CHECK_BRANCH();
3600 END_CASE(JSOP_FINALLY)
3602 BEGIN_CASE(JSOP_THROWING)
3603 JS_ASSERT(!cx->throwing);
3604 cx->throwing = JS_TRUE;
3605 cx->exception = POP_OPND();
3606 END_CASE(JSOP_THROWING)
3608 BEGIN_CASE(JSOP_THROW)
3609 JS_ASSERT(!cx->throwing);
3610 CHECK_BRANCH();
3611 cx->throwing = JS_TRUE;
3612 cx->exception = POP_OPND();
3613 /* let the code at error try to catch the exception. */
3614 goto error;
3616 BEGIN_CASE(JSOP_SETLOCALPOP)
3618 * The stack must have a block with at least one local slot below
3619 * the exception object.
3621 JS_ASSERT((size_t) (regs.sp - StackBase(fp)) >= 2);
3622 slot = GET_UINT16(regs.pc);
3623 JS_ASSERT(slot + 1 < script->nslots);
3624 fp->slots[slot] = POP_OPND();
3625 END_CASE(JSOP_SETLOCALPOP)
3627 BEGIN_CASE(JSOP_IFPRIMTOP)
3629 * If the top of stack is of primitive type, jump to our target.
3630 * Otherwise advance to the next opcode.
3632 JS_ASSERT(regs.sp > StackBase(fp));
3633 rval = FETCH_OPND(-1);
3634 if (JSVAL_IS_PRIMITIVE(rval)) {
3635 len = GET_JUMP_OFFSET(regs.pc);
3636 BRANCH(len);
3638 END_CASE(JSOP_IFPRIMTOP)
3640 BEGIN_CASE(JSOP_PRIMTOP)
3641 JS_ASSERT(regs.sp > StackBase(fp));
3642 lval = FETCH_OPND(-1);
3643 i = GET_INT8(regs.pc);
3644 if (!JSVAL_IS_PRIMITIVE(lval)) {
3645 lval = FETCH_OPND(-2);
3646 js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO,
3647 -2, lval, NULL,
3648 (i == JSTYPE_VOID)
3649 ? "primitive type"
3650 : JS_TYPE_STR(i));
3651 goto error;
3653 END_CASE(JSOP_PRIMTOP)
3655 BEGIN_CASE(JSOP_OBJTOP)
3656 lval = FETCH_OPND(-1);
3657 if (JSVAL_IS_PRIMITIVE(lval)) {
3658 js_ReportValueError(cx, GET_UINT16(regs.pc), -1, lval, NULL);
3659 goto error;
3661 END_CASE(JSOP_OBJTOP)
3663 BEGIN_CASE(JSOP_INSTANCEOF)
3664 rval = FETCH_OPND(-1);
3665 if (JSVAL_IS_PRIMITIVE(rval) ||
3666 !(obj = JSVAL_TO_OBJECT(rval))->map->ops->hasInstance) {
3667 js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
3668 -1, rval, NULL);
3669 goto error;
3671 lval = FETCH_OPND(-2);
3672 cond = JS_FALSE;
3673 if (!obj->map->ops->hasInstance(cx, obj, lval, &cond))
3674 goto error;
3675 regs.sp--;
3676 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));
3677 END_CASE(JSOP_INSTANCEOF)
3679 #if JS_HAS_DEBUGGER_KEYWORD
3680 BEGIN_CASE(JSOP_DEBUGGER)
3682 JSTrapHandler handler = cx->debugHooks->debuggerHandler;
3683 if (handler) {
3684 switch (handler(cx, script, regs.pc, &rval,
3685 cx->debugHooks->debuggerHandlerData)) {
3686 case JSTRAP_ERROR:
3687 goto error;
3688 case JSTRAP_CONTINUE:
3689 break;
3690 case JSTRAP_RETURN:
3691 fp->rval = rval;
3692 ok = JS_TRUE;
3693 goto forced_return;
3694 case JSTRAP_THROW:
3695 cx->throwing = JS_TRUE;
3696 cx->exception = rval;
3697 goto error;
3698 default:;
3700 CHECK_INTERRUPT_HANDLER();
3703 END_CASE(JSOP_DEBUGGER)
3704 #endif /* JS_HAS_DEBUGGER_KEYWORD */
3706 #if JS_HAS_XML_SUPPORT
3707 BEGIN_CASE(JSOP_DEFXMLNS)
3708 rval = POP();
3709 if (!js_SetDefaultXMLNamespace(cx, rval))
3710 goto error;
3711 END_CASE(JSOP_DEFXMLNS)
3713 BEGIN_CASE(JSOP_ANYNAME)
3714 if (!js_GetAnyName(cx, &rval))
3715 goto error;
3716 PUSH_OPND(rval);
3717 END_CASE(JSOP_ANYNAME)
3719 BEGIN_CASE(JSOP_QNAMEPART)
3720 LOAD_ATOM(0);
3721 PUSH_OPND(ATOM_KEY(atom));
3722 END_CASE(JSOP_QNAMEPART)
3724 BEGIN_CASE(JSOP_QNAMECONST)
3725 LOAD_ATOM(0);
3726 rval = ATOM_KEY(atom);
3727 lval = FETCH_OPND(-1);
3728 obj = js_ConstructXMLQNameObject(cx, lval, rval);
3729 if (!obj)
3730 goto error;
3731 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
3732 END_CASE(JSOP_QNAMECONST)
3734 BEGIN_CASE(JSOP_QNAME)
3735 rval = FETCH_OPND(-1);
3736 lval = FETCH_OPND(-2);
3737 obj = js_ConstructXMLQNameObject(cx, lval, rval);
3738 if (!obj)
3739 goto error;
3740 regs.sp--;
3741 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
3742 END_CASE(JSOP_QNAME)
3744 BEGIN_CASE(JSOP_TOATTRNAME)
3745 rval = FETCH_OPND(-1);
3746 if (!js_ToAttributeName(cx, &rval))
3747 goto error;
3748 STORE_OPND(-1, rval);
3749 END_CASE(JSOP_TOATTRNAME)
3751 BEGIN_CASE(JSOP_TOATTRVAL)
3752 rval = FETCH_OPND(-1);
3753 JS_ASSERT(JSVAL_IS_STRING(rval));
3754 str = js_EscapeAttributeValue(cx, JSVAL_TO_STRING(rval), JS_FALSE);
3755 if (!str)
3756 goto error;
3757 STORE_OPND(-1, STRING_TO_JSVAL(str));
3758 END_CASE(JSOP_TOATTRVAL)
3760 BEGIN_CASE(JSOP_ADDATTRNAME)
3761 BEGIN_CASE(JSOP_ADDATTRVAL)
3762 rval = FETCH_OPND(-1);
3763 lval = FETCH_OPND(-2);
3764 str = JSVAL_TO_STRING(lval);
3765 str2 = JSVAL_TO_STRING(rval);
3766 str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2);
3767 if (!str)
3768 goto error;
3769 regs.sp--;
3770 STORE_OPND(-1, STRING_TO_JSVAL(str));
3771 END_CASE(JSOP_ADDATTRNAME)
3773 BEGIN_CASE(JSOP_BINDXMLNAME)
3774 lval = FETCH_OPND(-1);
3775 if (!js_FindXMLProperty(cx, lval, &obj, &id))
3776 goto error;
3777 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
3778 PUSH_OPND(ID_TO_VALUE(id));
3779 END_CASE(JSOP_BINDXMLNAME)
3781 BEGIN_CASE(JSOP_SETXMLNAME)
3782 obj = JSVAL_TO_OBJECT(FETCH_OPND(-3));
3783 rval = FETCH_OPND(-1);
3784 FETCH_ELEMENT_ID(obj, -2, id);
3785 if (!obj->setProperty(cx, id, &rval))
3786 goto error;
3787 rval = FETCH_OPND(-1);
3788 regs.sp -= 2;
3789 STORE_OPND(-1, rval);
3790 END_CASE(JSOP_SETXMLNAME)
3792 BEGIN_CASE(JSOP_CALLXMLNAME)
3793 BEGIN_CASE(JSOP_XMLNAME)
3794 lval = FETCH_OPND(-1);
3795 if (!js_FindXMLProperty(cx, lval, &obj, &id))
3796 goto error;
3797 if (!obj->getProperty(cx, id, &rval))
3798 goto error;
3799 STORE_OPND(-1, rval);
3800 if (op == JSOP_CALLXMLNAME)
3801 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3802 END_CASE(JSOP_XMLNAME)
3804 BEGIN_CASE(JSOP_DESCENDANTS)
3805 BEGIN_CASE(JSOP_DELDESC)
3806 FETCH_OBJECT(cx, -2, lval, obj);
3807 rval = FETCH_OPND(-1);
3808 if (!js_GetXMLDescendants(cx, obj, rval, &rval))
3809 goto error;
3811 if (op == JSOP_DELDESC) {
3812 regs.sp[-1] = rval; /* set local root */
3813 if (!js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval)))
3814 goto error;
3815 rval = JSVAL_TRUE; /* always succeed */
3818 regs.sp--;
3819 STORE_OPND(-1, rval);
3820 END_CASE(JSOP_DESCENDANTS)
3822 BEGIN_CASE(JSOP_FILTER)
3824 * We push the hole value before jumping to [enditer] so we can
3825 * detect the first iteration and direct js_StepXMLListFilter to
3826 * initialize filter's state.
3828 PUSH_OPND(JSVAL_HOLE);
3829 len = GET_JUMP_OFFSET(regs.pc);
3830 JS_ASSERT(len > 0);
3831 END_VARLEN_CASE
3833 BEGIN_CASE(JSOP_ENDFILTER)
3834 cond = (regs.sp[-1] != JSVAL_HOLE);
3835 if (cond) {
3836 /* Exit the "with" block left from the previous iteration. */
3837 js_LeaveWith(cx);
3839 if (!js_StepXMLListFilter(cx, cond))
3840 goto error;
3841 if (regs.sp[-1] != JSVAL_NULL) {
3843 * Decrease sp after EnterWith returns as we use sp[-1] there
3844 * to root temporaries.
3846 JS_ASSERT(VALUE_IS_XML(cx, regs.sp[-1]));
3847 if (!js_EnterWith(cx, -2))
3848 goto error;
3849 regs.sp--;
3850 len = GET_JUMP_OFFSET(regs.pc);
3851 JS_ASSERT(len < 0);
3852 BRANCH(len);
3854 regs.sp--;
3855 END_CASE(JSOP_ENDFILTER);
3857 BEGIN_CASE(JSOP_TOXML)
3858 rval = FETCH_OPND(-1);
3859 obj = js_ValueToXMLObject(cx, rval);
3860 if (!obj)
3861 goto error;
3862 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
3863 END_CASE(JSOP_TOXML)
3865 BEGIN_CASE(JSOP_TOXMLLIST)
3866 rval = FETCH_OPND(-1);
3867 obj = js_ValueToXMLListObject(cx, rval);
3868 if (!obj)
3869 goto error;
3870 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
3871 END_CASE(JSOP_TOXMLLIST)
3873 BEGIN_CASE(JSOP_XMLTAGEXPR)
3874 rval = FETCH_OPND(-1);
3875 str = js_ValueToString(cx, rval);
3876 if (!str)
3877 goto error;
3878 STORE_OPND(-1, STRING_TO_JSVAL(str));
3879 END_CASE(JSOP_XMLTAGEXPR)
3881 BEGIN_CASE(JSOP_XMLELTEXPR)
3882 rval = FETCH_OPND(-1);
3883 if (VALUE_IS_XML(cx, rval)) {
3884 str = js_ValueToXMLString(cx, rval);
3885 } else {
3886 str = js_ValueToString(cx, rval);
3887 if (str)
3888 str = js_EscapeElementValue(cx, str);
3890 if (!str)
3891 goto error;
3892 STORE_OPND(-1, STRING_TO_JSVAL(str));
3893 END_CASE(JSOP_XMLELTEXPR)
3895 BEGIN_CASE(JSOP_XMLOBJECT)
3896 LOAD_OBJECT(0);
3897 obj = js_CloneXMLObject(cx, obj);
3898 if (!obj)
3899 goto error;
3900 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3901 END_CASE(JSOP_XMLOBJECT)
3903 BEGIN_CASE(JSOP_XMLCDATA)
3904 LOAD_ATOM(0);
3905 str = ATOM_TO_STRING(atom);
3906 obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_TEXT, NULL, str);
3907 if (!obj)
3908 goto error;
3909 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3910 END_CASE(JSOP_XMLCDATA)
3912 BEGIN_CASE(JSOP_XMLCOMMENT)
3913 LOAD_ATOM(0);
3914 str = ATOM_TO_STRING(atom);
3915 obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_COMMENT, NULL, str);
3916 if (!obj)
3917 goto error;
3918 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3919 END_CASE(JSOP_XMLCOMMENT)
3921 BEGIN_CASE(JSOP_XMLPI)
3922 LOAD_ATOM(0);
3923 str = ATOM_TO_STRING(atom);
3924 rval = FETCH_OPND(-1);
3925 str2 = JSVAL_TO_STRING(rval);
3926 obj = js_NewXMLSpecialObject(cx,
3927 JSXML_CLASS_PROCESSING_INSTRUCTION,
3928 str, str2);
3929 if (!obj)
3930 goto error;
3931 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
3932 END_CASE(JSOP_XMLPI)
3934 BEGIN_CASE(JSOP_GETFUNNS)
3935 if (!js_GetFunctionNamespace(cx, &rval))
3936 goto error;
3937 PUSH_OPND(rval);
3938 END_CASE(JSOP_GETFUNNS)
3939 #endif /* JS_HAS_XML_SUPPORT */
3941 BEGIN_CASE(JSOP_ENTERBLOCK)
3942 LOAD_OBJECT(0);
3943 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj));
3944 JS_ASSERT(StackBase(fp) + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp);
3945 vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj);
3946 JS_ASSERT(regs.sp < vp);
3947 JS_ASSERT(vp <= fp->slots + script->nslots);
3948 while (regs.sp < vp) {
3949 STORE_OPND(0, JSVAL_VOID);
3950 regs.sp++;
3953 #ifdef DEBUG
3954 JS_ASSERT(fp->blockChain == OBJ_GET_PARENT(cx, obj));
3957 * The young end of fp->scopeChain may omit blocks if we
3958 * haven't closed over them, but if there are any closure
3959 * blocks on fp->scopeChain, they'd better be (clones of)
3960 * ancestors of the block we're entering now; anything
3961 * else we should have popped off fp->scopeChain when we
3962 * left its static scope.
3964 obj2 = fp->scopeChain;
3965 while ((clasp = OBJ_GET_CLASS(cx, obj2)) == &js_WithClass)
3966 obj2 = OBJ_GET_PARENT(cx, obj2);
3967 if (clasp == &js_BlockClass &&
3968 obj2->getAssignedPrivate() == fp) {
3969 JSObject *youngestProto = OBJ_GET_PROTO(cx, obj2);
3970 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(youngestProto));
3971 parent = obj;
3972 while ((parent = OBJ_GET_PARENT(cx, parent)) != youngestProto)
3973 JS_ASSERT(parent);
3975 #endif
3977 fp->blockChain = obj;
3978 END_CASE(JSOP_ENTERBLOCK)
3980 BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
3981 BEGIN_CASE(JSOP_LEAVEBLOCK)
3983 #ifdef DEBUG
3984 JS_ASSERT(OBJ_GET_CLASS(cx, fp->blockChain) == &js_BlockClass);
3985 uintN blockDepth = OBJ_BLOCK_DEPTH(cx, fp->blockChain);
3987 JS_ASSERT(blockDepth <= StackDepth(script));
3988 #endif
3990 * If we're about to leave the dynamic scope of a block that has
3991 * been cloned onto fp->scopeChain, clear its private data, move
3992 * its locals from the stack into the clone, and pop it off the
3993 * chain.
3995 obj = fp->scopeChain;
3996 if (OBJ_GET_PROTO(cx, obj) == fp->blockChain) {
3997 JS_ASSERT (OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
3998 if (!js_PutBlockObject(cx, JS_TRUE))
3999 goto error;
4002 /* Pop the block chain, too. */
4003 fp->blockChain = OBJ_GET_PARENT(cx, fp->blockChain);
4006 * We will move the result of the expression to the new topmost
4007 * stack slot.
4009 if (op == JSOP_LEAVEBLOCKEXPR)
4010 rval = FETCH_OPND(-1);
4011 regs.sp -= GET_UINT16(regs.pc);
4012 if (op == JSOP_LEAVEBLOCKEXPR) {
4013 JS_ASSERT(StackBase(fp) + blockDepth == regs.sp - 1);
4014 STORE_OPND(-1, rval);
4015 } else {
4016 JS_ASSERT(StackBase(fp) + blockDepth == regs.sp);
4019 END_CASE(JSOP_LEAVEBLOCK)
4021 BEGIN_CASE(JSOP_CALLBUILTIN)
4022 #ifdef JS_TRACER
4023 obj = js_GetBuiltinFunction(cx, GET_INDEX(regs.pc));
4024 if (!obj)
4025 goto error;
4026 rval = FETCH_OPND(-1);
4027 PUSH_OPND(rval);
4028 STORE_OPND(-2, OBJECT_TO_JSVAL(obj));
4029 #else
4030 goto bad_opcode; /* This is an imacro-only opcode. */
4031 #endif
4032 END_CASE(JSOP_CALLBUILTIN)
4034 #if JS_HAS_GENERATORS
4035 BEGIN_CASE(JSOP_GENERATOR)
4036 ASSERT_NOT_THROWING(cx);
4037 regs.pc += JSOP_GENERATOR_LENGTH;
4038 obj = js_NewGenerator(cx, fp);
4039 if (!obj)
4040 goto error;
4041 JS_ASSERT(!fp->callobj && !fp->argsobj);
4042 fp->rval = OBJECT_TO_JSVAL(obj);
4043 ok = JS_TRUE;
4044 if (inlineCallCount != 0)
4045 goto inline_return;
4046 goto exit;
4048 BEGIN_CASE(JSOP_YIELD)
4049 ASSERT_NOT_THROWING(cx);
4050 if (FRAME_TO_GENERATOR(fp)->state == JSGEN_CLOSING) {
4051 js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD,
4052 JSDVG_SEARCH_STACK, fp->argv[-2], NULL);
4053 goto error;
4055 fp->rval = FETCH_OPND(-1);
4056 fp->flags |= JSFRAME_YIELDING;
4057 regs.pc += JSOP_YIELD_LENGTH;
4058 ok = JS_TRUE;
4059 goto exit;
4061 BEGIN_CASE(JSOP_ARRAYPUSH)
4062 slot = GET_UINT16(regs.pc);
4063 JS_ASSERT(script->nfixed <= slot);
4064 JS_ASSERT(slot < script->nslots);
4065 lval = fp->slots[slot];
4066 obj = JSVAL_TO_OBJECT(lval);
4067 rval = FETCH_OPND(-1);
4068 if (!js_ArrayCompPush(cx, obj, rval))
4069 goto error;
4070 regs.sp--;
4071 END_CASE(JSOP_ARRAYPUSH)
4072 #endif /* JS_HAS_GENERATORS */
4074 BEGIN_CASE(JSOP_LOOP)
4075 END_CASE(JSOP_LOOP)
4077 #if JS_THREADED_INTERP
4078 L_JSOP_BACKPATCH:
4079 L_JSOP_BACKPATCH_POP:
4081 # if !JS_HAS_GENERATORS
4082 L_JSOP_GENERATOR:
4083 L_JSOP_YIELD:
4084 L_JSOP_ARRAYPUSH:
4085 # endif
4087 # if !JS_HAS_SHARP_VARS
4088 L_JSOP_DEFSHARP:
4089 L_JSOP_USESHARP:
4090 # endif
4092 # if !JS_HAS_DESTRUCTURING
4093 L_JSOP_ENUMCONSTELEM:
4094 # endif
4096 # if !JS_HAS_XML_SUPPORT
4097 L_JSOP_CALLXMLNAME:
4098 L_JSOP_STARTXMLEXPR:
4099 L_JSOP_STARTXML:
4100 L_JSOP_DELDESC:
4101 L_JSOP_GETFUNNS:
4102 L_JSOP_XMLPI:
4103 L_JSOP_XMLCOMMENT:
4104 L_JSOP_XMLCDATA:
4105 L_JSOP_XMLOBJECT:
4106 L_JSOP_XMLELTEXPR:
4107 L_JSOP_XMLTAGEXPR:
4108 L_JSOP_TOXMLLIST:
4109 L_JSOP_TOXML:
4110 L_JSOP_ENDFILTER:
4111 L_JSOP_FILTER:
4112 L_JSOP_DESCENDANTS:
4113 L_JSOP_XMLNAME:
4114 L_JSOP_SETXMLNAME:
4115 L_JSOP_BINDXMLNAME:
4116 L_JSOP_ADDATTRVAL:
4117 L_JSOP_ADDATTRNAME:
4118 L_JSOP_TOATTRVAL:
4119 L_JSOP_TOATTRNAME:
4120 L_JSOP_QNAME:
4121 L_JSOP_QNAMECONST:
4122 L_JSOP_QNAMEPART:
4123 L_JSOP_ANYNAME:
4124 L_JSOP_DEFXMLNS:
4125 # endif
4126 #endif /* !JS_THREADED_INTERP */