Bug 559408: Arena pool macros to methods. (r=gal)
[mozilla-central.git] / js / src / jsops.cpp
blobe091bad6408d8aa274937846788d37436dc20083
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 JSInterruptHook hook = cx->debugHooks->interruptHook;
52 if (hook) {
53 #ifdef JS_TRACER
54 if (TRACE_RECORDER(cx))
55 AbortRecording(cx, "interrupt hook");
56 #endif
57 switch (hook(cx, script, regs.pc, &rval,
58 cx->debugHooks->interruptHookData)) {
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 if (TraceRecorder* tr = TRACE_RECORDER(cx)) {
78 AbortableRecordingStatus status = tr->monitorRecording(op);
79 JS_ASSERT_IF(cx->throwing, status == ARECORD_ERROR);
80 switch (status) {
81 case ARECORD_CONTINUE:
82 moreInterrupts = true;
83 break;
84 case ARECORD_IMACRO:
85 atoms = COMMON_ATOMS_START(&rt->atomState);
86 op = JSOp(*regs.pc);
87 DO_OP(); /* keep interrupting for op. */
88 break;
89 case ARECORD_ERROR:
90 // The code at 'error:' aborts the recording.
91 goto error;
92 case ARECORD_ABORTED:
93 case ARECORD_COMPLETED:
94 break;
95 case ARECORD_STOP:
96 /* A 'stop' error should have already aborted recording. */
97 default:
98 JS_NOT_REACHED("Bad recording status");
101 #endif /* !JS_TRACER */
103 #if JS_THREADED_INTERP
104 #ifdef MOZ_TRACEVIS
105 if (!moreInterrupts)
106 ExitTraceVisState(cx, R_ABORT);
107 #endif
108 jumpTable = moreInterrupts ? interruptJumpTable : normalJumpTable;
109 JS_EXTENSION_(goto *normalJumpTable[op]);
110 #else
111 switchMask = moreInterrupts ? -1 : 0;
112 switchOp = intN(op);
113 goto do_switch;
114 #endif
117 /* No-ops for ease of decompilation. */
118 ADD_EMPTY_CASE(JSOP_NOP)
119 ADD_EMPTY_CASE(JSOP_CONDSWITCH)
120 ADD_EMPTY_CASE(JSOP_TRY)
121 ADD_EMPTY_CASE(JSOP_TRACE)
122 #if JS_HAS_XML_SUPPORT
123 ADD_EMPTY_CASE(JSOP_STARTXML)
124 ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
125 #endif
126 END_EMPTY_CASES
128 /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
129 BEGIN_CASE(JSOP_LINENO)
130 END_CASE(JSOP_LINENO)
132 BEGIN_CASE(JSOP_PUSH)
133 PUSH_OPND(JSVAL_VOID);
134 END_CASE(JSOP_PUSH)
136 BEGIN_CASE(JSOP_POP)
137 regs.sp--;
138 END_CASE(JSOP_POP)
140 BEGIN_CASE(JSOP_POPN)
141 regs.sp -= GET_UINT16(regs.pc);
142 #ifdef DEBUG
143 JS_ASSERT(StackBase(fp) <= regs.sp);
144 obj = fp->blockChain;
145 JS_ASSERT_IF(obj,
146 OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
147 <= (size_t) (regs.sp - StackBase(fp)));
148 for (obj = fp->scopeChain; obj; obj = obj->getParent()) {
149 clasp = obj->getClass();
150 if (clasp != &js_BlockClass && clasp != &js_WithClass)
151 continue;
152 if (obj->getPrivate() != fp)
153 break;
154 JS_ASSERT(StackBase(fp) + OBJ_BLOCK_DEPTH(cx, obj)
155 + ((clasp == &js_BlockClass)
156 ? OBJ_BLOCK_COUNT(cx, obj)
157 : 1)
158 <= regs.sp);
160 #endif
161 END_CASE(JSOP_POPN)
163 BEGIN_CASE(JSOP_SETRVAL)
164 BEGIN_CASE(JSOP_POPV)
165 ASSERT_NOT_THROWING(cx);
166 fp->rval = POP_OPND();
167 END_CASE(JSOP_POPV)
169 BEGIN_CASE(JSOP_ENTERWITH)
170 if (!js_EnterWith(cx, -1))
171 goto error;
174 * We must ensure that different "with" blocks have different stack depth
175 * associated with them. This allows the try handler search to properly
176 * recover the scope chain. Thus we must keep the stack at least at the
177 * current level.
179 * We set sp[-1] to the current "with" object to help asserting the
180 * enter/leave balance in [leavewith].
182 regs.sp[-1] = OBJECT_TO_JSVAL(fp->scopeChain);
183 END_CASE(JSOP_ENTERWITH)
185 BEGIN_CASE(JSOP_LEAVEWITH)
186 JS_ASSERT(regs.sp[-1] == OBJECT_TO_JSVAL(fp->scopeChain));
187 regs.sp--;
188 js_LeaveWith(cx);
189 END_CASE(JSOP_LEAVEWITH)
191 BEGIN_CASE(JSOP_RETURN)
192 fp->rval = POP_OPND();
193 /* FALL THROUGH */
195 BEGIN_CASE(JSOP_RETRVAL) /* fp->rval already set */
196 BEGIN_CASE(JSOP_STOP)
198 * When the inlined frame exits with an exception or an error, ok will be
199 * false after the inline_return label.
201 ASSERT_NOT_THROWING(cx);
202 CHECK_BRANCH();
204 if (fp->imacpc) {
206 * If we are at the end of an imacro, return to its caller in the
207 * current frame.
209 JS_ASSERT(op == JSOP_STOP);
210 JS_ASSERT((uintN)(regs.sp - fp->slots) <= script->nslots);
211 regs.pc = fp->imacpc + js_CodeSpec[*fp->imacpc].length;
212 fp->imacpc = NULL;
213 atoms = script->atomMap.vector;
214 op = JSOp(*regs.pc);
215 DO_OP();
218 JS_ASSERT(regs.sp == StackBase(fp));
219 if ((fp->flags & JSFRAME_CONSTRUCTING) && JSVAL_IS_PRIMITIVE(fp->rval))
220 fp->rval = fp->thisv;
221 ok = JS_TRUE;
222 if (inlineCallCount)
223 inline_return:
225 JSInlineFrame *ifp = (JSInlineFrame *) fp;
226 void *hookData = ifp->hookData;
228 JS_ASSERT(!fp->blockChain);
229 JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
231 if (script->staticLevel < JS_DISPLAY_SIZE)
232 cx->display[script->staticLevel] = fp->displaySave;
234 if (hookData) {
235 JSInterpreterHook hook;
236 JSBool status;
238 hook = cx->debugHooks->callHook;
239 if (hook) {
241 * Do not pass &ok directly as exposing the address inhibits
242 * optimizations and uninitialised warnings.
244 status = ok;
245 hook(cx, fp, JS_FALSE, &status, hookData);
246 ok = status;
247 CHECK_INTERRUPT_HANDLER();
252 * If fp has a call object, sync values and clear the back-
253 * pointer. This can happen for a lightweight function if it calls eval
254 * unexpectedly (in a way that is hidden from the compiler). See bug
255 * 325540.
257 fp->putActivationObjects(cx);
259 DTrace::exitJSFun(cx, fp, fp->fun, fp->rval);
261 /* Restore context version only if callee hasn't set version. */
262 if (JS_LIKELY(cx->version == currentVersion)) {
263 currentVersion = ifp->callerVersion;
264 if (currentVersion != cx->version)
265 js_SetVersion(cx, currentVersion);
269 * If inline-constructing, replace primitive rval with the new object
270 * passed in via |this|, and instrument this constructor invocation.
272 if (fp->flags & JSFRAME_CONSTRUCTING) {
273 if (JSVAL_IS_PRIMITIVE(fp->rval))
274 fp->rval = fp->thisv;
275 JS_RUNTIME_METER(cx->runtime, constructs);
278 /* Restore caller's registers. */
279 regs = ifp->callerRegs;
281 /* Store the return value in the caller's operand frame. */
282 regs.sp -= 1 + (size_t) ifp->frame.argc;
283 regs.sp[-1] = fp->rval;
285 bool recursive = fp->script == fp->down->script;
287 /* Restore cx->fp and release the inline frame's space. */
288 cx->fp = fp = fp->down;
289 JS_ASSERT(fp->regs == &ifp->callerRegs);
290 fp->regs = &regs;
291 cx->stackPool.release(ifp->mark);
293 /* Restore the calling script's interpreter registers. */
294 script = fp->script;
295 atoms = FrameAtomBase(cx, fp);
297 /* Resume execution in the calling frame. */
298 inlineCallCount--;
299 if (JS_LIKELY(ok)) {
300 JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, script, regs.pc)].length
301 == JSOP_CALL_LENGTH);
302 TRACE_0(LeaveFrame);
303 if (!TRACE_RECORDER(cx) && recursive) {
304 if (*(regs.pc + JSOP_CALL_LENGTH) == JSOP_TRACE) {
305 regs.pc += JSOP_CALL_LENGTH;
306 MONITOR_BRANCH(Record_LeaveFrame);
307 op = (JSOp)*regs.pc;
308 DO_OP();
311 if (*(regs.pc + JSOP_CALL_LENGTH) == JSOP_TRACE ||
312 *(regs.pc + JSOP_CALL_LENGTH) == JSOP_NOP) {
313 JS_STATIC_ASSERT(JSOP_TRACE_LENGTH == JSOP_NOP_LENGTH);
314 regs.pc += JSOP_CALL_LENGTH;
315 len = JSOP_TRACE_LENGTH;
316 } else {
317 len = JSOP_CALL_LENGTH;
319 DO_NEXT_OP(len);
321 goto error;
323 goto exit;
325 BEGIN_CASE(JSOP_DEFAULT)
326 (void) POP();
327 /* FALL THROUGH */
328 BEGIN_CASE(JSOP_GOTO)
329 len = GET_JUMP_OFFSET(regs.pc);
330 BRANCH(len);
331 END_CASE(JSOP_GOTO)
333 BEGIN_CASE(JSOP_IFEQ)
334 POP_BOOLEAN(cx, rval, cond);
335 if (cond == JS_FALSE) {
336 len = GET_JUMP_OFFSET(regs.pc);
337 BRANCH(len);
339 END_CASE(JSOP_IFEQ)
341 BEGIN_CASE(JSOP_IFNE)
342 POP_BOOLEAN(cx, rval, cond);
343 if (cond != JS_FALSE) {
344 len = GET_JUMP_OFFSET(regs.pc);
345 BRANCH(len);
347 END_CASE(JSOP_IFNE)
349 BEGIN_CASE(JSOP_OR)
350 POP_BOOLEAN(cx, rval, cond);
351 if (cond == JS_TRUE) {
352 len = GET_JUMP_OFFSET(regs.pc);
353 PUSH_OPND(rval);
354 DO_NEXT_OP(len);
356 END_CASE(JSOP_OR)
358 BEGIN_CASE(JSOP_AND)
359 POP_BOOLEAN(cx, rval, cond);
360 if (cond == JS_FALSE) {
361 len = GET_JUMP_OFFSET(regs.pc);
362 PUSH_OPND(rval);
363 DO_NEXT_OP(len);
365 END_CASE(JSOP_AND)
367 BEGIN_CASE(JSOP_DEFAULTX)
368 (void) POP();
369 /* FALL THROUGH */
370 BEGIN_CASE(JSOP_GOTOX)
371 len = GET_JUMPX_OFFSET(regs.pc);
372 BRANCH(len);
373 END_CASE(JSOP_GOTOX);
375 BEGIN_CASE(JSOP_IFEQX)
376 POP_BOOLEAN(cx, rval, cond);
377 if (cond == JS_FALSE) {
378 len = GET_JUMPX_OFFSET(regs.pc);
379 BRANCH(len);
381 END_CASE(JSOP_IFEQX)
383 BEGIN_CASE(JSOP_IFNEX)
384 POP_BOOLEAN(cx, rval, cond);
385 if (cond != JS_FALSE) {
386 len = GET_JUMPX_OFFSET(regs.pc);
387 BRANCH(len);
389 END_CASE(JSOP_IFNEX)
391 BEGIN_CASE(JSOP_ORX)
392 POP_BOOLEAN(cx, rval, cond);
393 if (cond == JS_TRUE) {
394 len = GET_JUMPX_OFFSET(regs.pc);
395 PUSH_OPND(rval);
396 DO_NEXT_OP(len);
398 END_CASE(JSOP_ORX)
400 BEGIN_CASE(JSOP_ANDX)
401 POP_BOOLEAN(cx, rval, cond);
402 if (cond == JS_FALSE) {
403 len = GET_JUMPX_OFFSET(regs.pc);
404 PUSH_OPND(rval);
405 DO_NEXT_OP(len);
407 END_CASE(JSOP_ANDX)
410 * If the index value at sp[n] is not an int that fits in a jsval, it could
411 * be an object (an XML QName, AttributeName, or AnyName), but only if we are
412 * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a
413 * string atom id.
415 #define FETCH_ELEMENT_ID(obj, n, id) \
416 JS_BEGIN_MACRO \
417 jsval idval_ = FETCH_OPND(n); \
418 if (JSVAL_IS_INT(idval_)) { \
419 id = INT_JSVAL_TO_JSID(idval_); \
420 } else { \
421 if (!js_InternNonIntElementId(cx, obj, idval_, &id)) \
422 goto error; \
423 regs.sp[n] = ID_TO_VALUE(id); \
425 JS_END_MACRO
427 #define TRY_BRANCH_AFTER_COND(cond,spdec) \
428 JS_BEGIN_MACRO \
429 uintN diff_; \
430 JS_ASSERT(js_CodeSpec[op].length == 1); \
431 diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ; \
432 if (diff_ <= 1) { \
433 regs.sp -= spdec; \
434 if (cond == (diff_ != 0)) { \
435 ++regs.pc; \
436 len = GET_JUMP_OFFSET(regs.pc); \
437 BRANCH(len); \
439 len = 1 + JSOP_IFEQ_LENGTH; \
440 DO_NEXT_OP(len); \
442 JS_END_MACRO
444 BEGIN_CASE(JSOP_IN)
445 rval = FETCH_OPND(-1);
446 if (JSVAL_IS_PRIMITIVE(rval)) {
447 js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rval, NULL);
448 goto error;
450 obj = JSVAL_TO_OBJECT(rval);
451 FETCH_ELEMENT_ID(obj, -2, id);
452 if (!obj->lookupProperty(cx, id, &obj2, &prop))
453 goto error;
454 cond = prop != NULL;
455 if (prop)
456 obj2->dropProperty(cx, prop);
457 TRY_BRANCH_AFTER_COND(cond, 2);
458 regs.sp--;
459 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));
460 END_CASE(JSOP_IN)
462 BEGIN_CASE(JSOP_ITER)
463 JS_ASSERT(regs.sp > StackBase(fp));
464 flags = regs.pc[1];
465 if (!js_ValueToIterator(cx, flags, &regs.sp[-1]))
466 goto error;
467 CHECK_INTERRUPT_HANDLER();
468 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));
469 END_CASE(JSOP_ITER)
471 BEGIN_CASE(JSOP_MOREITER)
472 JS_ASSERT(regs.sp - 1 >= StackBase(fp));
473 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));
474 if (!IteratorMore(cx, JSVAL_TO_OBJECT(regs.sp[-1]), &cond, &regs.sp[0]))
475 goto error;
476 CHECK_INTERRUPT_HANDLER();
477 TRY_BRANCH_AFTER_COND(cond, 0);
478 JS_ASSERT(regs.pc[1] == JSOP_IFNEX);
479 PUSH_OPND(BOOLEAN_TO_JSVAL(cond));
480 END_CASE(JSOP_MOREITER)
482 BEGIN_CASE(JSOP_ENDITER)
483 JS_ASSERT(regs.sp - 1 >= StackBase(fp));
484 ok = js_CloseIterator(cx, regs.sp[-1]);
485 regs.sp--;
486 if (!ok)
487 goto error;
488 END_CASE(JSOP_ENDITER)
490 BEGIN_CASE(JSOP_FORARG)
491 JS_ASSERT(regs.sp - 1 >= StackBase(fp));
492 slot = GET_ARGNO(regs.pc);
493 JS_ASSERT(slot < fp->fun->nargs);
494 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));
495 if (!IteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-1]), &fp->argv[slot]))
496 goto error;
497 END_CASE(JSOP_FORARG)
499 BEGIN_CASE(JSOP_FORLOCAL)
500 JS_ASSERT(regs.sp - 1 >= StackBase(fp));
501 slot = GET_SLOTNO(regs.pc);
502 JS_ASSERT(slot < fp->script->nslots);
503 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));
504 if (!IteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-1]), &fp->slots[slot]))
505 goto error;
506 END_CASE(JSOP_FORLOCAL)
508 BEGIN_CASE(JSOP_FORNAME)
509 JS_ASSERT(regs.sp - 1 >= StackBase(fp));
510 LOAD_ATOM(0);
511 id = ATOM_TO_JSID(atom);
512 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
513 goto error;
514 if (prop)
515 obj2->dropProperty(cx, prop);
517 AutoValueRooter tvr(cx);
518 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));
519 if (!IteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-1]), tvr.addr()))
520 goto error;
521 ok = obj->setProperty(cx, id, tvr.addr());
522 if (!ok)
523 goto error;
525 END_CASE(JSOP_FORNAME)
527 BEGIN_CASE(JSOP_FORPROP)
528 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
529 LOAD_ATOM(0);
530 id = ATOM_TO_JSID(atom);
531 FETCH_OBJECT(cx, -1, lval, obj);
533 AutoValueRooter tvr(cx);
534 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-2]));
535 if (!IteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-2]), tvr.addr()))
536 goto error;
537 ok = obj->setProperty(cx, id, tvr.addr());
538 if (!ok)
539 goto error;
541 regs.sp--;
542 END_CASE(JSOP_FORPROP)
544 BEGIN_CASE(JSOP_FORELEM)
546 * JSOP_FORELEM simply dups the property identifier at top of stack and
547 * lets the subsequent JSOP_ENUMELEM opcode sequence handle the left-hand
548 * side expression evaluation and assignment. This opcode exists solely to
549 * help the decompiler.
551 JS_ASSERT(regs.sp - 1 >= StackBase(fp));
552 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));
553 if (!IteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-1]), &regs.sp[0]))
554 goto error;
555 regs.sp++;
556 END_CASE(JSOP_FORELEM)
558 BEGIN_CASE(JSOP_DUP)
559 JS_ASSERT(regs.sp > StackBase(fp));
560 rval = FETCH_OPND(-1);
561 PUSH(rval);
562 END_CASE(JSOP_DUP)
564 BEGIN_CASE(JSOP_DUP2)
565 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
566 lval = FETCH_OPND(-2);
567 rval = FETCH_OPND(-1);
568 PUSH(lval);
569 PUSH(rval);
570 END_CASE(JSOP_DUP2)
572 BEGIN_CASE(JSOP_SWAP)
573 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
574 lval = FETCH_OPND(-2);
575 rval = FETCH_OPND(-1);
576 STORE_OPND(-1, lval);
577 STORE_OPND(-2, rval);
578 END_CASE(JSOP_SWAP)
580 BEGIN_CASE(JSOP_PICK)
581 i = regs.pc[1];
582 JS_ASSERT(regs.sp - (i+1) >= StackBase(fp));
583 lval = regs.sp[-(i+1)];
584 memmove(regs.sp - (i+1), regs.sp - i, sizeof(jsval)*i);
585 regs.sp[-1] = lval;
586 END_CASE(JSOP_PICK)
588 #define PROPERTY_OP(n, call) \
589 JS_BEGIN_MACRO \
590 /* Fetch the left part and resolve it to a non-null object. */ \
591 FETCH_OBJECT(cx, n, lval, obj); \
593 /* Get or set the property. */ \
594 if (!call) \
595 goto error; \
596 JS_END_MACRO
598 #define ELEMENT_OP(n, call) \
599 JS_BEGIN_MACRO \
600 /* Fetch the left part and resolve it to a non-null object. */ \
601 FETCH_OBJECT(cx, n - 1, lval, obj); \
603 /* Fetch index and convert it to id suitable for use with obj. */ \
604 FETCH_ELEMENT_ID(obj, n, id); \
606 /* Get or set the element. */ \
607 if (!call) \
608 goto error; \
609 JS_END_MACRO
611 #define NATIVE_GET(cx,obj,pobj,sprop,getHow,vp) \
612 JS_BEGIN_MACRO \
613 if (sprop->hasDefaultGetter()) { \
614 /* Fast path for Object instance properties. */ \
615 JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT || \
616 !sprop->hasDefaultSetter()); \
617 *vp = ((sprop)->slot != SPROP_INVALID_SLOT) \
618 ? (pobj)->lockedGetSlot((sprop)->slot) \
619 : JSVAL_VOID; \
620 } else { \
621 if (!js_NativeGet(cx, obj, pobj, sprop, getHow, vp)) \
622 goto error; \
624 JS_END_MACRO
626 #define NATIVE_SET(cx,obj,sprop,entry,vp) \
627 JS_BEGIN_MACRO \
628 TRACE_2(SetPropHit, entry, sprop); \
629 if (sprop->hasDefaultSetter() && \
630 (sprop)->slot != SPROP_INVALID_SLOT && \
631 !(obj)->scope()->brandedOrHasMethodBarrier()) { \
632 /* Fast path for, e.g., plain Object instance properties. */ \
633 (obj)->lockedSetSlot((sprop)->slot, *vp); \
634 } else { \
635 if (!js_NativeSet(cx, obj, sprop, false, vp)) \
636 goto error; \
638 JS_END_MACRO
641 * Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is
642 * the constant length of the SET opcode sequence, and spdec is the constant
643 * by which to decrease the stack pointer to pop all of the SET op's operands.
645 * NB: unlike macros that could conceivably be replaced by functions (ignoring
646 * goto error), where a call should not have to be braced in order to expand
647 * correctly (e.g., in if (cond) FOO(); else BAR()), these three macros lack
648 * JS_{BEGIN,END}_MACRO brackets. They are also indented so as to align with
649 * nearby opcode code.
651 #define SKIP_POP_AFTER_SET(oplen,spdec) \
652 if (regs.pc[oplen] == JSOP_POP) { \
653 regs.sp -= spdec; \
654 regs.pc += oplen + JSOP_POP_LENGTH; \
655 op = (JSOp) *regs.pc; \
656 DO_OP(); \
659 #define END_SET_CASE(OP) \
660 SKIP_POP_AFTER_SET(OP##_LENGTH, 1); \
661 END_CASE(OP)
663 #define END_SET_CASE_STORE_RVAL(OP,spdec) \
664 SKIP_POP_AFTER_SET(OP##_LENGTH, spdec); \
665 rval = FETCH_OPND(-1); \
666 regs.sp -= (spdec) - 1; \
667 STORE_OPND(-1, rval); \
668 END_CASE(OP)
670 BEGIN_CASE(JSOP_SETCONST)
671 LOAD_ATOM(0);
672 obj = fp->varobj(cx);
673 rval = FETCH_OPND(-1);
674 if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), rval,
675 JS_PropertyStub, JS_PropertyStub,
676 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) {
677 goto error;
679 END_SET_CASE(JSOP_SETCONST);
681 #if JS_HAS_DESTRUCTURING
682 BEGIN_CASE(JSOP_ENUMCONSTELEM)
683 rval = FETCH_OPND(-3);
684 FETCH_OBJECT(cx, -2, lval, obj);
685 FETCH_ELEMENT_ID(obj, -1, id);
686 if (!obj->defineProperty(cx, id, rval,
687 JS_PropertyStub, JS_PropertyStub,
688 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) {
689 goto error;
691 regs.sp -= 3;
692 END_CASE(JSOP_ENUMCONSTELEM)
693 #endif
695 BEGIN_CASE(JSOP_BINDNAME)
696 do {
697 PropertyCacheEntry *entry;
700 * We can skip the property lookup for the global object. If the
701 * property does not exist anywhere on the scope chain, JSOP_SETNAME
702 * adds the property to the global.
704 * As a consequence of this optimization for the global object we run
705 * its JSRESOLVE_ASSIGNING-tolerant resolve hooks only in JSOP_SETNAME,
706 * after the interpreter evaluates the right- hand-side of the
707 * assignment, and not here.
709 * This should be transparent to the hooks because the script, instead
710 * of name = rhs, could have used global.name = rhs given a global
711 * object reference, which also calls the hooks only after evaluating
712 * the rhs. We desire such resolve hook equivalence between the two
713 * forms.
715 obj = fp->scopeChain;
716 if (!obj->getParent())
717 break;
719 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
720 if (!atom) {
721 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
722 break;
725 id = ATOM_TO_JSID(atom);
726 obj = js_FindIdentifierBase(cx, fp->scopeChain, id);
727 if (!obj)
728 goto error;
729 } while (0);
730 PUSH_OPND(OBJECT_TO_JSVAL(obj));
731 END_CASE(JSOP_BINDNAME)
733 BEGIN_CASE(JSOP_IMACOP)
734 JS_ASSERT(JS_UPTRDIFF(fp->imacpc, script->code) < script->length);
735 op = JSOp(*fp->imacpc);
736 DO_OP();
738 #define BITWISE_OP(OP) \
739 JS_BEGIN_MACRO \
740 FETCH_INT(cx, -2, i); \
741 FETCH_INT(cx, -1, j); \
742 i = i OP j; \
743 regs.sp--; \
744 STORE_INT(cx, -1, i); \
745 JS_END_MACRO
747 BEGIN_CASE(JSOP_BITOR)
748 BITWISE_OP(|);
749 END_CASE(JSOP_BITOR)
751 BEGIN_CASE(JSOP_BITXOR)
752 BITWISE_OP(^);
753 END_CASE(JSOP_BITXOR)
755 BEGIN_CASE(JSOP_BITAND)
756 BITWISE_OP(&);
757 END_CASE(JSOP_BITAND)
759 #define RELATIONAL_OP(OP) \
760 JS_BEGIN_MACRO \
761 rval = FETCH_OPND(-1); \
762 lval = FETCH_OPND(-2); \
763 /* Optimize for two int-tagged operands (typical loop control). */ \
764 if ((lval & rval) & JSVAL_INT) { \
765 cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval); \
766 } else { \
767 if (!JSVAL_IS_PRIMITIVE(lval)) \
768 DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \
769 if (!JSVAL_IS_PRIMITIVE(rval)) \
770 DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \
771 if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { \
772 str = JSVAL_TO_STRING(lval); \
773 str2 = JSVAL_TO_STRING(rval); \
774 cond = js_CompareStrings(str, str2) OP 0; \
775 } else { \
776 VALUE_TO_NUMBER(cx, lval, d); \
777 VALUE_TO_NUMBER(cx, rval, d2); \
778 cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \
781 TRY_BRANCH_AFTER_COND(cond, 2); \
782 regs.sp--; \
783 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
784 JS_END_MACRO
787 * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies
788 * because they begin if/else chains, so callers must not put semicolons after
789 * the call expressions!
791 #if JS_HAS_XML_SUPPORT
792 #define XML_EQUALITY_OP(OP) \
793 if ((ltmp == JSVAL_OBJECT && \
794 (obj2 = JSVAL_TO_OBJECT(lval)) && \
795 OBJECT_IS_XML(cx, obj2)) || \
796 (rtmp == JSVAL_OBJECT && \
797 (obj2 = JSVAL_TO_OBJECT(rval)) && \
798 OBJECT_IS_XML(cx, obj2))) { \
799 if (JSVAL_IS_OBJECT(rval) && obj2 == JSVAL_TO_OBJECT(rval)) \
800 rval = lval; \
801 if (!js_TestXMLEquality(cx, obj2, rval, &cond)) \
802 goto error; \
803 cond = cond OP JS_TRUE; \
804 } else
806 #define EXTENDED_EQUALITY_OP(OP) \
807 if (ltmp == JSVAL_OBJECT && \
808 (obj2 = JSVAL_TO_OBJECT(lval)) && \
809 ((clasp = obj2->getClass())->flags & JSCLASS_IS_EXTENDED) && \
810 (((JSExtendedClass *) clasp)->equality)) { \
811 if (!((JSExtendedClass *) clasp)->equality(cx, obj2, rval, &cond)) \
812 goto error; \
813 cond = cond OP JS_TRUE; \
814 } else
815 #else
816 #define XML_EQUALITY_OP(OP) /* nothing */
817 #define EXTENDED_EQUALITY_OP(OP) /* nothing */
818 #endif
820 #define EQUALITY_OP(OP, IFNAN) \
821 JS_BEGIN_MACRO \
822 rval = FETCH_OPND(-1); \
823 lval = FETCH_OPND(-2); \
824 ltmp = JSVAL_TAG(lval); \
825 rtmp = JSVAL_TAG(rval); \
826 XML_EQUALITY_OP(OP) \
827 if (ltmp == rtmp) { \
828 if (ltmp == JSVAL_STRING) { \
829 str = JSVAL_TO_STRING(lval); \
830 str2 = JSVAL_TO_STRING(rval); \
831 cond = js_EqualStrings(str, str2) OP JS_TRUE; \
832 } else if (ltmp == JSVAL_DOUBLE) { \
833 d = *JSVAL_TO_DOUBLE(lval); \
834 d2 = *JSVAL_TO_DOUBLE(rval); \
835 cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \
836 } else { \
837 EXTENDED_EQUALITY_OP(OP) \
838 /* Handle all undefined (=>NaN) and int combinations. */ \
839 cond = lval OP rval; \
841 } else { \
842 if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) { \
843 cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) OP 1; \
844 } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) { \
845 cond = 1 OP 0; \
846 } else { \
847 if (ltmp == JSVAL_OBJECT) { \
848 DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval); \
849 ltmp = JSVAL_TAG(lval); \
850 } else if (rtmp == JSVAL_OBJECT) { \
851 DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); \
852 rtmp = JSVAL_TAG(rval); \
854 if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) { \
855 str = JSVAL_TO_STRING(lval); \
856 str2 = JSVAL_TO_STRING(rval); \
857 cond = js_EqualStrings(str, str2) OP JS_TRUE; \
858 } else { \
859 VALUE_TO_NUMBER(cx, lval, d); \
860 VALUE_TO_NUMBER(cx, rval, d2); \
861 cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \
865 TRY_BRANCH_AFTER_COND(cond, 2); \
866 regs.sp--; \
867 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
868 JS_END_MACRO
870 BEGIN_CASE(JSOP_EQ)
871 EQUALITY_OP(==, JS_FALSE);
872 END_CASE(JSOP_EQ)
874 BEGIN_CASE(JSOP_NE)
875 EQUALITY_OP(!=, JS_TRUE);
876 END_CASE(JSOP_NE)
878 #define STRICT_EQUALITY_OP(OP) \
879 JS_BEGIN_MACRO \
880 rval = FETCH_OPND(-1); \
881 lval = FETCH_OPND(-2); \
882 cond = js_StrictlyEqual(cx, lval, rval) OP JS_TRUE; \
883 regs.sp--; \
884 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
885 JS_END_MACRO
887 BEGIN_CASE(JSOP_STRICTEQ)
888 STRICT_EQUALITY_OP(==);
889 END_CASE(JSOP_STRICTEQ)
891 BEGIN_CASE(JSOP_STRICTNE)
892 STRICT_EQUALITY_OP(!=);
893 END_CASE(JSOP_STRICTNE)
895 BEGIN_CASE(JSOP_CASE)
896 STRICT_EQUALITY_OP(==);
897 (void) POP();
898 if (cond) {
899 len = GET_JUMP_OFFSET(regs.pc);
900 BRANCH(len);
902 PUSH(lval);
903 END_CASE(JSOP_CASE)
905 BEGIN_CASE(JSOP_CASEX)
906 STRICT_EQUALITY_OP(==);
907 (void) POP();
908 if (cond) {
909 len = GET_JUMPX_OFFSET(regs.pc);
910 BRANCH(len);
912 PUSH(lval);
913 END_CASE(JSOP_CASEX)
915 BEGIN_CASE(JSOP_LT)
916 RELATIONAL_OP(<);
917 END_CASE(JSOP_LT)
919 BEGIN_CASE(JSOP_LE)
920 RELATIONAL_OP(<=);
921 END_CASE(JSOP_LE)
923 BEGIN_CASE(JSOP_GT)
924 RELATIONAL_OP(>);
925 END_CASE(JSOP_GT)
927 BEGIN_CASE(JSOP_GE)
928 RELATIONAL_OP(>=);
929 END_CASE(JSOP_GE)
931 #undef EQUALITY_OP
932 #undef RELATIONAL_OP
934 #define SIGNED_SHIFT_OP(OP) \
935 JS_BEGIN_MACRO \
936 FETCH_INT(cx, -2, i); \
937 FETCH_INT(cx, -1, j); \
938 i = i OP (j & 31); \
939 regs.sp--; \
940 STORE_INT(cx, -1, i); \
941 JS_END_MACRO
943 BEGIN_CASE(JSOP_LSH)
944 SIGNED_SHIFT_OP(<<);
945 END_CASE(JSOP_LSH)
947 BEGIN_CASE(JSOP_RSH)
948 SIGNED_SHIFT_OP(>>);
949 END_CASE(JSOP_RSH)
951 BEGIN_CASE(JSOP_URSH)
953 uint32_t u;
955 FETCH_UINT(cx, -2, u);
956 FETCH_INT(cx, -1, j);
957 u >>= (j & 31);
958 regs.sp--;
959 STORE_UINT(cx, -1, u);
961 END_CASE(JSOP_URSH)
963 #undef BITWISE_OP
964 #undef SIGNED_SHIFT_OP
966 BEGIN_CASE(JSOP_ADD)
967 rval = FETCH_OPND(-1);
968 lval = FETCH_OPND(-2);
969 #if JS_HAS_XML_SUPPORT
970 if (!JSVAL_IS_PRIMITIVE(lval) &&
971 (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) &&
972 VALUE_IS_XML(cx, rval)) {
973 if (!js_ConcatenateXML(cx, obj2, rval, &rval))
974 goto error;
975 regs.sp--;
976 STORE_OPND(-1, rval);
977 } else
978 #endif
980 if (!JSVAL_IS_PRIMITIVE(lval))
981 DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval);
982 if (!JSVAL_IS_PRIMITIVE(rval))
983 DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval);
984 if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) {
985 if (cond) {
986 str = JSVAL_TO_STRING(lval);
987 str2 = js_ValueToString(cx, rval);
988 if (!str2)
989 goto error;
990 regs.sp[-1] = STRING_TO_JSVAL(str2);
991 } else {
992 str2 = JSVAL_TO_STRING(rval);
993 str = js_ValueToString(cx, lval);
994 if (!str)
995 goto error;
996 regs.sp[-2] = STRING_TO_JSVAL(str);
998 str = js_ConcatStrings(cx, str, str2);
999 if (!str)
1000 goto error;
1001 regs.sp--;
1002 STORE_OPND(-1, STRING_TO_JSVAL(str));
1003 } else {
1004 VALUE_TO_NUMBER(cx, lval, d);
1005 VALUE_TO_NUMBER(cx, rval, d2);
1006 d += d2;
1007 regs.sp--;
1008 STORE_NUMBER(cx, -1, d);
1011 END_CASE(JSOP_ADD)
1013 BEGIN_CASE(JSOP_OBJTOSTR)
1014 rval = FETCH_OPND(-1);
1015 if (!JSVAL_IS_PRIMITIVE(rval)) {
1016 str = js_ValueToString(cx, rval);
1017 if (!str)
1018 goto error;
1019 STORE_OPND(-1, STRING_TO_JSVAL(str));
1021 END_CASE(JSOP_OBJTOSTR)
1023 BEGIN_CASE(JSOP_CONCATN)
1025 JSCharBuffer buf(cx);
1026 argc = GET_ARGC(regs.pc);
1027 for (vp = regs.sp - argc; vp < regs.sp; vp++) {
1028 JS_ASSERT(JSVAL_IS_PRIMITIVE(*vp));
1029 if (!js_ValueToCharBuffer(cx, *vp, buf))
1030 goto error;
1032 str = js_NewStringFromCharBuffer(cx, buf);
1033 if (!str)
1034 goto error;
1035 regs.sp -= argc - 1;
1036 STORE_OPND(-1, STRING_TO_JSVAL(str));
1038 END_CASE(JSOP_CONCATN)
1040 #define BINARY_OP(OP) \
1041 JS_BEGIN_MACRO \
1042 FETCH_NUMBER(cx, -2, d); \
1043 FETCH_NUMBER(cx, -1, d2); \
1044 d = d OP d2; \
1045 regs.sp--; \
1046 STORE_NUMBER(cx, -1, d); \
1047 JS_END_MACRO
1049 BEGIN_CASE(JSOP_SUB)
1050 BINARY_OP(-);
1051 END_CASE(JSOP_SUB)
1053 BEGIN_CASE(JSOP_MUL)
1054 BINARY_OP(*);
1055 END_CASE(JSOP_MUL)
1057 BEGIN_CASE(JSOP_DIV)
1058 FETCH_NUMBER(cx, -1, d2);
1059 FETCH_NUMBER(cx, -2, d);
1060 regs.sp--;
1061 if (d2 == 0) {
1062 #ifdef XP_WIN
1063 /* XXX MSVC miscompiles such that (NaN == 0) */
1064 if (JSDOUBLE_IS_NaN(d2))
1065 rval = rt->NaNValue;
1066 else
1067 #endif
1068 if (d == 0 || JSDOUBLE_IS_NaN(d))
1069 rval = rt->NaNValue;
1070 else if (JSDOUBLE_IS_NEG(d) != JSDOUBLE_IS_NEG(d2))
1071 rval = rt->negativeInfinityValue;
1072 else
1073 rval = rt->positiveInfinityValue;
1074 STORE_OPND(-1, rval);
1075 } else {
1076 d /= d2;
1077 STORE_NUMBER(cx, -1, d);
1079 END_CASE(JSOP_DIV)
1081 BEGIN_CASE(JSOP_MOD)
1082 FETCH_NUMBER(cx, -1, d2);
1083 FETCH_NUMBER(cx, -2, d);
1084 regs.sp--;
1085 if (d2 == 0) {
1086 STORE_OPND(-1, rt->NaNValue);
1087 } else {
1088 d = js_fmod(d, d2);
1089 STORE_NUMBER(cx, -1, d);
1091 END_CASE(JSOP_MOD)
1093 BEGIN_CASE(JSOP_NOT)
1094 POP_BOOLEAN(cx, rval, cond);
1095 PUSH_OPND(BOOLEAN_TO_JSVAL(!cond));
1096 END_CASE(JSOP_NOT)
1098 BEGIN_CASE(JSOP_BITNOT)
1099 FETCH_INT(cx, -1, i);
1100 i = ~i;
1101 STORE_INT(cx, -1, i);
1102 END_CASE(JSOP_BITNOT)
1104 BEGIN_CASE(JSOP_NEG)
1106 * When the operand is int jsval, INT_FITS_IN_JSVAL(i) implies
1107 * INT_FITS_IN_JSVAL(-i) unless i is 0 or JSVAL_INT_MIN when the
1108 * results, -0.0 or JSVAL_INT_MAX + 1, are jsdouble values.
1110 rval = FETCH_OPND(-1);
1111 if (JSVAL_IS_INT(rval) &&
1112 rval != INT_TO_JSVAL(JSVAL_INT_MIN) &&
1113 (i = JSVAL_TO_INT(rval)) != 0) {
1114 JS_STATIC_ASSERT(!INT_FITS_IN_JSVAL(-JSVAL_INT_MIN));
1115 i = -i;
1116 JS_ASSERT(INT_FITS_IN_JSVAL(i));
1117 regs.sp[-1] = INT_TO_JSVAL(i);
1118 } else {
1119 if (!ValueToNumber(cx, regs.sp[-1], &d))
1120 goto error;
1121 d = -d;
1122 if (!js_NewNumberInRootedValue(cx, d, &regs.sp[-1]))
1123 goto error;
1125 END_CASE(JSOP_NEG)
1127 BEGIN_CASE(JSOP_POS)
1128 if (!ValueToNumberValue(cx, &regs.sp[-1]))
1129 goto error;
1130 END_CASE(JSOP_POS)
1132 BEGIN_CASE(JSOP_DELNAME)
1133 LOAD_ATOM(0);
1134 id = ATOM_TO_JSID(atom);
1135 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
1136 goto error;
1138 /* ECMA says to return true if name is undefined or inherited. */
1139 PUSH_OPND(JSVAL_TRUE);
1140 if (prop) {
1141 obj2->dropProperty(cx, prop);
1142 if (!obj->deleteProperty(cx, id, &regs.sp[-1]))
1143 goto error;
1145 END_CASE(JSOP_DELNAME)
1147 BEGIN_CASE(JSOP_DELPROP)
1148 LOAD_ATOM(0);
1149 id = ATOM_TO_JSID(atom);
1150 PROPERTY_OP(-1, obj->deleteProperty(cx, id, &rval));
1151 STORE_OPND(-1, rval);
1152 END_CASE(JSOP_DELPROP)
1154 BEGIN_CASE(JSOP_DELELEM)
1155 ELEMENT_OP(-1, obj->deleteProperty(cx, id, &rval));
1156 regs.sp--;
1157 STORE_OPND(-1, rval);
1158 END_CASE(JSOP_DELELEM)
1160 BEGIN_CASE(JSOP_TYPEOFEXPR)
1161 BEGIN_CASE(JSOP_TYPEOF)
1162 rval = FETCH_OPND(-1);
1163 type = JS_TypeOfValue(cx, rval);
1164 atom = rt->atomState.typeAtoms[type];
1165 STORE_OPND(-1, ATOM_KEY(atom));
1166 END_CASE(JSOP_TYPEOF)
1168 BEGIN_CASE(JSOP_VOID)
1169 STORE_OPND(-1, JSVAL_VOID);
1170 END_CASE(JSOP_VOID)
1172 BEGIN_CASE(JSOP_INCELEM)
1173 BEGIN_CASE(JSOP_DECELEM)
1174 BEGIN_CASE(JSOP_ELEMINC)
1175 BEGIN_CASE(JSOP_ELEMDEC)
1177 * Delay fetching of id until we have the object to ensure the proper
1178 * evaluation order. See bug 372331.
1180 id = 0;
1181 i = -2;
1182 goto fetch_incop_obj;
1184 BEGIN_CASE(JSOP_INCPROP)
1185 BEGIN_CASE(JSOP_DECPROP)
1186 BEGIN_CASE(JSOP_PROPINC)
1187 BEGIN_CASE(JSOP_PROPDEC)
1188 LOAD_ATOM(0);
1189 id = ATOM_TO_JSID(atom);
1190 i = -1;
1192 fetch_incop_obj:
1193 FETCH_OBJECT(cx, i, lval, obj);
1194 if (id == 0)
1195 FETCH_ELEMENT_ID(obj, -1, id);
1196 goto do_incop;
1198 BEGIN_CASE(JSOP_INCNAME)
1199 BEGIN_CASE(JSOP_DECNAME)
1200 BEGIN_CASE(JSOP_NAMEINC)
1201 BEGIN_CASE(JSOP_NAMEDEC)
1203 PropertyCacheEntry *entry;
1205 obj = fp->scopeChain;
1207 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
1208 if (!atom) {
1209 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
1210 if (obj == obj2 && entry->vword.isSlot()) {
1211 slot = entry->vword.toSlot();
1212 JS_ASSERT(slot < obj->scope()->freeslot);
1213 rval = obj->lockedGetSlot(slot);
1214 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {
1215 rtmp = rval;
1216 rval += (js_CodeSpec[op].format & JOF_INC) ? 2 : -2;
1217 if (!(js_CodeSpec[op].format & JOF_POST))
1218 rtmp = rval;
1219 obj->lockedSetSlot(slot, rval);
1220 PUSH_OPND(rtmp);
1221 len = JSOP_INCNAME_LENGTH;
1222 DO_NEXT_OP(len);
1225 LOAD_ATOM(0);
1228 id = ATOM_TO_JSID(atom);
1229 if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
1230 goto error;
1231 if (!prop)
1232 goto atom_not_defined;
1233 obj2->dropProperty(cx, prop);
1236 do_incop:
1238 const JSCodeSpec *cs;
1239 jsval v;
1242 * We need a root to store the value to leave on the stack until
1243 * we have done with obj->setProperty.
1245 PUSH_OPND(JSVAL_NULL);
1246 if (!obj->getProperty(cx, id, &regs.sp[-1]))
1247 goto error;
1249 cs = &js_CodeSpec[op];
1250 JS_ASSERT(cs->ndefs == 1);
1251 JS_ASSERT((cs->format & JOF_TMPSLOT_MASK) == JOF_TMPSLOT2);
1252 v = regs.sp[-1];
1253 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(v))) {
1254 jsval incr;
1256 incr = (cs->format & JOF_INC) ? 2 : -2;
1257 if (cs->format & JOF_POST) {
1258 regs.sp[-1] = v + incr;
1259 } else {
1260 v += incr;
1261 regs.sp[-1] = v;
1263 fp->flags |= JSFRAME_ASSIGNING;
1264 ok = obj->setProperty(cx, id, &regs.sp[-1]);
1265 fp->flags &= ~JSFRAME_ASSIGNING;
1266 if (!ok)
1267 goto error;
1270 * We must set regs.sp[-1] to v for both post and pre increments
1271 * as the setter overwrites regs.sp[-1].
1273 regs.sp[-1] = v;
1274 } else {
1275 /* We need an extra root for the result. */
1276 PUSH_OPND(JSVAL_NULL);
1277 if (!js_DoIncDec(cx, cs, &regs.sp[-2], &regs.sp[-1]))
1278 goto error;
1279 fp->flags |= JSFRAME_ASSIGNING;
1280 ok = obj->setProperty(cx, id, &regs.sp[-1]);
1281 fp->flags &= ~JSFRAME_ASSIGNING;
1282 if (!ok)
1283 goto error;
1284 regs.sp--;
1287 if (cs->nuses == 0) {
1288 /* regs.sp[-1] already contains the result of name increment. */
1289 } else {
1290 rtmp = regs.sp[-1];
1291 regs.sp -= cs->nuses;
1292 regs.sp[-1] = rtmp;
1294 len = cs->length;
1295 DO_NEXT_OP(len);
1299 jsval incr, incr2;
1301 /* Position cases so the most frequent i++ does not need a jump. */
1302 BEGIN_CASE(JSOP_DECARG)
1303 incr = -2; incr2 = -2; goto do_arg_incop;
1304 BEGIN_CASE(JSOP_ARGDEC)
1305 incr = -2; incr2 = 0; goto do_arg_incop;
1306 BEGIN_CASE(JSOP_INCARG)
1307 incr = 2; incr2 = 2; goto do_arg_incop;
1308 BEGIN_CASE(JSOP_ARGINC)
1309 incr = 2; incr2 = 0;
1311 do_arg_incop:
1312 slot = GET_ARGNO(regs.pc);
1313 JS_ASSERT(slot < fp->fun->nargs);
1314 METER_SLOT_OP(op, slot);
1315 vp = fp->argv + slot;
1316 goto do_int_fast_incop;
1318 BEGIN_CASE(JSOP_DECLOCAL)
1319 incr = -2; incr2 = -2; goto do_local_incop;
1320 BEGIN_CASE(JSOP_LOCALDEC)
1321 incr = -2; incr2 = 0; goto do_local_incop;
1322 BEGIN_CASE(JSOP_INCLOCAL)
1323 incr = 2; incr2 = 2; goto do_local_incop;
1324 BEGIN_CASE(JSOP_LOCALINC)
1325 incr = 2; incr2 = 0;
1328 * do_local_incop comes right before do_int_fast_incop as we want to
1329 * avoid an extra jump for variable cases as local++ is more frequent
1330 * than arg++.
1332 do_local_incop:
1333 slot = GET_SLOTNO(regs.pc);
1334 JS_ASSERT(slot < fp->script->nslots);
1335 vp = fp->slots + slot;
1336 METER_SLOT_OP(op, slot);
1337 vp = fp->slots + slot;
1339 do_int_fast_incop:
1340 rval = *vp;
1341 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {
1342 *vp = rval + incr;
1343 JS_ASSERT(JSOP_INCARG_LENGTH == js_CodeSpec[op].length);
1344 SKIP_POP_AFTER_SET(JSOP_INCARG_LENGTH, 0);
1345 PUSH_OPND(rval + incr2);
1346 } else {
1347 PUSH_OPND(rval);
1348 if (!js_DoIncDec(cx, &js_CodeSpec[op], &regs.sp[-1], vp))
1349 goto error;
1351 len = JSOP_INCARG_LENGTH;
1352 JS_ASSERT(len == js_CodeSpec[op].length);
1353 DO_NEXT_OP(len);
1356 /* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */
1357 #define FAST_GLOBAL_INCREMENT_OP(SLOWOP,INCR,INCR2) \
1358 op2 = SLOWOP; \
1359 incr = INCR; \
1360 incr2 = INCR2; \
1361 goto do_global_incop
1364 jsval incr, incr2;
1366 BEGIN_CASE(JSOP_DECGVAR)
1367 FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, -2, -2);
1368 BEGIN_CASE(JSOP_GVARDEC)
1369 FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, -2, 0);
1370 BEGIN_CASE(JSOP_INCGVAR)
1371 FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, 2, 2);
1372 BEGIN_CASE(JSOP_GVARINC)
1373 FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, 2, 0);
1375 #undef FAST_GLOBAL_INCREMENT_OP
1377 do_global_incop:
1378 JS_ASSERT((js_CodeSpec[op].format & JOF_TMPSLOT_MASK) ==
1379 JOF_TMPSLOT2);
1380 slot = GET_SLOTNO(regs.pc);
1381 JS_ASSERT(slot < GlobalVarCount(fp));
1382 METER_SLOT_OP(op, slot);
1383 lval = fp->slots[slot];
1384 if (JSVAL_IS_NULL(lval)) {
1385 op = op2;
1386 DO_OP();
1388 slot = JSVAL_TO_INT(lval);
1389 JS_ASSERT(fp->varobj(cx) == cx->activeCallStack()->getInitialVarObj());
1390 rval = cx->activeCallStack()->getInitialVarObj()->getSlotMT(cx, slot);
1391 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {
1392 PUSH_OPND(rval + incr2);
1393 rval += incr;
1394 } else {
1395 PUSH_OPND(rval);
1396 PUSH_OPND(JSVAL_NULL); /* Extra root */
1397 if (!js_DoIncDec(cx, &js_CodeSpec[op], &regs.sp[-2], &regs.sp[-1]))
1398 goto error;
1399 rval = regs.sp[-1];
1400 --regs.sp;
1402 fp->varobj(cx)->setSlotMT(cx, slot, rval);
1403 len = JSOP_INCGVAR_LENGTH; /* all gvar incops are same length */
1404 JS_ASSERT(len == js_CodeSpec[op].length);
1405 DO_NEXT_OP(len);
1408 #define COMPUTE_THIS(cx, fp, obj) \
1409 JS_BEGIN_MACRO \
1410 if (!(obj = (fp)->getThisObject(cx))) \
1411 goto error; \
1412 JS_END_MACRO
1414 BEGIN_CASE(JSOP_THIS)
1415 COMPUTE_THIS(cx, fp, obj);
1416 PUSH_OPND(OBJECT_TO_JSVAL(obj));
1417 END_CASE(JSOP_THIS)
1419 BEGIN_CASE(JSOP_UNBRANDTHIS)
1420 COMPUTE_THIS(cx, fp, obj);
1421 if (!obj->unbrand(cx))
1422 goto error;
1423 END_CASE(JSOP_UNBRANDTHIS)
1425 BEGIN_CASE(JSOP_GETTHISPROP)
1426 i = 0;
1427 COMPUTE_THIS(cx, fp, obj);
1428 PUSH(JSVAL_NULL);
1429 goto do_getprop_with_obj;
1431 #undef COMPUTE_THIS
1433 BEGIN_CASE(JSOP_GETARGPROP)
1434 i = ARGNO_LEN;
1435 slot = GET_ARGNO(regs.pc);
1436 JS_ASSERT(slot < fp->fun->nargs);
1437 PUSH_OPND(fp->argv[slot]);
1438 goto do_getprop_body;
1440 BEGIN_CASE(JSOP_GETLOCALPROP)
1441 i = SLOTNO_LEN;
1442 slot = GET_SLOTNO(regs.pc);
1443 JS_ASSERT(slot < script->nslots);
1444 PUSH_OPND(fp->slots[slot]);
1445 goto do_getprop_body;
1447 BEGIN_CASE(JSOP_GETPROP)
1448 BEGIN_CASE(JSOP_GETXPROP)
1449 i = 0;
1451 do_getprop_body:
1452 lval = FETCH_OPND(-1);
1454 do_getprop_with_lval:
1455 VALUE_TO_OBJECT(cx, -1, lval, obj);
1457 do_getprop_with_obj:
1458 do {
1459 JSObject *aobj;
1460 PropertyCacheEntry *entry;
1463 * We do not impose the method read barrier if in an imacro,
1464 * assuming any property gets it does (e.g., for 'toString'
1465 * from JSOP_NEW) will not be leaked to the calling script.
1467 aobj = js_GetProtoIfDenseArray(obj);
1469 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
1470 if (!atom) {
1471 ASSERT_VALID_PROPERTY_CACHE_HIT(i, aobj, obj2, entry);
1472 if (entry->vword.isObject()) {
1473 rval = entry->vword.toJsval();
1474 } else if (entry->vword.isSlot()) {
1475 slot = entry->vword.toSlot();
1476 JS_ASSERT(slot < obj2->scope()->freeslot);
1477 rval = obj2->lockedGetSlot(slot);
1478 } else {
1479 JS_ASSERT(entry->vword.isSprop());
1480 sprop = entry->vword.toSprop();
1481 NATIVE_GET(cx, obj, obj2, sprop,
1482 fp->imacpc ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER,
1483 &rval);
1485 break;
1488 id = ATOM_TO_JSID(atom);
1489 if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)
1490 ? !js_GetPropertyHelper(cx, obj, id,
1491 fp->imacpc
1492 ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
1493 : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER,
1494 &rval)
1495 : !obj->getProperty(cx, id, &rval)) {
1496 goto error;
1498 } while (0);
1500 STORE_OPND(-1, rval);
1501 JS_ASSERT(JSOP_GETPROP_LENGTH + i == js_CodeSpec[op].length);
1502 len = JSOP_GETPROP_LENGTH + i;
1503 END_VARLEN_CASE
1505 BEGIN_CASE(JSOP_LENGTH)
1506 lval = FETCH_OPND(-1);
1507 if (JSVAL_IS_STRING(lval)) {
1508 str = JSVAL_TO_STRING(lval);
1509 regs.sp[-1] = INT_TO_JSVAL(str->length());
1510 } else if (!JSVAL_IS_PRIMITIVE(lval)) {
1511 obj = JSVAL_TO_OBJECT(lval);
1512 if (obj->isArray()) {
1513 jsuint length = obj->getArrayLength();
1515 if (length <= JSVAL_INT_MAX)
1516 regs.sp[-1] = INT_TO_JSVAL(length);
1517 else if (!js_NewDoubleInRootedValue(cx, (jsdouble) length, &regs.sp[-1]))
1518 goto error;
1519 } else if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
1520 uint32 length = obj->getArgsLength();
1522 JS_ASSERT(INT_FITS_IN_JSVAL(length));
1523 regs.sp[-1] = INT_TO_JSVAL(length);
1524 } else {
1525 i = -2;
1526 goto do_getprop_with_lval;
1528 } else {
1529 i = -2;
1530 goto do_getprop_with_lval;
1532 END_CASE(JSOP_LENGTH)
1534 BEGIN_CASE(JSOP_CALLPROP)
1536 JSObject *aobj;
1537 PropertyCacheEntry *entry;
1539 lval = FETCH_OPND(-1);
1540 if (!JSVAL_IS_PRIMITIVE(lval)) {
1541 obj = JSVAL_TO_OBJECT(lval);
1542 } else {
1543 JSProtoKey protoKey;
1544 if (JSVAL_IS_STRING(lval)) {
1545 protoKey = JSProto_String;
1546 } else if (JSVAL_IS_NUMBER(lval)) {
1547 protoKey = JSProto_Number;
1548 } else if (JSVAL_IS_BOOLEAN(lval)) {
1549 protoKey = JSProto_Boolean;
1550 } else {
1551 JS_ASSERT(JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval));
1552 js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
1553 goto error;
1555 if (!js_GetClassPrototype(cx, NULL, protoKey, &obj))
1556 goto error;
1559 aobj = js_GetProtoIfDenseArray(obj);
1561 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
1562 if (!atom) {
1563 ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry);
1564 if (entry->vword.isObject()) {
1565 rval = entry->vword.toJsval();
1566 } else if (entry->vword.isSlot()) {
1567 slot = entry->vword.toSlot();
1568 JS_ASSERT(slot < obj2->scope()->freeslot);
1569 rval = obj2->lockedGetSlot(slot);
1570 } else {
1571 JS_ASSERT(entry->vword.isSprop());
1572 sprop = entry->vword.toSprop();
1573 NATIVE_GET(cx, obj, obj2, sprop, JSGET_NO_METHOD_BARRIER, &rval);
1575 STORE_OPND(-1, rval);
1576 PUSH_OPND(lval);
1577 goto end_callprop;
1581 * Cache miss: use the immediate atom that was loaded for us under
1582 * PropertyCache::test.
1584 id = ATOM_TO_JSID(atom);
1585 PUSH(JSVAL_NULL);
1586 if (!JSVAL_IS_PRIMITIVE(lval)) {
1587 if (!js_GetMethod(cx, obj, id,
1588 JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)
1589 ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
1590 : JSGET_NO_METHOD_BARRIER,
1591 &rval)) {
1592 goto error;
1594 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
1595 STORE_OPND(-2, rval);
1596 } else {
1597 JS_ASSERT(obj->map->ops->getProperty == js_GetProperty);
1598 if (!js_GetPropertyHelper(cx, obj, id,
1599 JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER,
1600 &rval)) {
1601 goto error;
1603 STORE_OPND(-1, lval);
1604 STORE_OPND(-2, rval);
1607 end_callprop:
1608 /* Wrap primitive lval in object clothing if necessary. */
1609 if (JSVAL_IS_PRIMITIVE(lval)) {
1610 /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */
1611 if (!VALUE_IS_FUNCTION(cx, rval) ||
1612 (obj = JSVAL_TO_OBJECT(rval),
1613 fun = GET_FUNCTION_PRIVATE(cx, obj),
1614 !PRIMITIVE_THIS_TEST(fun, lval))) {
1615 if (!js_PrimitiveToObject(cx, &regs.sp[-1]))
1616 goto error;
1619 #if JS_HAS_NO_SUCH_METHOD
1620 if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) {
1621 LOAD_ATOM(0);
1622 regs.sp[-2] = ATOM_KEY(atom);
1623 if (!js_OnUnknownMethod(cx, regs.sp - 2))
1624 goto error;
1626 #endif
1628 END_CASE(JSOP_CALLPROP)
1630 BEGIN_CASE(JSOP_UNBRAND)
1631 JS_ASSERT(regs.sp - fp->slots >= 1);
1632 lval = FETCH_OPND(-1);
1633 obj = JSVAL_TO_OBJECT(lval);
1634 if (!obj->unbrand(cx))
1635 goto error;
1636 END_CASE(JSOP_UNBRAND)
1638 BEGIN_CASE(JSOP_SETNAME)
1639 BEGIN_CASE(JSOP_SETPROP)
1640 BEGIN_CASE(JSOP_SETMETHOD)
1641 rval = FETCH_OPND(-1);
1642 JS_ASSERT_IF(op == JSOP_SETMETHOD, VALUE_IS_FUNCTION(cx, rval));
1643 lval = FETCH_OPND(-2);
1644 JS_ASSERT_IF(op == JSOP_SETNAME, !JSVAL_IS_PRIMITIVE(lval));
1645 VALUE_TO_OBJECT(cx, -2, lval, obj);
1647 do {
1648 PropertyCache *cache = &JS_PROPERTY_CACHE(cx);
1649 PropertyCacheEntry *entry = NULL;
1650 atom = NULL;
1653 * Probe the property cache, specializing for two important
1654 * set-property cases. First:
1656 * function f(a, b, c) {
1657 * var o = {p:a, q:b, r:c};
1658 * return o;
1661 * or similar real-world cases, which evolve a newborn native
1662 * object predicatably through some bounded number of property
1663 * additions. And second:
1665 * o.p = x;
1667 * in a frequently executed method or loop body, where p will
1668 * (possibly after the first iteration) always exist in native
1669 * object o.
1671 if (cache->testForSet(cx, regs.pc, obj, &entry, &obj2, &atom)) {
1673 * Fast property cache hit, only partially confirmed by
1674 * testForSet. We know that the entry applies to regs.pc and
1675 * that obj's shape matches.
1677 * The entry predicts either a new property to be added
1678 * directly to obj by this set, or on an existing "own"
1679 * property, or on a prototype property that has a setter.
1681 JS_ASSERT(entry->vword.isSprop());
1682 sprop = entry->vword.toSprop();
1683 JS_ASSERT_IF(sprop->isDataDescriptor(), sprop->writable());
1684 JS_ASSERT_IF(sprop->hasSlot(), entry->vcapTag() == 0);
1686 JSScope *scope = obj->scope();
1687 JS_ASSERT(!scope->sealed());
1690 * Fastest path: check whether the cached sprop is already
1691 * in scope and call NATIVE_SET and break to get out of the
1692 * do-while(0). But we can call NATIVE_SET only if obj owns
1693 * scope or sprop is shared.
1695 bool checkForAdd;
1696 if (!sprop->hasSlot()) {
1697 if (entry->vcapTag() == 0 ||
1698 ((obj2 = obj->getProto()) &&
1699 obj2->isNative() &&
1700 obj2->shape() == entry->vshape())) {
1701 goto fast_set_propcache_hit;
1704 /* The cache entry doesn't apply. vshape mismatch. */
1705 checkForAdd = false;
1706 } else if (!scope->isSharedEmpty()) {
1707 if (sprop == scope->lastProperty() || scope->hasProperty(sprop)) {
1708 fast_set_propcache_hit:
1709 PCMETER(cache->pchits++);
1710 PCMETER(cache->setpchits++);
1711 NATIVE_SET(cx, obj, sprop, entry, &rval);
1712 break;
1714 checkForAdd = sprop->hasSlot() && sprop->parent == scope->lastProperty();
1715 } else {
1717 * We check that cx own obj here and will continue to
1718 * own it after js_GetMutableScope returns so we can
1719 * continue to skip JS_UNLOCK_OBJ calls.
1721 JS_ASSERT(CX_OWNS_OBJECT_TITLE(cx, obj));
1722 scope = js_GetMutableScope(cx, obj);
1723 JS_ASSERT(CX_OWNS_OBJECT_TITLE(cx, obj));
1724 if (!scope)
1725 goto error;
1726 checkForAdd = !sprop->parent;
1729 if (checkForAdd &&
1730 entry->vshape() == rt->protoHazardShape &&
1731 sprop->hasDefaultSetter() &&
1732 (slot = sprop->slot) == scope->freeslot) {
1734 * Fast path: adding a plain old property that was once
1735 * at the frontier of the property tree, whose slot is
1736 * next to claim among the allocated slots in obj,
1737 * where scope->table has not been created yet.
1739 * We may want to remove hazard conditions above and
1740 * inline compensation code here, depending on
1741 * real-world workloads.
1743 PCMETER(cache->pchits++);
1744 PCMETER(cache->addpchits++);
1747 * Beware classes such as Function that use the
1748 * reserveSlots hook to allocate a number of reserved
1749 * slots that may vary with obj.
1751 if (slot < obj->numSlots() &&
1752 !obj->getClass()->reserveSlots) {
1753 ++scope->freeslot;
1754 } else {
1755 if (!js_AllocSlot(cx, obj, &slot))
1756 goto error;
1760 * If this obj's number of reserved slots differed, or
1761 * if something created a hash table for scope, we must
1762 * pay the price of JSScope::putProperty.
1764 * (A reserveSlots hook can cause scopes of the same
1765 * shape to have different freeslot values. This is
1766 * what causes the slot != sprop->slot case. See
1767 * js_GetMutableScope.)
1769 if (slot != sprop->slot || scope->table) {
1770 JSScopeProperty *sprop2 =
1771 scope->putProperty(cx, sprop->id,
1772 sprop->getter(), sprop->setter(),
1773 slot, sprop->attributes(),
1774 sprop->getFlags(), sprop->shortid);
1775 if (!sprop2) {
1776 js_FreeSlot(cx, obj, slot);
1777 goto error;
1779 sprop = sprop2;
1780 } else {
1781 scope->extend(cx, sprop);
1785 * No method change check here because here we are
1786 * adding a new property, not updating an existing
1787 * slot's value that might contain a method of a
1788 * branded scope.
1790 TRACE_2(SetPropHit, entry, sprop);
1791 obj->lockedSetSlot(slot, rval);
1794 * Purge the property cache of the id we may have just
1795 * shadowed in obj's scope and proto chains. We do this
1796 * after unlocking obj's scope to avoid lock nesting.
1798 js_PurgeScopeChain(cx, obj, sprop->id);
1799 break;
1801 PCMETER(cache->setpcmisses++);
1802 atom = NULL;
1803 } else if (!atom) {
1805 * Slower property cache hit, fully confirmed by testForSet (in
1806 * the slow path, via fullTest).
1808 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
1809 sprop = NULL;
1810 if (obj == obj2) {
1811 sprop = entry->vword.toSprop();
1812 JS_ASSERT(sprop->writable());
1813 JS_ASSERT(!obj2->scope()->sealed());
1814 NATIVE_SET(cx, obj, sprop, entry, &rval);
1816 if (sprop)
1817 break;
1820 if (!atom)
1821 LOAD_ATOM(0);
1822 id = ATOM_TO_JSID(atom);
1823 if (entry && JS_LIKELY(obj->map->ops->setProperty == js_SetProperty)) {
1824 uintN defineHow;
1825 if (op == JSOP_SETMETHOD)
1826 defineHow = JSDNP_CACHE_RESULT | JSDNP_SET_METHOD;
1827 else if (op == JSOP_SETNAME)
1828 defineHow = JSDNP_CACHE_RESULT | JSDNP_UNQUALIFIED;
1829 else
1830 defineHow = JSDNP_CACHE_RESULT;
1831 if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rval))
1832 goto error;
1833 } else {
1834 if (!obj->setProperty(cx, id, &rval))
1835 goto error;
1836 ABORT_RECORDING(cx, "Non-native set");
1838 } while (0);
1839 END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2);
1841 BEGIN_CASE(JSOP_GETELEM)
1842 /* Open-coded ELEMENT_OP optimized for strings and dense arrays. */
1843 lval = FETCH_OPND(-2);
1844 rval = FETCH_OPND(-1);
1845 if (JSVAL_IS_STRING(lval) && JSVAL_IS_INT(rval)) {
1846 str = JSVAL_TO_STRING(lval);
1847 i = JSVAL_TO_INT(rval);
1848 if ((size_t)i < str->length()) {
1849 str = JSString::getUnitString(cx, str, size_t(i));
1850 if (!str)
1851 goto error;
1852 rval = STRING_TO_JSVAL(str);
1853 goto end_getelem;
1857 VALUE_TO_OBJECT(cx, -2, lval, obj);
1858 if (JSVAL_IS_INT(rval)) {
1859 if (obj->isDenseArray()) {
1860 jsuint idx = jsuint(JSVAL_TO_INT(rval));
1862 if (idx < obj->getArrayLength() &&
1863 idx < obj->getDenseArrayCapacity()) {
1864 rval = obj->getDenseArrayElement(idx);
1865 if (rval != JSVAL_HOLE)
1866 goto end_getelem;
1868 /* Reload rval from the stack in the rare hole case. */
1869 rval = FETCH_OPND(-1);
1871 } else if (obj->isArguments()
1872 #ifdef JS_TRACER
1873 && !GetArgsPrivateNative(obj)
1874 #endif
1876 uint32 arg = uint32(JSVAL_TO_INT(rval));
1878 if (arg < obj->getArgsLength()) {
1879 JSStackFrame *afp = (JSStackFrame *) obj->getPrivate();
1880 if (afp) {
1881 rval = afp->argv[arg];
1882 goto end_getelem;
1885 rval = obj->getArgsElement(arg);
1886 if (rval != JSVAL_HOLE)
1887 goto end_getelem;
1888 rval = FETCH_OPND(-1);
1891 id = INT_JSVAL_TO_JSID(rval);
1892 } else {
1893 if (!js_InternNonIntElementId(cx, obj, rval, &id))
1894 goto error;
1897 if (!obj->getProperty(cx, id, &rval))
1898 goto error;
1899 end_getelem:
1900 regs.sp--;
1901 STORE_OPND(-1, rval);
1902 END_CASE(JSOP_GETELEM)
1904 BEGIN_CASE(JSOP_CALLELEM)
1905 ELEMENT_OP(-1, js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &rval));
1906 #if JS_HAS_NO_SUCH_METHOD
1907 if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) {
1908 regs.sp[-2] = regs.sp[-1];
1909 regs.sp[-1] = OBJECT_TO_JSVAL(obj);
1910 if (!js_OnUnknownMethod(cx, regs.sp - 2))
1911 goto error;
1912 } else
1913 #endif
1915 STORE_OPND(-2, rval);
1916 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
1918 END_CASE(JSOP_CALLELEM)
1920 BEGIN_CASE(JSOP_SETELEM)
1921 rval = FETCH_OPND(-1);
1922 FETCH_OBJECT(cx, -3, lval, obj);
1923 FETCH_ELEMENT_ID(obj, -2, id);
1924 do {
1925 if (obj->isDenseArray() && JSID_IS_INT(id)) {
1926 jsuint length;
1928 length = obj->getDenseArrayCapacity();
1929 i = JSID_TO_INT(id);
1930 if ((jsuint)i < length) {
1931 if (obj->getDenseArrayElement(i) == JSVAL_HOLE) {
1932 if (js_PrototypeHasIndexedProperties(cx, obj))
1933 break;
1934 if ((jsuint)i >= obj->getArrayLength())
1935 obj->setDenseArrayLength(i + 1);
1936 obj->incDenseArrayCountBy(1);
1938 obj->setDenseArrayElement(i, rval);
1939 goto end_setelem;
1942 } while (0);
1943 if (!obj->setProperty(cx, id, &rval))
1944 goto error;
1945 end_setelem:
1946 END_SET_CASE_STORE_RVAL(JSOP_SETELEM, 3)
1948 BEGIN_CASE(JSOP_ENUMELEM)
1949 /* Funky: the value to set is under the [obj, id] pair. */
1950 rval = FETCH_OPND(-3);
1951 FETCH_OBJECT(cx, -2, lval, obj);
1952 FETCH_ELEMENT_ID(obj, -1, id);
1953 if (!obj->setProperty(cx, id, &rval))
1954 goto error;
1955 regs.sp -= 3;
1956 END_CASE(JSOP_ENUMELEM)
1958 BEGIN_CASE(JSOP_NEW)
1959 /* Get immediate argc and find the constructor function. */
1960 argc = GET_ARGC(regs.pc);
1961 vp = regs.sp - (2 + argc);
1962 JS_ASSERT(vp >= StackBase(fp));
1965 * Assign lval, obj, and fun exactly as the code at inline_call: expects to
1966 * find them, to avoid nesting a js_Interpret call via js_InvokeConstructor.
1968 lval = *vp;
1969 if (VALUE_IS_FUNCTION(cx, lval)) {
1970 obj = JSVAL_TO_OBJECT(lval);
1971 fun = GET_FUNCTION_PRIVATE(cx, obj);
1972 if (FUN_INTERPRETED(fun)) {
1973 /* Root as we go using vp[1]. */
1974 if (!obj->getProperty(cx,
1975 ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom),
1976 &vp[1])) {
1977 goto error;
1979 rval = vp[1];
1980 obj2 = NewObject(cx, &js_ObjectClass,
1981 JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL,
1982 obj->getParent());
1983 if (!obj2)
1984 goto error;
1986 if (fun->u.i.script->isEmpty()) {
1987 *vp = OBJECT_TO_JSVAL(obj2);
1988 regs.sp = vp + 1;
1989 goto end_new;
1992 vp[1] = OBJECT_TO_JSVAL(obj2);
1993 flags = JSFRAME_CONSTRUCTING;
1994 goto inline_call;
1998 if (!js_InvokeConstructor(cx, argc, JS_FALSE, vp))
1999 goto error;
2000 regs.sp = vp + 1;
2001 CHECK_INTERRUPT_HANDLER();
2002 TRACE_0(NativeCallComplete);
2004 end_new:
2005 END_CASE(JSOP_NEW)
2007 BEGIN_CASE(JSOP_CALL)
2008 BEGIN_CASE(JSOP_EVAL)
2009 BEGIN_CASE(JSOP_APPLY)
2010 argc = GET_ARGC(regs.pc);
2011 vp = regs.sp - (argc + 2);
2013 lval = *vp;
2014 if (VALUE_IS_FUNCTION(cx, lval)) {
2015 obj = JSVAL_TO_OBJECT(lval);
2016 fun = GET_FUNCTION_PRIVATE(cx, obj);
2018 /* Clear frame flags since this is not a constructor call. */
2019 flags = 0;
2020 if (FUN_INTERPRETED(fun))
2021 inline_call:
2023 uintN nframeslots, nvars, missing;
2024 jsuword nbytes;
2025 void *newmark;
2026 jsval *newsp;
2027 JSInlineFrame *newifp;
2028 JSInterpreterHook hook;
2030 script = fun->u.i.script;
2031 if (script->isEmpty()) {
2032 script = fp->script;
2033 *vp = JSVAL_VOID;
2034 regs.sp = vp + 1;
2035 goto end_call;
2038 /* Restrict recursion of lightweight functions. */
2039 if (inlineCallCount >= JS_MAX_INLINE_CALL_COUNT) {
2040 js_ReportOverRecursed(cx);
2041 script = fp->script;
2042 goto error;
2045 /* Compute the total number of stack slots needed by fun. */
2046 nframeslots = JS_HOWMANY(sizeof(JSInlineFrame), sizeof(jsval));
2047 atoms = script->atomMap.vector;
2048 nbytes = (nframeslots + script->nslots) * sizeof(jsval);
2050 /* Allocate missing expected args adjacent to actuals. */
2051 JSArena *a = cx->stackPool.getCurrent();
2052 newmark = (void *) a->getAvail();
2053 if (fun->nargs <= argc) {
2054 missing = 0;
2055 } else {
2056 newsp = vp + 2 + fun->nargs;
2057 JS_ASSERT(newsp > regs.sp);
2058 if ((jsuword) newsp <= a->getLimit()) {
2059 if ((jsuword) newsp > a->getAvail())
2060 a->setAvail(jsuword(newsp));
2061 jsval *argsp = newsp;
2062 do {
2063 *--argsp = JSVAL_VOID;
2064 } while (argsp != regs.sp);
2065 missing = 0;
2066 } else {
2067 missing = fun->nargs - argc;
2068 nbytes += (2 + fun->nargs) * sizeof(jsval);
2072 /* Allocate the inline frame with its slots and operands. */
2073 if (a->getAvail() + nbytes <= a->getLimit()) {
2074 newsp = (jsval *) a->getAvail();
2075 a->setAvail(a->getAvail() + nbytes);
2076 JS_ASSERT(missing == 0);
2077 } else {
2078 cx->stackPool.allocateCast<jsval *>(newsp, nbytes);
2079 if (!newsp) {
2080 js_ReportOutOfScriptQuota(cx);
2081 goto bad_inline_call;
2085 * Move args if the missing ones overflow arena a, then push
2086 * undefined for the missing args.
2088 if (missing) {
2089 memcpy(newsp, vp, (2 + argc) * sizeof(jsval));
2090 vp = newsp;
2091 newsp = vp + 2 + argc;
2092 do {
2093 *newsp++ = JSVAL_VOID;
2094 } while (--missing != 0);
2098 /* Claim space for the stack frame and initialize it. */
2099 newifp = (JSInlineFrame *) newsp;
2100 newsp += nframeslots;
2101 newifp->frame.callobj = NULL;
2102 newifp->frame.argsobj = NULL;
2103 newifp->frame.script = script;
2104 newifp->frame.fun = fun;
2105 newifp->frame.argc = argc;
2106 newifp->frame.argv = vp + 2;
2107 newifp->frame.rval = JSVAL_VOID;
2108 newifp->frame.down = fp;
2109 newifp->frame.annotation = NULL;
2110 newifp->frame.scopeChain = parent = obj->getParent();
2111 newifp->frame.flags = flags;
2112 newifp->frame.blockChain = NULL;
2113 if (script->staticLevel < JS_DISPLAY_SIZE) {
2114 JSStackFrame **disp = &cx->display[script->staticLevel];
2115 newifp->frame.displaySave = *disp;
2116 *disp = &newifp->frame;
2118 newifp->mark = newmark;
2120 /* Compute the 'this' parameter now that argv is set. */
2121 JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags));
2122 newifp->frame.thisv = vp[1];
2124 newifp->frame.regs = NULL;
2125 newifp->frame.imacpc = NULL;
2126 newifp->frame.slots = newsp;
2128 /* Push void to initialize local variables. */
2129 nvars = fun->u.i.nvars;
2130 while (nvars--)
2131 *newsp++ = JSVAL_VOID;
2133 /* Scope with a call object parented by callee's parent. */
2134 if (JSFUN_HEAVYWEIGHT_TEST(fun->flags) &&
2135 !js_GetCallObject(cx, &newifp->frame)) {
2136 goto bad_inline_call;
2139 /* Switch version if currentVersion wasn't overridden. */
2140 newifp->callerVersion = (JSVersion) cx->version;
2141 if (JS_LIKELY(cx->version == currentVersion)) {
2142 currentVersion = (JSVersion) script->version;
2143 if (currentVersion != cx->version)
2144 js_SetVersion(cx, currentVersion);
2147 /* Push the frame and set interpreter registers. */
2148 newifp->callerRegs = regs;
2149 fp->regs = &newifp->callerRegs;
2150 regs.sp = newsp;
2151 regs.pc = script->code;
2152 newifp->frame.regs = &regs;
2153 cx->fp = fp = &newifp->frame;
2155 /* Call the debugger hook if present. */
2156 hook = cx->debugHooks->callHook;
2157 if (hook) {
2158 newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0,
2159 cx->debugHooks->callHookData);
2160 CHECK_INTERRUPT_HANDLER();
2161 } else {
2162 newifp->hookData = NULL;
2165 inlineCallCount++;
2166 JS_RUNTIME_METER(rt, inlineCalls);
2168 DTrace::enterJSFun(cx, fp, fun, fp->down, fp->argc, fp->argv);
2170 #ifdef JS_TRACER
2171 if (TraceRecorder *tr = TRACE_RECORDER(cx)) {
2172 AbortableRecordingStatus status = tr->record_EnterFrame(inlineCallCount);
2173 RESTORE_INTERP_VARS();
2174 if (StatusAbortsRecorderIfActive(status)) {
2175 if (TRACE_RECORDER(cx)) {
2176 JS_ASSERT(TRACE_RECORDER(cx) == tr);
2177 AbortRecording(cx, "record_EnterFrame failed");
2179 if (status == ARECORD_ERROR)
2180 goto error;
2182 } else if (fp->script == fp->down->script &&
2183 *fp->down->regs->pc == JSOP_CALL &&
2184 *fp->regs->pc == JSOP_TRACE) {
2185 MONITOR_BRANCH(Record_EnterFrame);
2187 #endif
2189 /* Load first op and dispatch it (safe since JSOP_STOP). */
2190 op = (JSOp) *regs.pc;
2191 DO_OP();
2193 bad_inline_call:
2194 JS_ASSERT(fp->regs == &regs);
2195 script = fp->script;
2196 atoms = script->atomMap.vector;
2197 js_FreeRawStack(cx, newmark);
2198 goto error;
2201 if (fun->flags & JSFUN_FAST_NATIVE) {
2202 DTrace::enterJSFun(cx, NULL, fun, fp, argc, vp + 2, &lval);
2204 JS_ASSERT(fun->u.n.extra == 0);
2205 JS_ASSERT(JSVAL_IS_OBJECT(vp[1]) ||
2206 PRIMITIVE_THIS_TEST(fun, vp[1]));
2207 ok = ((JSFastNative) fun->u.n.native)(cx, argc, vp);
2208 DTrace::exitJSFun(cx, NULL, fun, *vp, &lval);
2209 regs.sp = vp + 1;
2210 if (!ok)
2211 goto error;
2212 TRACE_0(NativeCallComplete);
2213 goto end_call;
2217 ok = js_Invoke(cx, argc, vp, 0);
2218 regs.sp = vp + 1;
2219 CHECK_INTERRUPT_HANDLER();
2220 if (!ok)
2221 goto error;
2222 JS_RUNTIME_METER(rt, nonInlineCalls);
2223 TRACE_0(NativeCallComplete);
2225 end_call:
2226 END_CASE(JSOP_CALL)
2228 BEGIN_CASE(JSOP_SETCALL)
2229 argc = GET_ARGC(regs.pc);
2230 vp = regs.sp - argc - 2;
2231 if (js_Invoke(cx, argc, vp, 0))
2232 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_LEFTSIDE_OF_ASS);
2233 goto error;
2234 END_CASE(JSOP_SETCALL)
2236 BEGIN_CASE(JSOP_NAME)
2237 BEGIN_CASE(JSOP_CALLNAME)
2239 PropertyCacheEntry *entry;
2241 obj = fp->scopeChain;
2243 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
2244 if (!atom) {
2245 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
2246 if (entry->vword.isObject()) {
2247 rval = entry->vword.toJsval();
2248 goto do_push_rval;
2251 if (entry->vword.isSlot()) {
2252 slot = entry->vword.toSlot();
2253 JS_ASSERT(slot < obj2->scope()->freeslot);
2254 rval = obj2->lockedGetSlot(slot);
2255 goto do_push_rval;
2258 JS_ASSERT(entry->vword.isSprop());
2259 sprop = entry->vword.toSprop();
2260 goto do_native_get;
2263 id = ATOM_TO_JSID(atom);
2264 if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
2265 goto error;
2266 if (!prop) {
2267 /* Kludge to allow (typeof foo == "undefined") tests. */
2268 endpc = script->code + script->length;
2269 op2 = js_GetOpcode(cx, script, regs.pc + JSOP_NAME_LENGTH);
2270 if (op2 == JSOP_TYPEOF) {
2271 PUSH_OPND(JSVAL_VOID);
2272 len = JSOP_NAME_LENGTH;
2273 DO_NEXT_OP(len);
2275 goto atom_not_defined;
2278 /* Take the slow path if prop was not found in a native object. */
2279 if (!obj->isNative() || !obj2->isNative()) {
2280 obj2->dropProperty(cx, prop);
2281 if (!obj->getProperty(cx, id, &rval))
2282 goto error;
2283 } else {
2284 sprop = (JSScopeProperty *)prop;
2285 do_native_get:
2286 NATIVE_GET(cx, obj, obj2, sprop, JSGET_METHOD_BARRIER, &rval);
2287 obj2->dropProperty(cx, (JSProperty *) sprop);
2290 do_push_rval:
2291 PUSH_OPND(rval);
2292 if (op == JSOP_CALLNAME)
2293 PUSH_OPND(OBJECT_TO_JSVAL(obj));
2295 END_CASE(JSOP_NAME)
2297 BEGIN_CASE(JSOP_UINT16)
2298 i = (jsint) GET_UINT16(regs.pc);
2299 rval = INT_TO_JSVAL(i);
2300 PUSH_OPND(rval);
2301 END_CASE(JSOP_UINT16)
2303 BEGIN_CASE(JSOP_UINT24)
2304 i = (jsint) GET_UINT24(regs.pc);
2305 rval = INT_TO_JSVAL(i);
2306 PUSH_OPND(rval);
2307 END_CASE(JSOP_UINT24)
2309 BEGIN_CASE(JSOP_INT8)
2310 i = GET_INT8(regs.pc);
2311 rval = INT_TO_JSVAL(i);
2312 PUSH_OPND(rval);
2313 END_CASE(JSOP_INT8)
2315 BEGIN_CASE(JSOP_INT32)
2316 i = GET_INT32(regs.pc);
2317 rval = INT_TO_JSVAL(i);
2318 PUSH_OPND(rval);
2319 END_CASE(JSOP_INT32)
2321 BEGIN_CASE(JSOP_INDEXBASE)
2323 * Here atoms can exceed script->atomMap.length as we use atoms as a
2324 * segment register for object literals as well.
2326 atoms += GET_INDEXBASE(regs.pc);
2327 END_CASE(JSOP_INDEXBASE)
2329 BEGIN_CASE(JSOP_INDEXBASE1)
2330 BEGIN_CASE(JSOP_INDEXBASE2)
2331 BEGIN_CASE(JSOP_INDEXBASE3)
2332 atoms += (op - JSOP_INDEXBASE1 + 1) << 16;
2333 END_CASE(JSOP_INDEXBASE3)
2335 BEGIN_CASE(JSOP_RESETBASE0)
2336 BEGIN_CASE(JSOP_RESETBASE)
2337 atoms = script->atomMap.vector;
2338 END_CASE(JSOP_RESETBASE)
2340 BEGIN_CASE(JSOP_DOUBLE)
2341 JS_ASSERT(!fp->imacpc);
2342 JS_ASSERT(size_t(atoms - script->atomMap.vector) < script->atomMap.length);
2343 /* FALL THROUGH */
2345 BEGIN_CASE(JSOP_STRING)
2346 LOAD_ATOM(0);
2347 PUSH_OPND(ATOM_KEY(atom));
2348 END_CASE(JSOP_DOUBLE)
2350 BEGIN_CASE(JSOP_OBJECT)
2351 LOAD_OBJECT(0);
2352 PUSH_OPND(OBJECT_TO_JSVAL(obj));
2353 END_CASE(JSOP_OBJECT)
2355 BEGIN_CASE(JSOP_REGEXP) {
2357 * Push a regexp object cloned from the regexp literal object mapped by the
2358 * bytecode at pc. ES5 finally fixed this bad old ES3 design flaw which was
2359 * flouted by many browser-based implementations.
2361 * We avoid the js_GetScopeChain call here and pass fp->scopeChain as
2362 * js_GetClassPrototype uses the latter only to locate the global.
2364 index = GET_FULL_INDEX(0);
2365 JSObject *proto;
2366 if (!js_GetClassPrototype(cx, fp->scopeChain, JSProto_RegExp, &proto))
2367 goto error;
2368 JS_ASSERT(proto);
2369 obj = js_CloneRegExpObject(cx, script->getRegExp(index), proto);
2370 if (!obj)
2371 goto error;
2372 rval = OBJECT_TO_JSVAL(obj);
2373 PUSH_OPND(rval);
2375 END_CASE(JSOP_REGEXP)
2377 BEGIN_CASE(JSOP_ZERO)
2378 PUSH_OPND(JSVAL_ZERO);
2379 END_CASE(JSOP_ZERO)
2381 BEGIN_CASE(JSOP_ONE)
2382 PUSH_OPND(JSVAL_ONE);
2383 END_CASE(JSOP_ONE)
2385 BEGIN_CASE(JSOP_NULL)
2386 PUSH_OPND(JSVAL_NULL);
2387 END_CASE(JSOP_NULL)
2389 BEGIN_CASE(JSOP_FALSE)
2390 PUSH_OPND(JSVAL_FALSE);
2391 END_CASE(JSOP_FALSE)
2393 BEGIN_CASE(JSOP_TRUE)
2394 PUSH_OPND(JSVAL_TRUE);
2395 END_CASE(JSOP_TRUE)
2397 BEGIN_CASE(JSOP_TABLESWITCH)
2398 pc2 = regs.pc;
2399 len = GET_JUMP_OFFSET(pc2);
2402 * ECMAv2+ forbids conversion of discriminant, so we will skip to the
2403 * default case if the discriminant isn't already an int jsval. (This
2404 * opcode is emitted only for dense jsint-domain switches.)
2406 rval = POP_OPND();
2407 if (JSVAL_IS_INT(rval)) {
2408 i = JSVAL_TO_INT(rval);
2409 } else if (JSVAL_IS_DOUBLE(rval) && *JSVAL_TO_DOUBLE(rval) == 0) {
2410 /* Treat -0 (double) as 0. */
2411 i = 0;
2412 } else {
2413 DO_NEXT_OP(len);
2416 pc2 += JUMP_OFFSET_LEN;
2417 low = GET_JUMP_OFFSET(pc2);
2418 pc2 += JUMP_OFFSET_LEN;
2419 high = GET_JUMP_OFFSET(pc2);
2421 i -= low;
2422 if ((jsuint)i < (jsuint)(high - low + 1)) {
2423 pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i;
2424 off = (jsint) GET_JUMP_OFFSET(pc2);
2425 if (off)
2426 len = off;
2428 END_VARLEN_CASE
2430 BEGIN_CASE(JSOP_TABLESWITCHX)
2431 pc2 = regs.pc;
2432 len = GET_JUMPX_OFFSET(pc2);
2435 * ECMAv2+ forbids conversion of discriminant, so we will skip to the
2436 * default case if the discriminant isn't already an int jsval. (This
2437 * opcode is emitted only for dense jsint-domain switches.)
2439 rval = POP_OPND();
2440 if (JSVAL_IS_INT(rval)) {
2441 i = JSVAL_TO_INT(rval);
2442 } else if (JSVAL_IS_DOUBLE(rval) && *JSVAL_TO_DOUBLE(rval) == 0) {
2443 /* Treat -0 (double) as 0. */
2444 i = 0;
2445 } else {
2446 DO_NEXT_OP(len);
2449 pc2 += JUMPX_OFFSET_LEN;
2450 low = GET_JUMP_OFFSET(pc2);
2451 pc2 += JUMP_OFFSET_LEN;
2452 high = GET_JUMP_OFFSET(pc2);
2454 i -= low;
2455 if ((jsuint)i < (jsuint)(high - low + 1)) {
2456 pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i;
2457 off = (jsint) GET_JUMPX_OFFSET(pc2);
2458 if (off)
2459 len = off;
2461 END_VARLEN_CASE
2463 BEGIN_CASE(JSOP_LOOKUPSWITCHX)
2464 off = JUMPX_OFFSET_LEN;
2465 goto do_lookup_switch;
2467 BEGIN_CASE(JSOP_LOOKUPSWITCH)
2468 off = JUMP_OFFSET_LEN;
2470 do_lookup_switch:
2472 * JSOP_LOOKUPSWITCH and JSOP_LOOKUPSWITCHX are never used if any atom
2473 * index in it would exceed 64K limit.
2475 JS_ASSERT(!fp->imacpc);
2476 JS_ASSERT(atoms == script->atomMap.vector);
2477 pc2 = regs.pc;
2478 lval = POP_OPND();
2480 if (!JSVAL_IS_PRIMITIVE(lval))
2481 goto end_lookup_switch;
2483 pc2 += off;
2484 npairs = (jsint) GET_UINT16(pc2);
2485 pc2 += UINT16_LEN;
2486 JS_ASSERT(npairs); /* empty switch uses JSOP_TABLESWITCH */
2488 #define SEARCH_PAIRS(MATCH_CODE) \
2489 for (;;) { \
2490 JS_ASSERT(GET_INDEX(pc2) < script->atomMap.length); \
2491 atom = atoms[GET_INDEX(pc2)]; \
2492 rval = ATOM_KEY(atom); \
2493 MATCH_CODE \
2494 pc2 += INDEX_LEN; \
2495 if (match) \
2496 break; \
2497 pc2 += off; \
2498 if (--npairs == 0) { \
2499 pc2 = regs.pc; \
2500 break; \
2504 if (JSVAL_IS_STRING(lval)) {
2505 str = JSVAL_TO_STRING(lval);
2506 SEARCH_PAIRS(
2507 match = (JSVAL_IS_STRING(rval) &&
2508 ((str2 = JSVAL_TO_STRING(rval)) == str ||
2509 js_EqualStrings(str2, str)));
2511 } else if (JSVAL_IS_DOUBLE(lval)) {
2512 d = *JSVAL_TO_DOUBLE(lval);
2513 SEARCH_PAIRS(
2514 match = (JSVAL_IS_DOUBLE(rval) &&
2515 *JSVAL_TO_DOUBLE(rval) == d);
2517 } else {
2518 SEARCH_PAIRS(
2519 match = (lval == rval);
2522 #undef SEARCH_PAIRS
2524 end_lookup_switch:
2525 len = (op == JSOP_LOOKUPSWITCH)
2526 ? GET_JUMP_OFFSET(pc2)
2527 : GET_JUMPX_OFFSET(pc2);
2528 END_VARLEN_CASE
2530 BEGIN_CASE(JSOP_TRAP)
2532 JSTrapStatus status;
2534 status = JS_HandleTrap(cx, script, regs.pc, &rval);
2535 switch (status) {
2536 case JSTRAP_ERROR:
2537 goto error;
2538 case JSTRAP_RETURN:
2539 fp->rval = rval;
2540 ok = JS_TRUE;
2541 goto forced_return;
2542 case JSTRAP_THROW:
2543 cx->throwing = JS_TRUE;
2544 cx->exception = rval;
2545 goto error;
2546 default:
2547 break;
2549 JS_ASSERT(status == JSTRAP_CONTINUE);
2550 CHECK_INTERRUPT_HANDLER();
2551 JS_ASSERT(JSVAL_IS_INT(rval));
2552 op = (JSOp) JSVAL_TO_INT(rval);
2553 JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
2554 DO_OP();
2557 BEGIN_CASE(JSOP_ARGUMENTS)
2558 if (!js_GetArgsValue(cx, fp, &rval))
2559 goto error;
2560 PUSH_OPND(rval);
2561 END_CASE(JSOP_ARGUMENTS)
2563 BEGIN_CASE(JSOP_ARGSUB)
2564 id = INT_TO_JSID(GET_ARGNO(regs.pc));
2565 if (!js_GetArgsProperty(cx, fp, id, &rval))
2566 goto error;
2567 PUSH_OPND(rval);
2568 END_CASE(JSOP_ARGSUB)
2570 BEGIN_CASE(JSOP_ARGCNT)
2571 id = ATOM_TO_JSID(rt->atomState.lengthAtom);
2572 if (!js_GetArgsProperty(cx, fp, id, &rval))
2573 goto error;
2574 PUSH_OPND(rval);
2575 END_CASE(JSOP_ARGCNT)
2577 BEGIN_CASE(JSOP_GETARG)
2578 BEGIN_CASE(JSOP_CALLARG)
2579 slot = GET_ARGNO(regs.pc);
2580 JS_ASSERT(slot < fp->fun->nargs);
2581 METER_SLOT_OP(op, slot);
2582 PUSH_OPND(fp->argv[slot]);
2583 if (op == JSOP_CALLARG)
2584 PUSH_OPND(JSVAL_NULL);
2585 END_CASE(JSOP_GETARG)
2587 BEGIN_CASE(JSOP_SETARG)
2588 slot = GET_ARGNO(regs.pc);
2589 JS_ASSERT(slot < fp->fun->nargs);
2590 METER_SLOT_OP(op, slot);
2591 vp = &fp->argv[slot];
2592 *vp = FETCH_OPND(-1);
2593 END_SET_CASE(JSOP_SETARG)
2595 BEGIN_CASE(JSOP_GETLOCAL)
2596 slot = GET_SLOTNO(regs.pc);
2597 JS_ASSERT(slot < script->nslots);
2598 PUSH_OPND(fp->slots[slot]);
2599 END_CASE(JSOP_GETLOCAL)
2601 BEGIN_CASE(JSOP_CALLLOCAL)
2602 slot = GET_SLOTNO(regs.pc);
2603 JS_ASSERT(slot < script->nslots);
2604 PUSH_OPND(fp->slots[slot]);
2605 PUSH_OPND(JSVAL_NULL);
2606 END_CASE(JSOP_CALLLOCAL)
2608 BEGIN_CASE(JSOP_SETLOCAL)
2609 slot = GET_SLOTNO(regs.pc);
2610 JS_ASSERT(slot < script->nslots);
2611 vp = &fp->slots[slot];
2612 *vp = FETCH_OPND(-1);
2613 END_SET_CASE(JSOP_SETLOCAL)
2615 BEGIN_CASE(JSOP_GETUPVAR)
2616 BEGIN_CASE(JSOP_CALLUPVAR)
2618 JSUpvarArray *uva = script->upvars();
2620 index = GET_UINT16(regs.pc);
2621 JS_ASSERT(index < uva->length);
2623 rval = js_GetUpvar(cx, script->staticLevel, uva->vector[index]);
2624 PUSH_OPND(rval);
2626 if (op == JSOP_CALLUPVAR)
2627 PUSH_OPND(JSVAL_NULL);
2629 END_CASE(JSOP_GETUPVAR)
2631 BEGIN_CASE(JSOP_GETUPVAR_DBG)
2632 BEGIN_CASE(JSOP_CALLUPVAR_DBG)
2633 fun = fp->fun;
2634 JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
2635 JS_ASSERT(fun->u.i.wrapper);
2637 /* Scope for tempPool mark and local names allocation in it. */
2639 void *mark = cx->tempPool.getMark();
2640 jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
2641 if (!names)
2642 goto error;
2644 index = fun->countArgsAndVars() + GET_UINT16(regs.pc);
2645 atom = JS_LOCAL_NAME_TO_ATOM(names[index]);
2646 id = ATOM_TO_JSID(atom);
2648 ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
2649 cx->tempPool.release(mark);
2650 if (!ok)
2651 goto error;
2654 if (!prop)
2655 goto atom_not_defined;
2657 /* Minimize footprint with generic code instead of NATIVE_GET. */
2658 obj2->dropProperty(cx, prop);
2659 vp = regs.sp;
2660 PUSH_OPND(JSVAL_NULL);
2661 if (!obj->getProperty(cx, id, vp))
2662 goto error;
2664 if (op == JSOP_CALLUPVAR_DBG)
2665 PUSH_OPND(JSVAL_NULL);
2666 END_CASE(JSOP_GETUPVAR_DBG)
2668 BEGIN_CASE(JSOP_GETDSLOT)
2669 BEGIN_CASE(JSOP_CALLDSLOT)
2670 JS_ASSERT(fp->argv);
2671 obj = JSVAL_TO_OBJECT(fp->argv[-2]);
2672 JS_ASSERT(obj);
2673 JS_ASSERT(obj->dslots);
2675 index = GET_UINT16(regs.pc);
2676 JS_ASSERT(JS_INITIAL_NSLOTS + index < jsatomid(obj->dslots[-1]));
2677 JS_ASSERT_IF(obj->scope()->object == obj,
2678 JS_INITIAL_NSLOTS + index < obj->scope()->freeslot);
2680 PUSH_OPND(obj->dslots[index]);
2681 if (op == JSOP_CALLDSLOT)
2682 PUSH_OPND(JSVAL_NULL);
2683 END_CASE(JSOP_GETDSLOT)
2685 BEGIN_CASE(JSOP_GETGVAR)
2686 BEGIN_CASE(JSOP_CALLGVAR)
2687 slot = GET_SLOTNO(regs.pc);
2688 JS_ASSERT(slot < GlobalVarCount(fp));
2689 METER_SLOT_OP(op, slot);
2690 lval = fp->slots[slot];
2691 if (JSVAL_IS_NULL(lval)) {
2692 op = (op == JSOP_GETGVAR) ? JSOP_NAME : JSOP_CALLNAME;
2693 DO_OP();
2695 JS_ASSERT(fp->varobj(cx) == cx->activeCallStack()->getInitialVarObj());
2696 obj = cx->activeCallStack()->getInitialVarObj();
2697 slot = JSVAL_TO_INT(lval);
2698 rval = obj->getSlotMT(cx, slot);
2699 PUSH_OPND(rval);
2700 if (op == JSOP_CALLGVAR)
2701 PUSH_OPND(JSVAL_NULL);
2702 END_CASE(JSOP_GETGVAR)
2704 BEGIN_CASE(JSOP_SETGVAR)
2705 slot = GET_SLOTNO(regs.pc);
2706 JS_ASSERT(slot < GlobalVarCount(fp));
2707 METER_SLOT_OP(op, slot);
2708 rval = FETCH_OPND(-1);
2709 JS_ASSERT(fp->varobj(cx) == cx->activeCallStack()->getInitialVarObj());
2710 obj = cx->activeCallStack()->getInitialVarObj();
2711 lval = fp->slots[slot];
2712 if (JSVAL_IS_NULL(lval)) {
2714 * Inline-clone and deoptimize JSOP_SETNAME code here because
2715 * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval]
2716 * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME.
2718 #ifdef JS_TRACER
2719 if (TRACE_RECORDER(cx))
2720 AbortRecording(cx, "SETGVAR with NULL slot");
2721 #endif
2722 LOAD_ATOM(0);
2723 id = ATOM_TO_JSID(atom);
2724 if (!obj->setProperty(cx, id, &rval))
2725 goto error;
2726 } else {
2727 slot = JSVAL_TO_INT(lval);
2728 JS_LOCK_OBJ(cx, obj);
2729 JSScope *scope = obj->scope();
2730 if (!scope->methodWriteBarrier(cx, slot, rval)) {
2731 JS_UNLOCK_SCOPE(cx, scope);
2732 goto error;
2734 obj->lockedSetSlot(slot, rval);
2735 JS_UNLOCK_SCOPE(cx, scope);
2737 END_SET_CASE(JSOP_SETGVAR)
2739 BEGIN_CASE(JSOP_DEFCONST)
2740 BEGIN_CASE(JSOP_DEFVAR)
2741 index = GET_INDEX(regs.pc);
2742 atom = atoms[index];
2745 * index is relative to atoms at this point but for global var
2746 * code below we need the absolute value.
2748 index += atoms - script->atomMap.vector;
2749 obj = fp->varobj(cx);
2750 JS_ASSERT(obj->map->ops->defineProperty == js_DefineProperty);
2751 attrs = JSPROP_ENUMERATE;
2752 if (!(fp->flags & JSFRAME_EVAL))
2753 attrs |= JSPROP_PERMANENT;
2754 if (op == JSOP_DEFCONST)
2755 attrs |= JSPROP_READONLY;
2757 /* Lookup id in order to check for redeclaration problems. */
2758 id = ATOM_TO_JSID(atom);
2759 prop = NULL;
2760 if (op == JSOP_DEFVAR) {
2762 * Redundant declaration of a |var|, even one for a non-writable
2763 * property like |undefined| in ES5, does nothing.
2765 if (!obj->lookupProperty(cx, id, &obj2, &prop))
2766 goto error;
2767 } else {
2768 if (!js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop))
2769 goto error;
2772 /* Bind a variable only if it's not yet defined. */
2773 if (!prop) {
2774 if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID, JS_PropertyStub, JS_PropertyStub,
2775 attrs, 0, 0, &prop)) {
2776 goto error;
2778 JS_ASSERT(prop);
2779 obj2 = obj;
2783 * Try to optimize a property we either just created, or found
2784 * directly in the global object, that is permanent, has a slot,
2785 * and has stub getter and setter, into a "fast global" accessed
2786 * by the JSOP_*GVAR opcodes.
2788 if (!fp->fun &&
2789 index < GlobalVarCount(fp) &&
2790 obj2 == obj &&
2791 obj->isNative()) {
2792 sprop = (JSScopeProperty *) prop;
2793 if (!sprop->configurable() &&
2794 SPROP_HAS_VALID_SLOT(sprop, obj->scope()) &&
2795 sprop->hasDefaultGetterOrIsMethod() &&
2796 sprop->hasDefaultSetter()) {
2798 * Fast globals use frame variables to map the global name's atom
2799 * index to the permanent varobj slot number, tagged as a jsval.
2800 * The atom index for the global's name literal is identical to its
2801 * variable index.
2803 fp->slots[index] = INT_TO_JSVAL(sprop->slot);
2807 obj2->dropProperty(cx, prop);
2808 END_CASE(JSOP_DEFVAR)
2810 BEGIN_CASE(JSOP_DEFFUN)
2812 JSPropertyOp getter, setter;
2813 bool doSet;
2814 JSObject *pobj;
2815 JSProperty *prop;
2816 uint32 old;
2819 * A top-level function defined in Global or Eval code (see ECMA-262
2820 * Ed. 3), or else a SpiderMonkey extension: a named function statement in
2821 * a compound statement (not at the top statement level of global code, or
2822 * at the top level of a function body).
2824 LOAD_FUNCTION(0);
2825 obj = FUN_OBJECT(fun);
2827 if (FUN_NULL_CLOSURE(fun)) {
2829 * Even a null closure needs a parent for principals finding.
2830 * FIXME: bug 476950, although debugger users may also demand some kind
2831 * of scope link for debugger-assisted eval-in-frame.
2833 obj2 = fp->scopeChain;
2834 } else {
2835 JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
2838 * Inline js_GetScopeChain a bit to optimize for the case of a
2839 * top-level function.
2841 if (!fp->blockChain) {
2842 obj2 = fp->scopeChain;
2843 } else {
2844 obj2 = js_GetScopeChain(cx, fp);
2845 if (!obj2)
2846 goto error;
2851 * If static link is not current scope, clone fun's object to link to the
2852 * current scope via parent. We do this to enable sharing of compiled
2853 * functions among multiple equivalent scopes, amortizing the cost of
2854 * compilation over a number of executions. Examples include XUL scripts
2855 * and event handlers shared among Firefox or other Mozilla app chrome
2856 * windows, and user-defined JS functions precompiled and then shared among
2857 * requests in server-side JS.
2859 if (obj->getParent() != obj2) {
2860 obj = CloneFunctionObject(cx, fun, obj2);
2861 if (!obj)
2862 goto error;
2866 * Protect obj from any GC hiding below JSObject::setProperty or
2867 * JSObject::defineProperty. All paths from here must flow through the
2868 * fp->scopeChain code below the parent->defineProperty call.
2870 MUST_FLOW_THROUGH("restore_scope");
2871 fp->scopeChain = obj;
2873 rval = OBJECT_TO_JSVAL(obj);
2876 * ECMA requires functions defined when entering Eval code to be
2877 * impermanent.
2879 attrs = (fp->flags & JSFRAME_EVAL)
2880 ? JSPROP_ENUMERATE
2881 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
2884 * Load function flags that are also property attributes. Getters and
2885 * setters do not need a slot, their value is stored elsewhere in the
2886 * property itself, not in obj slots.
2888 getter = setter = JS_PropertyStub;
2889 flags = JSFUN_GSFLAG2ATTR(fun->flags);
2890 if (flags) {
2891 /* Function cannot be both getter a setter. */
2892 JS_ASSERT(flags == JSPROP_GETTER || flags == JSPROP_SETTER);
2893 attrs |= flags | JSPROP_SHARED;
2894 rval = JSVAL_VOID;
2895 if (flags == JSPROP_GETTER)
2896 getter = CastAsPropertyOp(obj);
2897 else
2898 setter = CastAsPropertyOp(obj);
2902 * We define the function as a property of the variable object and not the
2903 * current scope chain even for the case of function expression statements
2904 * and functions defined by eval inside let or with blocks.
2906 parent = fp->varobj(cx);
2907 JS_ASSERT(parent);
2910 * Check for a const property of the same name -- or any kind of property
2911 * if executing with the strict option. We check here at runtime as well
2912 * as at compile-time, to handle eval as well as multiple HTML script tags.
2914 id = ATOM_TO_JSID(fun->atom);
2915 prop = NULL;
2916 ok = js_CheckRedeclaration(cx, parent, id, attrs, &pobj, &prop);
2917 if (!ok)
2918 goto restore_scope;
2921 * We deviate from 10.1.2 in ECMA 262 v3 and under eval use for function
2922 * declarations JSObject::setProperty, not JSObject::defineProperty, to
2923 * preserve the JSOP_PERMANENT attribute of existing properties and make
2924 * sure that such properties cannot be deleted.
2926 * We also use JSObject::setProperty for the existing properties of Call
2927 * objects with matching attributes to preserve the native getters and
2928 * setters that store the value of the property in the interpreter frame,
2929 * see bug 467495.
2931 doSet = (attrs == JSPROP_ENUMERATE);
2932 JS_ASSERT_IF(doSet, fp->flags & JSFRAME_EVAL);
2933 if (prop) {
2934 if (parent == pobj &&
2935 parent->getClass() == &js_CallClass &&
2936 (old = ((JSScopeProperty *) prop)->attributes(),
2937 !(old & (JSPROP_GETTER|JSPROP_SETTER)) &&
2938 (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs)) {
2940 * js_CheckRedeclaration must reject attempts to add a getter or
2941 * setter to an existing property without a getter or setter.
2943 JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT)));
2944 JS_ASSERT(!(old & JSPROP_READONLY));
2945 doSet = JS_TRUE;
2947 pobj->dropProperty(cx, prop);
2949 ok = doSet
2950 ? parent->setProperty(cx, id, &rval)
2951 : parent->defineProperty(cx, id, rval, getter, setter, attrs);
2953 restore_scope:
2954 /* Restore fp->scopeChain now that obj is defined in fp->callobj. */
2955 fp->scopeChain = obj2;
2956 if (!ok)
2957 goto error;
2959 END_CASE(JSOP_DEFFUN)
2961 BEGIN_CASE(JSOP_DEFFUN_FC)
2962 BEGIN_CASE(JSOP_DEFFUN_DBGFC)
2963 LOAD_FUNCTION(0);
2965 obj = (op == JSOP_DEFFUN_FC)
2966 ? js_NewFlatClosure(cx, fun)
2967 : js_NewDebuggableFlatClosure(cx, fun);
2968 if (!obj)
2969 goto error;
2970 rval = OBJECT_TO_JSVAL(obj);
2972 attrs = (fp->flags & JSFRAME_EVAL)
2973 ? JSPROP_ENUMERATE
2974 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
2976 flags = JSFUN_GSFLAG2ATTR(fun->flags);
2977 if (flags) {
2978 attrs |= flags | JSPROP_SHARED;
2979 rval = JSVAL_VOID;
2982 parent = fp->varobj(cx);
2983 JS_ASSERT(parent);
2985 id = ATOM_TO_JSID(fun->atom);
2986 ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL);
2987 if (ok) {
2988 if (attrs == JSPROP_ENUMERATE) {
2989 JS_ASSERT(fp->flags & JSFRAME_EVAL);
2990 ok = parent->setProperty(cx, id, &rval);
2991 } else {
2992 JS_ASSERT(attrs & JSPROP_PERMANENT);
2994 ok = parent->defineProperty(cx, id, rval,
2995 (flags & JSPROP_GETTER)
2996 ? CastAsPropertyOp(obj)
2997 : JS_PropertyStub,
2998 (flags & JSPROP_SETTER)
2999 ? CastAsPropertyOp(obj)
3000 : JS_PropertyStub,
3001 attrs);
3005 if (!ok)
3006 goto error;
3007 END_CASE(JSOP_DEFFUN_FC)
3009 BEGIN_CASE(JSOP_DEFLOCALFUN)
3011 * Define a local function (i.e., one nested at the top level of another
3012 * function), parented by the current scope chain, stored in a local
3013 * variable slot that the compiler allocated. This is an optimization over
3014 * JSOP_DEFFUN that avoids requiring a call object for the outer function's
3015 * activation.
3017 LOAD_FUNCTION(SLOTNO_LEN);
3018 JS_ASSERT(FUN_INTERPRETED(fun));
3019 JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
3020 obj = FUN_OBJECT(fun);
3022 if (FUN_NULL_CLOSURE(fun)) {
3023 obj = CloneFunctionObject(cx, fun, fp->scopeChain);
3024 if (!obj)
3025 goto error;
3026 } else {
3027 parent = js_GetScopeChain(cx, fp);
3028 if (!parent)
3029 goto error;
3031 if (obj->getParent() != parent) {
3032 #ifdef JS_TRACER
3033 if (TRACE_RECORDER(cx))
3034 AbortRecording(cx, "DEFLOCALFUN for closure");
3035 #endif
3036 obj = CloneFunctionObject(cx, fun, parent);
3037 if (!obj)
3038 goto error;
3042 slot = GET_SLOTNO(regs.pc);
3043 TRACE_2(DefLocalFunSetSlot, slot, obj);
3045 fp->slots[slot] = OBJECT_TO_JSVAL(obj);
3046 END_CASE(JSOP_DEFLOCALFUN)
3048 BEGIN_CASE(JSOP_DEFLOCALFUN_FC)
3049 LOAD_FUNCTION(SLOTNO_LEN);
3051 obj = js_NewFlatClosure(cx, fun);
3052 if (!obj)
3053 goto error;
3055 slot = GET_SLOTNO(regs.pc);
3056 TRACE_2(DefLocalFunSetSlot, slot, obj);
3058 fp->slots[slot] = OBJECT_TO_JSVAL(obj);
3059 END_CASE(JSOP_DEFLOCALFUN_FC)
3061 BEGIN_CASE(JSOP_DEFLOCALFUN_DBGFC)
3062 LOAD_FUNCTION(SLOTNO_LEN);
3064 obj = js_NewDebuggableFlatClosure(cx, fun);
3065 if (!obj)
3066 goto error;
3068 slot = GET_SLOTNO(regs.pc);
3069 fp->slots[slot] = OBJECT_TO_JSVAL(obj);
3070 END_CASE(JSOP_DEFLOCALFUN_DBGFC)
3072 BEGIN_CASE(JSOP_LAMBDA)
3073 /* Load the specified function object literal. */
3074 LOAD_FUNCTION(0);
3075 obj = FUN_OBJECT(fun);
3077 /* do-while(0) so we can break instead of using a goto. */
3078 do {
3079 if (FUN_NULL_CLOSURE(fun)) {
3080 parent = fp->scopeChain;
3082 if (obj->getParent() == parent) {
3083 op = JSOp(regs.pc[JSOP_LAMBDA_LENGTH]);
3086 * Optimize ({method: function () { ... }, ...}) and
3087 * this.method = function () { ... }; bytecode sequences.
3089 if (op == JSOP_SETMETHOD) {
3090 #ifdef DEBUG
3091 op2 = JSOp(regs.pc[JSOP_LAMBDA_LENGTH + JSOP_SETMETHOD_LENGTH]);
3092 JS_ASSERT(op2 == JSOP_POP || op2 == JSOP_POPV);
3093 #endif
3095 lval = FETCH_OPND(-1);
3096 if (JSVAL_IS_OBJECT(lval) &&
3097 (obj2 = JSVAL_TO_OBJECT(lval)) &&
3098 obj2->getClass() == &js_ObjectClass) {
3099 break;
3101 } else if (op == JSOP_INITMETHOD) {
3102 lval = FETCH_OPND(-1);
3103 JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval));
3104 obj2 = JSVAL_TO_OBJECT(lval);
3105 JS_ASSERT(obj2->getClass() == &js_ObjectClass);
3106 JS_ASSERT(obj2->scope()->object == obj2);
3107 break;
3110 } else {
3111 parent = js_GetScopeChain(cx, fp);
3112 if (!parent)
3113 goto error;
3116 obj = CloneFunctionObject(cx, fun, parent);
3117 if (!obj)
3118 goto error;
3119 } while (0);
3121 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3122 END_CASE(JSOP_LAMBDA)
3124 BEGIN_CASE(JSOP_LAMBDA_FC)
3125 LOAD_FUNCTION(0);
3127 obj = js_NewFlatClosure(cx, fun);
3128 if (!obj)
3129 goto error;
3131 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3132 END_CASE(JSOP_LAMBDA_FC)
3134 BEGIN_CASE(JSOP_LAMBDA_DBGFC)
3135 LOAD_FUNCTION(0);
3137 obj = js_NewDebuggableFlatClosure(cx, fun);
3138 if (!obj)
3139 goto error;
3141 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3142 END_CASE(JSOP_LAMBDA_DBGFC)
3144 BEGIN_CASE(JSOP_CALLEE)
3145 PUSH_OPND(fp->argv[-2]);
3146 END_CASE(JSOP_CALLEE)
3148 BEGIN_CASE(JSOP_GETTER)
3149 BEGIN_CASE(JSOP_SETTER)
3150 do_getter_setter:
3151 op2 = (JSOp) *++regs.pc;
3152 switch (op2) {
3153 case JSOP_INDEXBASE:
3154 atoms += GET_INDEXBASE(regs.pc);
3155 regs.pc += JSOP_INDEXBASE_LENGTH - 1;
3156 goto do_getter_setter;
3157 case JSOP_INDEXBASE1:
3158 case JSOP_INDEXBASE2:
3159 case JSOP_INDEXBASE3:
3160 atoms += (op2 - JSOP_INDEXBASE1 + 1) << 16;
3161 goto do_getter_setter;
3163 case JSOP_SETNAME:
3164 case JSOP_SETPROP:
3165 LOAD_ATOM(0);
3166 id = ATOM_TO_JSID(atom);
3167 rval = FETCH_OPND(-1);
3168 i = -1;
3169 goto gs_pop_lval;
3171 case JSOP_SETELEM:
3172 rval = FETCH_OPND(-1);
3173 id = 0;
3174 i = -2;
3175 gs_pop_lval:
3176 FETCH_OBJECT(cx, i - 1, lval, obj);
3177 break;
3179 case JSOP_INITPROP:
3180 JS_ASSERT(regs.sp - StackBase(fp) >= 2);
3181 rval = FETCH_OPND(-1);
3182 i = -1;
3183 LOAD_ATOM(0);
3184 id = ATOM_TO_JSID(atom);
3185 goto gs_get_lval;
3187 default:
3188 JS_ASSERT(op2 == JSOP_INITELEM);
3190 JS_ASSERT(regs.sp - StackBase(fp) >= 3);
3191 rval = FETCH_OPND(-1);
3192 id = 0;
3193 i = -2;
3194 gs_get_lval:
3195 lval = FETCH_OPND(i-1);
3196 JS_ASSERT(JSVAL_IS_OBJECT(lval));
3197 obj = JSVAL_TO_OBJECT(lval);
3198 break;
3201 /* Ensure that id has a type suitable for use with obj. */
3202 if (id == 0)
3203 FETCH_ELEMENT_ID(obj, i, id);
3205 if (!js_IsCallable(rval)) {
3206 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3207 JSMSG_BAD_GETTER_OR_SETTER,
3208 (op == JSOP_GETTER)
3209 ? js_getter_str
3210 : js_setter_str);
3211 goto error;
3215 * Getters and setters are just like watchpoints from an access control
3216 * point of view.
3218 if (!obj->checkAccess(cx, id, JSACC_WATCH, &rtmp, &attrs))
3219 goto error;
3221 if (op == JSOP_GETTER) {
3222 getter = CastAsPropertyOp(JSVAL_TO_OBJECT(rval));
3223 setter = JS_PropertyStub;
3224 attrs = JSPROP_GETTER;
3225 } else {
3226 getter = JS_PropertyStub;
3227 setter = CastAsPropertyOp(JSVAL_TO_OBJECT(rval));
3228 attrs = JSPROP_SETTER;
3230 attrs |= JSPROP_ENUMERATE | JSPROP_SHARED;
3232 /* Check for a readonly or permanent property of the same name. */
3233 if (!js_CheckRedeclaration(cx, obj, id, attrs, NULL, NULL))
3234 goto error;
3236 if (!obj->defineProperty(cx, id, JSVAL_VOID, getter, setter, attrs))
3237 goto error;
3239 regs.sp += i;
3240 if (js_CodeSpec[op2].ndefs > js_CodeSpec[op2].nuses) {
3241 JS_ASSERT(js_CodeSpec[op2].ndefs == js_CodeSpec[op2].nuses + 1);
3242 STORE_OPND(-1, rval);
3244 len = js_CodeSpec[op2].length;
3245 DO_NEXT_OP(len);
3247 BEGIN_CASE(JSOP_HOLE)
3248 PUSH_OPND(JSVAL_HOLE);
3249 END_CASE(JSOP_HOLE)
3251 BEGIN_CASE(JSOP_NEWARRAY)
3252 len = GET_UINT16(regs.pc);
3253 cx->fp->assertValidStackDepth(len);
3254 obj = js_NewArrayObject(cx, len, regs.sp - len, JS_TRUE);
3255 if (!obj)
3256 goto error;
3257 regs.sp -= len - 1;
3258 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
3259 END_CASE(JSOP_NEWARRAY)
3261 BEGIN_CASE(JSOP_NEWINIT)
3262 i = GET_INT8(regs.pc);
3263 JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
3264 if (i == JSProto_Array) {
3265 obj = js_NewArrayObject(cx, 0, NULL);
3266 if (!obj)
3267 goto error;
3268 } else {
3269 obj = NewObject(cx, &js_ObjectClass, NULL, NULL);
3270 if (!obj)
3271 goto error;
3273 if (regs.pc[JSOP_NEWINIT_LENGTH] != JSOP_ENDINIT) {
3274 JS_LOCK_OBJ(cx, obj);
3275 JSScope *scope = js_GetMutableScope(cx, obj);
3276 if (!scope) {
3277 JS_UNLOCK_OBJ(cx, obj);
3278 goto error;
3282 * We cannot assume that js_GetMutableScope above creates a scope
3283 * owned by cx and skip JS_UNLOCK_SCOPE. A new object debugger
3284 * hook may add properties to the newly created object, suspend
3285 * the current request and share the object with other threads.
3287 JS_UNLOCK_SCOPE(cx, scope);
3291 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3292 CHECK_INTERRUPT_HANDLER();
3293 END_CASE(JSOP_NEWINIT)
3295 BEGIN_CASE(JSOP_ENDINIT)
3296 /* Re-set the newborn root to the top of this object tree. */
3297 JS_ASSERT(regs.sp - StackBase(fp) >= 1);
3298 lval = FETCH_OPND(-1);
3299 JS_ASSERT(JSVAL_IS_OBJECT(lval));
3300 cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] = JSVAL_TO_OBJECT(lval);
3301 END_CASE(JSOP_ENDINIT)
3303 BEGIN_CASE(JSOP_INITPROP)
3304 BEGIN_CASE(JSOP_INITMETHOD)
3306 /* Load the property's initial value into rval. */
3307 JS_ASSERT(regs.sp - StackBase(fp) >= 2);
3308 rval = FETCH_OPND(-1);
3310 /* Load the object being initialized into lval/obj. */
3311 lval = FETCH_OPND(-2);
3312 obj = JSVAL_TO_OBJECT(lval);
3313 JS_ASSERT(obj->isNative());
3314 JS_ASSERT(!obj->getClass()->reserveSlots);
3316 JSScope *scope = obj->scope();
3317 PropertyCacheEntry *entry;
3320 * Probe the property cache.
3322 * We can not assume that the object created by JSOP_NEWINIT is still
3323 * single-threaded as the debugger can access it from other threads.
3324 * So check first.
3326 * On a hit, if the cached sprop has a non-default setter, it must be
3327 * __proto__. If sprop->parent != scope->lastProperty(), there is a
3328 * repeated property name. The fast path does not handle these two cases.
3330 if (CX_OWNS_OBJECT_TITLE(cx, obj) &&
3331 JS_PROPERTY_CACHE(cx).testForInit(rt, regs.pc, obj, scope, &sprop, &entry) &&
3332 sprop->hasDefaultSetter() &&
3333 sprop->parent == scope->lastProperty())
3335 /* Fast path. Property cache hit. */
3336 slot = sprop->slot;
3337 JS_ASSERT(slot == scope->freeslot);
3338 if (slot < obj->numSlots()) {
3339 ++scope->freeslot;
3340 } else {
3341 if (!js_AllocSlot(cx, obj, &slot))
3342 goto error;
3343 JS_ASSERT(slot == sprop->slot);
3346 JS_ASSERT(!scope->lastProperty() ||
3347 scope->shape == scope->lastProperty()->shape);
3348 if (scope->table) {
3349 JSScopeProperty *sprop2 =
3350 scope->addProperty(cx, sprop->id, sprop->getter(), sprop->setter(), slot,
3351 sprop->attributes(), sprop->getFlags(), sprop->shortid);
3352 if (!sprop2) {
3353 js_FreeSlot(cx, obj, slot);
3354 goto error;
3356 JS_ASSERT(sprop2 == sprop);
3357 } else {
3358 JS_ASSERT(!scope->isSharedEmpty());
3359 scope->extend(cx, sprop);
3363 * No method change check here because here we are adding a new
3364 * property, not updating an existing slot's value that might
3365 * contain a method of a branded scope.
3367 TRACE_2(SetPropHit, entry, sprop);
3368 obj->lockedSetSlot(slot, rval);
3369 } else {
3370 PCMETER(JS_PROPERTY_CACHE(cx).inipcmisses++);
3372 /* Get the immediate property name into id. */
3373 LOAD_ATOM(0);
3374 id = ATOM_TO_JSID(atom);
3376 /* Set the property named by obj[id] to rval. */
3377 if (!js_CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER,
3378 NULL, NULL)) {
3379 goto error;
3382 uintN defineHow = (op == JSOP_INITMETHOD)
3383 ? JSDNP_CACHE_RESULT | JSDNP_SET_METHOD
3384 : JSDNP_CACHE_RESULT;
3385 if (!(JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom)
3386 ? js_SetPropertyHelper(cx, obj, id, defineHow, &rval)
3387 : js_DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
3388 JSPROP_ENUMERATE, 0, 0, NULL,
3389 defineHow))) {
3390 goto error;
3394 /* Common tail for property cache hit and miss cases. */
3395 regs.sp--;
3397 END_CASE(JSOP_INITPROP);
3399 BEGIN_CASE(JSOP_INITELEM)
3400 /* Pop the element's value into rval. */
3401 JS_ASSERT(regs.sp - StackBase(fp) >= 3);
3402 rval = FETCH_OPND(-1);
3404 /* Find the object being initialized at top of stack. */
3405 lval = FETCH_OPND(-3);
3406 JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval));
3407 obj = JSVAL_TO_OBJECT(lval);
3409 /* Fetch id now that we have obj. */
3410 FETCH_ELEMENT_ID(obj, -2, id);
3413 * Check for property redeclaration strict warning (we may be in an object
3414 * initialiser, not an array initialiser).
3416 if (!js_CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER, NULL, NULL))
3417 goto error;
3420 * If rval is a hole, do not call JSObject::defineProperty. In this case,
3421 * obj must be an array, so if the current op is the last element
3422 * initialiser, set the array length to one greater than id.
3424 if (rval == JSVAL_HOLE) {
3425 JS_ASSERT(obj->isArray());
3426 JS_ASSERT(JSID_IS_INT(id));
3427 JS_ASSERT(jsuint(JSID_TO_INT(id)) < JS_ARGS_LENGTH_MAX);
3428 if (js_GetOpcode(cx, script, regs.pc + JSOP_INITELEM_LENGTH) == JSOP_ENDINIT &&
3429 !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1))) {
3430 goto error;
3432 } else {
3433 if (!obj->defineProperty(cx, id, rval, NULL, NULL, JSPROP_ENUMERATE))
3434 goto error;
3436 regs.sp -= 2;
3437 END_CASE(JSOP_INITELEM)
3439 #if JS_HAS_SHARP_VARS
3441 BEGIN_CASE(JSOP_DEFSHARP)
3442 slot = GET_UINT16(regs.pc);
3443 JS_ASSERT(slot + 1 < fp->script->nfixed);
3444 lval = fp->slots[slot];
3445 if (!JSVAL_IS_PRIMITIVE(lval)) {
3446 obj = JSVAL_TO_OBJECT(lval);
3447 } else {
3448 JS_ASSERT(JSVAL_IS_VOID(lval));
3449 obj = js_NewArrayObject(cx, 0, NULL);
3450 if (!obj)
3451 goto error;
3452 fp->slots[slot] = OBJECT_TO_JSVAL(obj);
3454 i = (jsint) GET_UINT16(regs.pc + UINT16_LEN);
3455 id = INT_TO_JSID(i);
3456 rval = FETCH_OPND(-1);
3457 if (JSVAL_IS_PRIMITIVE(rval)) {
3458 char numBuf[12];
3459 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
3460 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3461 JSMSG_BAD_SHARP_DEF, numBuf);
3462 goto error;
3464 if (!obj->defineProperty(cx, id, rval, NULL, NULL, JSPROP_ENUMERATE))
3465 goto error;
3466 END_CASE(JSOP_DEFSHARP)
3468 BEGIN_CASE(JSOP_USESHARP)
3469 slot = GET_UINT16(regs.pc);
3470 JS_ASSERT(slot + 1 < fp->script->nfixed);
3471 lval = fp->slots[slot];
3472 i = (jsint) GET_UINT16(regs.pc + UINT16_LEN);
3473 if (JSVAL_IS_VOID(lval)) {
3474 rval = JSVAL_VOID;
3475 } else {
3476 obj = JSVAL_TO_OBJECT(fp->slots[slot]);
3477 id = INT_TO_JSID(i);
3478 if (!obj->getProperty(cx, id, &rval))
3479 goto error;
3481 if (!JSVAL_IS_OBJECT(rval)) {
3482 char numBuf[12];
3484 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
3485 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3486 JSMSG_BAD_SHARP_USE, numBuf);
3487 goto error;
3489 PUSH_OPND(rval);
3490 END_CASE(JSOP_USESHARP)
3492 BEGIN_CASE(JSOP_SHARPINIT)
3493 slot = GET_UINT16(regs.pc);
3494 JS_ASSERT(slot + 1 < fp->script->nfixed);
3495 vp = &fp->slots[slot];
3496 rval = vp[1];
3499 * We peek ahead safely here because empty initialisers get zero
3500 * JSOP_SHARPINIT ops, and non-empty ones get two: the first comes
3501 * immediately after JSOP_NEWINIT followed by one or more property
3502 * initialisers; and the second comes directly before JSOP_ENDINIT.
3504 if (regs.pc[JSOP_SHARPINIT_LENGTH] != JSOP_ENDINIT) {
3505 rval = JSVAL_IS_VOID(rval) ? JSVAL_ONE : rval + 2;
3506 } else {
3507 JS_ASSERT(JSVAL_IS_INT(rval));
3508 rval -= 2;
3509 if (rval == JSVAL_ZERO)
3510 vp[0] = JSVAL_VOID;
3512 vp[1] = rval;
3513 END_CASE(JSOP_SHARPINIT)
3515 #endif /* JS_HAS_SHARP_VARS */
3517 BEGIN_CASE(JSOP_GOSUB)
3518 PUSH(JSVAL_FALSE);
3519 i = (regs.pc - script->main) + JSOP_GOSUB_LENGTH;
3520 PUSH(INT_TO_JSVAL(i));
3521 len = GET_JUMP_OFFSET(regs.pc);
3522 END_VARLEN_CASE
3524 BEGIN_CASE(JSOP_GOSUBX)
3525 PUSH(JSVAL_FALSE);
3526 i = (regs.pc - script->main) + JSOP_GOSUBX_LENGTH;
3527 len = GET_JUMPX_OFFSET(regs.pc);
3528 PUSH(INT_TO_JSVAL(i));
3529 END_VARLEN_CASE
3531 BEGIN_CASE(JSOP_RETSUB)
3532 /* Pop [exception or hole, retsub pc-index]. */
3533 rval = POP();
3534 lval = POP();
3535 JS_ASSERT(JSVAL_IS_BOOLEAN(lval));
3536 if (JSVAL_TO_BOOLEAN(lval)) {
3538 * Exception was pending during finally, throw it *before* we adjust
3539 * pc, because pc indexes into script->trynotes. This turns out not to
3540 * be necessary, but it seems clearer. And it points out a FIXME:
3541 * 350509, due to Igor Bukanov.
3543 cx->throwing = JS_TRUE;
3544 cx->exception = rval;
3545 goto error;
3547 JS_ASSERT(JSVAL_IS_INT(rval));
3548 len = JSVAL_TO_INT(rval);
3549 regs.pc = script->main;
3550 END_VARLEN_CASE
3552 BEGIN_CASE(JSOP_EXCEPTION)
3553 JS_ASSERT(cx->throwing);
3554 PUSH(cx->exception);
3555 cx->throwing = JS_FALSE;
3556 CHECK_BRANCH();
3557 END_CASE(JSOP_EXCEPTION)
3559 BEGIN_CASE(JSOP_FINALLY)
3560 CHECK_BRANCH();
3561 END_CASE(JSOP_FINALLY)
3563 BEGIN_CASE(JSOP_THROWING)
3564 JS_ASSERT(!cx->throwing);
3565 cx->throwing = JS_TRUE;
3566 cx->exception = POP_OPND();
3567 END_CASE(JSOP_THROWING)
3569 BEGIN_CASE(JSOP_THROW)
3570 JS_ASSERT(!cx->throwing);
3571 CHECK_BRANCH();
3572 cx->throwing = JS_TRUE;
3573 cx->exception = POP_OPND();
3574 /* let the code at error try to catch the exception. */
3575 goto error;
3577 BEGIN_CASE(JSOP_SETLOCALPOP)
3579 * The stack must have a block with at least one local slot below the
3580 * exception object.
3582 JS_ASSERT((size_t) (regs.sp - StackBase(fp)) >= 2);
3583 slot = GET_UINT16(regs.pc);
3584 JS_ASSERT(slot + 1 < script->nslots);
3585 fp->slots[slot] = POP_OPND();
3586 END_CASE(JSOP_SETLOCALPOP)
3588 BEGIN_CASE(JSOP_IFPRIMTOP)
3590 * If the top of stack is of primitive type, jump to our target. Otherwise
3591 * advance to the next opcode.
3593 JS_ASSERT(regs.sp > StackBase(fp));
3594 rval = FETCH_OPND(-1);
3595 if (JSVAL_IS_PRIMITIVE(rval)) {
3596 len = GET_JUMP_OFFSET(regs.pc);
3597 BRANCH(len);
3599 END_CASE(JSOP_IFPRIMTOP)
3601 BEGIN_CASE(JSOP_PRIMTOP)
3602 JS_ASSERT(regs.sp > StackBase(fp));
3603 lval = FETCH_OPND(-1);
3604 i = GET_INT8(regs.pc);
3605 if (!JSVAL_IS_PRIMITIVE(lval)) {
3606 lval = FETCH_OPND(-2);
3607 js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO, -2, lval, NULL,
3608 (i == JSTYPE_VOID) ? "primitive type" : JS_TYPE_STR(i));
3609 goto error;
3611 END_CASE(JSOP_PRIMTOP)
3613 BEGIN_CASE(JSOP_OBJTOP)
3614 lval = FETCH_OPND(-1);
3615 if (JSVAL_IS_PRIMITIVE(lval)) {
3616 js_ReportValueError(cx, GET_UINT16(regs.pc), -1, lval, NULL);
3617 goto error;
3619 END_CASE(JSOP_OBJTOP)
3621 BEGIN_CASE(JSOP_INSTANCEOF)
3622 rval = FETCH_OPND(-1);
3623 if (JSVAL_IS_PRIMITIVE(rval) ||
3624 !(obj = JSVAL_TO_OBJECT(rval))->map->ops->hasInstance) {
3625 js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
3626 -1, rval, NULL);
3627 goto error;
3629 lval = FETCH_OPND(-2);
3630 cond = JS_FALSE;
3631 if (!obj->map->ops->hasInstance(cx, obj, lval, &cond))
3632 goto error;
3633 regs.sp--;
3634 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));
3635 END_CASE(JSOP_INSTANCEOF)
3637 #if JS_HAS_DEBUGGER_KEYWORD
3638 BEGIN_CASE(JSOP_DEBUGGER)
3640 JSDebuggerHandler handler = cx->debugHooks->debuggerHandler;
3641 if (handler) {
3642 switch (handler(cx, script, regs.pc, &rval, cx->debugHooks->debuggerHandlerData)) {
3643 case JSTRAP_ERROR:
3644 goto error;
3645 case JSTRAP_CONTINUE:
3646 break;
3647 case JSTRAP_RETURN:
3648 fp->rval = rval;
3649 ok = JS_TRUE;
3650 goto forced_return;
3651 case JSTRAP_THROW:
3652 cx->throwing = JS_TRUE;
3653 cx->exception = rval;
3654 goto error;
3655 default:;
3657 CHECK_INTERRUPT_HANDLER();
3660 END_CASE(JSOP_DEBUGGER)
3661 #endif /* JS_HAS_DEBUGGER_KEYWORD */
3663 #if JS_HAS_XML_SUPPORT
3664 BEGIN_CASE(JSOP_DEFXMLNS)
3665 rval = POP();
3666 if (!js_SetDefaultXMLNamespace(cx, rval))
3667 goto error;
3668 END_CASE(JSOP_DEFXMLNS)
3670 BEGIN_CASE(JSOP_ANYNAME)
3671 if (!js_GetAnyName(cx, &rval))
3672 goto error;
3673 PUSH_OPND(rval);
3674 END_CASE(JSOP_ANYNAME)
3676 BEGIN_CASE(JSOP_QNAMEPART)
3677 LOAD_ATOM(0);
3678 PUSH_OPND(ATOM_KEY(atom));
3679 END_CASE(JSOP_QNAMEPART)
3681 BEGIN_CASE(JSOP_QNAMECONST)
3682 LOAD_ATOM(0);
3683 rval = ATOM_KEY(atom);
3684 lval = FETCH_OPND(-1);
3685 obj = js_ConstructXMLQNameObject(cx, lval, rval);
3686 if (!obj)
3687 goto error;
3688 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
3689 END_CASE(JSOP_QNAMECONST)
3691 BEGIN_CASE(JSOP_QNAME)
3692 rval = FETCH_OPND(-1);
3693 lval = FETCH_OPND(-2);
3694 obj = js_ConstructXMLQNameObject(cx, lval, rval);
3695 if (!obj)
3696 goto error;
3697 regs.sp--;
3698 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
3699 END_CASE(JSOP_QNAME)
3701 BEGIN_CASE(JSOP_TOATTRNAME)
3702 rval = FETCH_OPND(-1);
3703 if (!js_ToAttributeName(cx, &rval))
3704 goto error;
3705 STORE_OPND(-1, rval);
3706 END_CASE(JSOP_TOATTRNAME)
3708 BEGIN_CASE(JSOP_TOATTRVAL)
3709 rval = FETCH_OPND(-1);
3710 JS_ASSERT(JSVAL_IS_STRING(rval));
3711 str = js_EscapeAttributeValue(cx, JSVAL_TO_STRING(rval), JS_FALSE);
3712 if (!str)
3713 goto error;
3714 STORE_OPND(-1, STRING_TO_JSVAL(str));
3715 END_CASE(JSOP_TOATTRVAL)
3717 BEGIN_CASE(JSOP_ADDATTRNAME)
3718 BEGIN_CASE(JSOP_ADDATTRVAL)
3719 rval = FETCH_OPND(-1);
3720 lval = FETCH_OPND(-2);
3721 str = JSVAL_TO_STRING(lval);
3722 str2 = JSVAL_TO_STRING(rval);
3723 str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2);
3724 if (!str)
3725 goto error;
3726 regs.sp--;
3727 STORE_OPND(-1, STRING_TO_JSVAL(str));
3728 END_CASE(JSOP_ADDATTRNAME)
3730 BEGIN_CASE(JSOP_BINDXMLNAME)
3731 lval = FETCH_OPND(-1);
3732 if (!js_FindXMLProperty(cx, lval, &obj, &id))
3733 goto error;
3734 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
3735 PUSH_OPND(ID_TO_VALUE(id));
3736 END_CASE(JSOP_BINDXMLNAME)
3738 BEGIN_CASE(JSOP_SETXMLNAME)
3739 obj = JSVAL_TO_OBJECT(FETCH_OPND(-3));
3740 rval = FETCH_OPND(-1);
3741 FETCH_ELEMENT_ID(obj, -2, id);
3742 if (!obj->setProperty(cx, id, &rval))
3743 goto error;
3744 rval = FETCH_OPND(-1);
3745 regs.sp -= 2;
3746 STORE_OPND(-1, rval);
3747 END_CASE(JSOP_SETXMLNAME)
3749 BEGIN_CASE(JSOP_CALLXMLNAME)
3750 BEGIN_CASE(JSOP_XMLNAME)
3751 lval = FETCH_OPND(-1);
3752 if (!js_FindXMLProperty(cx, lval, &obj, &id))
3753 goto error;
3754 if (!obj->getProperty(cx, id, &rval))
3755 goto error;
3756 STORE_OPND(-1, rval);
3757 if (op == JSOP_CALLXMLNAME)
3758 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3759 END_CASE(JSOP_XMLNAME)
3761 BEGIN_CASE(JSOP_DESCENDANTS)
3762 BEGIN_CASE(JSOP_DELDESC)
3763 FETCH_OBJECT(cx, -2, lval, obj);
3764 rval = FETCH_OPND(-1);
3765 if (!js_GetXMLDescendants(cx, obj, rval, &rval))
3766 goto error;
3768 if (op == JSOP_DELDESC) {
3769 regs.sp[-1] = rval; /* set local root */
3770 if (!js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval)))
3771 goto error;
3772 rval = JSVAL_TRUE; /* always succeed */
3775 regs.sp--;
3776 STORE_OPND(-1, rval);
3777 END_CASE(JSOP_DESCENDANTS)
3779 BEGIN_CASE(JSOP_FILTER)
3781 * We push the hole value before jumping to [enditer] so we can detect the
3782 * first iteration and direct js_StepXMLListFilter to initialize filter's
3783 * state.
3785 PUSH_OPND(JSVAL_HOLE);
3786 len = GET_JUMP_OFFSET(regs.pc);
3787 JS_ASSERT(len > 0);
3788 END_VARLEN_CASE
3790 BEGIN_CASE(JSOP_ENDFILTER)
3791 cond = (regs.sp[-1] != JSVAL_HOLE);
3792 if (cond) {
3793 /* Exit the "with" block left from the previous iteration. */
3794 js_LeaveWith(cx);
3796 if (!js_StepXMLListFilter(cx, cond))
3797 goto error;
3798 if (regs.sp[-1] != JSVAL_NULL) {
3800 * Decrease sp after EnterWith returns as we use sp[-1] there to root
3801 * temporaries.
3803 JS_ASSERT(VALUE_IS_XML(cx, regs.sp[-1]));
3804 if (!js_EnterWith(cx, -2))
3805 goto error;
3806 regs.sp--;
3807 len = GET_JUMP_OFFSET(regs.pc);
3808 JS_ASSERT(len < 0);
3809 BRANCH(len);
3811 regs.sp--;
3812 END_CASE(JSOP_ENDFILTER);
3814 BEGIN_CASE(JSOP_TOXML)
3815 rval = FETCH_OPND(-1);
3816 obj = js_ValueToXMLObject(cx, rval);
3817 if (!obj)
3818 goto error;
3819 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
3820 END_CASE(JSOP_TOXML)
3822 BEGIN_CASE(JSOP_TOXMLLIST)
3823 rval = FETCH_OPND(-1);
3824 obj = js_ValueToXMLListObject(cx, rval);
3825 if (!obj)
3826 goto error;
3827 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
3828 END_CASE(JSOP_TOXMLLIST)
3830 BEGIN_CASE(JSOP_XMLTAGEXPR)
3831 rval = FETCH_OPND(-1);
3832 str = js_ValueToString(cx, rval);
3833 if (!str)
3834 goto error;
3835 STORE_OPND(-1, STRING_TO_JSVAL(str));
3836 END_CASE(JSOP_XMLTAGEXPR)
3838 BEGIN_CASE(JSOP_XMLELTEXPR)
3839 rval = FETCH_OPND(-1);
3840 if (VALUE_IS_XML(cx, rval)) {
3841 str = js_ValueToXMLString(cx, rval);
3842 } else {
3843 str = js_ValueToString(cx, rval);
3844 if (str)
3845 str = js_EscapeElementValue(cx, str);
3847 if (!str)
3848 goto error;
3849 STORE_OPND(-1, STRING_TO_JSVAL(str));
3850 END_CASE(JSOP_XMLELTEXPR)
3852 BEGIN_CASE(JSOP_XMLOBJECT)
3853 LOAD_OBJECT(0);
3854 obj = js_CloneXMLObject(cx, obj);
3855 if (!obj)
3856 goto error;
3857 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3858 END_CASE(JSOP_XMLOBJECT)
3860 BEGIN_CASE(JSOP_XMLCDATA)
3861 LOAD_ATOM(0);
3862 str = ATOM_TO_STRING(atom);
3863 obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_TEXT, NULL, str);
3864 if (!obj)
3865 goto error;
3866 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3867 END_CASE(JSOP_XMLCDATA)
3869 BEGIN_CASE(JSOP_XMLCOMMENT)
3870 LOAD_ATOM(0);
3871 str = ATOM_TO_STRING(atom);
3872 obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_COMMENT, NULL, str);
3873 if (!obj)
3874 goto error;
3875 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3876 END_CASE(JSOP_XMLCOMMENT)
3878 BEGIN_CASE(JSOP_XMLPI)
3879 LOAD_ATOM(0);
3880 str = ATOM_TO_STRING(atom);
3881 rval = FETCH_OPND(-1);
3882 str2 = JSVAL_TO_STRING(rval);
3883 obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_PROCESSING_INSTRUCTION, str, str2);
3884 if (!obj)
3885 goto error;
3886 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
3887 END_CASE(JSOP_XMLPI)
3889 BEGIN_CASE(JSOP_GETFUNNS)
3890 if (!js_GetFunctionNamespace(cx, &rval))
3891 goto error;
3892 PUSH_OPND(rval);
3893 END_CASE(JSOP_GETFUNNS)
3894 #endif /* JS_HAS_XML_SUPPORT */
3896 BEGIN_CASE(JSOP_ENTERBLOCK)
3897 LOAD_OBJECT(0);
3898 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj));
3899 JS_ASSERT(StackBase(fp) + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp);
3900 vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj);
3901 JS_ASSERT(regs.sp < vp);
3902 JS_ASSERT(vp <= fp->slots + script->nslots);
3903 while (regs.sp < vp) {
3904 STORE_OPND(0, JSVAL_VOID);
3905 regs.sp++;
3908 #ifdef DEBUG
3909 JS_ASSERT(fp->blockChain == obj->getParent());
3912 * The young end of fp->scopeChain may omit blocks if we haven't closed
3913 * over them, but if there are any closure blocks on fp->scopeChain, they'd
3914 * better be (clones of) ancestors of the block we're entering now;
3915 * anything else we should have popped off fp->scopeChain when we left its
3916 * static scope.
3918 obj2 = fp->scopeChain;
3919 while ((clasp = obj2->getClass()) == &js_WithClass)
3920 obj2 = obj2->getParent();
3921 if (clasp == &js_BlockClass &&
3922 obj2->getPrivate() == fp) {
3923 JSObject *youngestProto = obj2->getProto();
3924 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(youngestProto));
3925 parent = obj;
3926 while ((parent = parent->getParent()) != youngestProto)
3927 JS_ASSERT(parent);
3929 #endif
3931 fp->blockChain = obj;
3932 END_CASE(JSOP_ENTERBLOCK)
3934 BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
3935 BEGIN_CASE(JSOP_LEAVEBLOCK)
3937 #ifdef DEBUG
3938 JS_ASSERT(fp->blockChain->getClass() == &js_BlockClass);
3939 uintN blockDepth = OBJ_BLOCK_DEPTH(cx, fp->blockChain);
3941 JS_ASSERT(blockDepth <= StackDepth(script));
3942 #endif
3944 * If we're about to leave the dynamic scope of a block that has been
3945 * cloned onto fp->scopeChain, clear its private data, move its locals from
3946 * the stack into the clone, and pop it off the chain.
3948 obj = fp->scopeChain;
3949 if (obj->getProto() == fp->blockChain) {
3950 JS_ASSERT(obj->getClass() == &js_BlockClass);
3951 if (!js_PutBlockObject(cx, JS_TRUE))
3952 goto error;
3955 /* Pop the block chain, too. */
3956 fp->blockChain = fp->blockChain->getParent();
3958 /* Move the result of the expression to the new topmost stack slot. */
3959 if (op == JSOP_LEAVEBLOCKEXPR)
3960 rval = FETCH_OPND(-1);
3961 regs.sp -= GET_UINT16(regs.pc);
3962 if (op == JSOP_LEAVEBLOCKEXPR) {
3963 JS_ASSERT(StackBase(fp) + blockDepth == regs.sp - 1);
3964 STORE_OPND(-1, rval);
3965 } else {
3966 JS_ASSERT(StackBase(fp) + blockDepth == regs.sp);
3969 END_CASE(JSOP_LEAVEBLOCK)
3971 #if JS_HAS_GENERATORS
3972 BEGIN_CASE(JSOP_GENERATOR)
3973 ASSERT_NOT_THROWING(cx);
3974 regs.pc += JSOP_GENERATOR_LENGTH;
3975 obj = js_NewGenerator(cx);
3976 if (!obj)
3977 goto error;
3978 JS_ASSERT(!fp->callobj && !fp->argsobj);
3979 fp->rval = OBJECT_TO_JSVAL(obj);
3980 ok = JS_TRUE;
3981 if (inlineCallCount != 0)
3982 goto inline_return;
3983 goto exit;
3985 BEGIN_CASE(JSOP_YIELD)
3986 ASSERT_NOT_THROWING(cx);
3987 if (FRAME_TO_GENERATOR(fp)->state == JSGEN_CLOSING) {
3988 js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD,
3989 JSDVG_SEARCH_STACK, fp->argv[-2], NULL);
3990 goto error;
3992 fp->rval = FETCH_OPND(-1);
3993 fp->flags |= JSFRAME_YIELDING;
3994 regs.pc += JSOP_YIELD_LENGTH;
3995 ok = JS_TRUE;
3996 goto exit;
3998 BEGIN_CASE(JSOP_ARRAYPUSH)
3999 slot = GET_UINT16(regs.pc);
4000 JS_ASSERT(script->nfixed <= slot);
4001 JS_ASSERT(slot < script->nslots);
4002 lval = fp->slots[slot];
4003 obj = JSVAL_TO_OBJECT(lval);
4004 rval = FETCH_OPND(-1);
4005 if (!js_ArrayCompPush(cx, obj, rval))
4006 goto error;
4007 regs.sp--;
4008 END_CASE(JSOP_ARRAYPUSH)
4009 #endif /* JS_HAS_GENERATORS */
4011 #if JS_THREADED_INTERP
4012 L_JSOP_BACKPATCH:
4013 L_JSOP_BACKPATCH_POP:
4015 # if !JS_HAS_GENERATORS
4016 L_JSOP_GENERATOR:
4017 L_JSOP_YIELD:
4018 L_JSOP_ARRAYPUSH:
4019 # endif
4021 # if !JS_HAS_SHARP_VARS
4022 L_JSOP_DEFSHARP:
4023 L_JSOP_USESHARP:
4024 L_JSOP_SHARPINIT:
4025 # endif
4027 # if !JS_HAS_DESTRUCTURING
4028 L_JSOP_ENUMCONSTELEM:
4029 # endif
4031 # if !JS_HAS_XML_SUPPORT
4032 L_JSOP_CALLXMLNAME:
4033 L_JSOP_STARTXMLEXPR:
4034 L_JSOP_STARTXML:
4035 L_JSOP_DELDESC:
4036 L_JSOP_GETFUNNS:
4037 L_JSOP_XMLPI:
4038 L_JSOP_XMLCOMMENT:
4039 L_JSOP_XMLCDATA:
4040 L_JSOP_XMLOBJECT:
4041 L_JSOP_XMLELTEXPR:
4042 L_JSOP_XMLTAGEXPR:
4043 L_JSOP_TOXMLLIST:
4044 L_JSOP_TOXML:
4045 L_JSOP_ENDFILTER:
4046 L_JSOP_FILTER:
4047 L_JSOP_DESCENDANTS:
4048 L_JSOP_XMLNAME:
4049 L_JSOP_SETXMLNAME:
4050 L_JSOP_BINDXMLNAME:
4051 L_JSOP_ADDATTRVAL:
4052 L_JSOP_ADDATTRNAME:
4053 L_JSOP_TOATTRVAL:
4054 L_JSOP_TOATTRNAME:
4055 L_JSOP_QNAME:
4056 L_JSOP_QNAMECONST:
4057 L_JSOP_QNAMEPART:
4058 L_JSOP_ANYNAME:
4059 L_JSOP_DEFXMLNS:
4060 # endif
4062 L_JSOP_UNUSED218:
4064 #endif /* !JS_THREADED_INTERP */