/[jscoverage]/trunk/js/jsops.cpp
ViewVC logotype

Contents of /trunk/js/jsops.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 507 - (show annotations)
Sun Jan 10 07:23:34 2010 UTC (9 years, 5 months ago) by siliconforks
File size: 168151 byte(s)
Update SpiderMonkey from Firefox 3.6rc1.

1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=79:
3 *
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 *
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/
11 *
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.
16 *
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
19 *
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.
24 *
25 * Contributor(s):
26 *
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.
38 *
39 * ***** END LICENSE BLOCK ***** */
40
41 /* This file needs to be included in possibly multiple places. */
42
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 */
49 {
50 bool moreInterrupts = false;
51 JSTrapHandler handler = cx->debugHooks->interruptHandler;
52 if (handler) {
53 #ifdef JS_TRACER
54 if (TRACE_RECORDER(cx))
55 js_AbortRecording(cx, "interrupt handler");
56 #endif
57 switch (handler(cx, script, regs.pc, &rval,
58 cx->debugHooks->interruptHandlerData)) {
59 case JSTRAP_ERROR:
60 goto error;
61 case JSTRAP_CONTINUE:
62 break;
63 case JSTRAP_RETURN:
64 fp->rval = rval;
65 ok = JS_TRUE;
66 goto forced_return;
67 case JSTRAP_THROW:
68 cx->throwing = JS_TRUE;
69 cx->exception = rval;
70 goto error;
71 default:;
72 }
73 moreInterrupts = true;
74 }
75
76 #ifdef JS_TRACER
77 TraceRecorder* tr = TRACE_RECORDER(cx);
78 if (tr) {
79 JSRecordingStatus status = TraceRecorder::monitorRecording(cx, tr, op);
80 switch (status) {
81 case JSRS_CONTINUE:
82 moreInterrupts = true;
83 break;
84 case JSRS_IMACRO:
85 atoms = COMMON_ATOMS_START(&rt->atomState);
86 op = JSOp(*regs.pc);
87 DO_OP(); /* keep interrupting for op. */
88 break;
89 case JSRS_ERROR:
90 // The code at 'error:' aborts the recording.
91 goto error;
92 case JSRS_STOP:
93 break;
94 default:
95 JS_NOT_REACHED("Bad recording status");
96 }
97 }
98 #endif /* !JS_TRACER */
99
100 #if JS_THREADED_INTERP
101 #ifdef MOZ_TRACEVIS
102 if (!moreInterrupts)
103 js_ExitTraceVisState(cx, R_ABORT);
104 #endif
105 jumpTable = moreInterrupts ? interruptJumpTable : normalJumpTable;
106 JS_EXTENSION_(goto *normalJumpTable[op]);
107 #else
108 switchMask = moreInterrupts ? -1 : 0;
109 switchOp = intN(op);
110 goto do_switch;
111 #endif
112 }
113
114 /* No-ops for ease of decompilation. */
115 ADD_EMPTY_CASE(JSOP_NOP)
116 ADD_EMPTY_CASE(JSOP_CONDSWITCH)
117 ADD_EMPTY_CASE(JSOP_TRY)
118 ADD_EMPTY_CASE(JSOP_TRACE)
119 #if JS_HAS_XML_SUPPORT
120 ADD_EMPTY_CASE(JSOP_STARTXML)
121 ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
122 #endif
123 END_EMPTY_CASES
124
125 /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
126 BEGIN_CASE(JSOP_LINENO)
127 END_CASE(JSOP_LINENO)
128
129 BEGIN_CASE(JSOP_PUSH)
130 PUSH_OPND(JSVAL_VOID);
131 END_CASE(JSOP_PUSH)
132
133 BEGIN_CASE(JSOP_POP)
134 regs.sp--;
135 END_CASE(JSOP_POP)
136
137 BEGIN_CASE(JSOP_POPN)
138 regs.sp -= GET_UINT16(regs.pc);
139 #ifdef DEBUG
140 JS_ASSERT(StackBase(fp) <= regs.sp);
141 obj = fp->blockChain;
142 JS_ASSERT_IF(obj,
143 OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
144 <= (size_t) (regs.sp - StackBase(fp)));
145 for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
146 clasp = OBJ_GET_CLASS(cx, obj);
147 if (clasp != &js_BlockClass && clasp != &js_WithClass)
148 continue;
149 if (obj->getPrivate() != fp)
150 break;
151 JS_ASSERT(StackBase(fp) + OBJ_BLOCK_DEPTH(cx, obj)
152 + ((clasp == &js_BlockClass)
153 ? OBJ_BLOCK_COUNT(cx, obj)
154 : 1)
155 <= regs.sp);
156 }
157 #endif
158 END_CASE(JSOP_POPN)
159
160 BEGIN_CASE(JSOP_SETRVAL)
161 BEGIN_CASE(JSOP_POPV)
162 ASSERT_NOT_THROWING(cx);
163 fp->rval = POP_OPND();
164 END_CASE(JSOP_POPV)
165
166 BEGIN_CASE(JSOP_ENTERWITH)
167 if (!js_EnterWith(cx, -1))
168 goto error;
169
170 /*
171 * We must ensure that different "with" blocks have different
172 * stack depth associated with them. This allows the try handler
173 * search to properly recover the scope chain. Thus we must keep
174 * the stack at least at the current level.
175 *
176 * We set sp[-1] to the current "with" object to help asserting
177 * the enter/leave balance in [leavewith].
178 */
179 regs.sp[-1] = OBJECT_TO_JSVAL(fp->scopeChain);
180 END_CASE(JSOP_ENTERWITH)
181
182 BEGIN_CASE(JSOP_LEAVEWITH)
183 JS_ASSERT(regs.sp[-1] == OBJECT_TO_JSVAL(fp->scopeChain));
184 regs.sp--;
185 js_LeaveWith(cx);
186 END_CASE(JSOP_LEAVEWITH)
187
188 BEGIN_CASE(JSOP_RETURN)
189 fp->rval = POP_OPND();
190 /* FALL THROUGH */
191
192 BEGIN_CASE(JSOP_RETRVAL) /* fp->rval already set */
193 BEGIN_CASE(JSOP_STOP)
194 /*
195 * When the inlined frame exits with an exception or an error, ok
196 * will be false after the inline_return label.
197 */
198 ASSERT_NOT_THROWING(cx);
199 CHECK_BRANCH();
200
201 if (fp->imacpc) {
202 /*
203 * If we are at the end of an imacro, return to its caller in
204 * the current frame.
205 */
206 JS_ASSERT(op == JSOP_STOP);
207
208 end_imacro:
209 JS_ASSERT((uintN)(regs.sp - fp->slots) <= script->nslots);
210 regs.pc = fp->imacpc + js_CodeSpec[*fp->imacpc].length;
211 fp->imacpc = NULL;
212 atoms = script->atomMap.vector;
213 op = JSOp(*regs.pc);
214 DO_OP();
215 }
216
217 JS_ASSERT(regs.sp == StackBase(fp));
218 if ((fp->flags & JSFRAME_CONSTRUCTING) &&
219 JSVAL_IS_PRIMITIVE(fp->rval)) {
220 if (!fp->fun) {
221 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
222 JSMSG_BAD_NEW_RESULT,
223 js_ValueToPrintableString(cx, rval));
224 goto error;
225 }
226 fp->rval = OBJECT_TO_JSVAL(fp->thisp);
227 }
228 ok = JS_TRUE;
229 if (inlineCallCount)
230 inline_return:
231 {
232 JSInlineFrame *ifp = (JSInlineFrame *) fp;
233 void *hookData = ifp->hookData;
234
235 JS_ASSERT(!fp->blockChain);
236 JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
237
238 if (script->staticLevel < JS_DISPLAY_SIZE)
239 cx->display[script->staticLevel] = fp->displaySave;
240
241 if (hookData) {
242 JSInterpreterHook hook;
243 JSBool status;
244
245 hook = cx->debugHooks->callHook;
246 if (hook) {
247 /*
248 * Do not pass &ok directly as exposing the address
249 * inhibits optimizations and uninitialised warnings.
250 */
251 status = ok;
252 hook(cx, fp, JS_FALSE, &status, hookData);
253 ok = status;
254 CHECK_INTERRUPT_HANDLER();
255 }
256 }
257
258 /*
259 * If fp has a call object, sync values and clear the back-
260 * pointer. This can happen for a lightweight function if it
261 * calls eval unexpectedly (in a way that is hidden from the
262 * compiler). See bug 325540.
263 */
264 fp->putActivationObjects(cx);
265
266 #ifdef INCLUDE_MOZILLA_DTRACE
267 /* DTrace function return, inlines */
268 if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
269 jsdtrace_function_rval(cx, fp, fp->fun, &fp->rval);
270 if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
271 jsdtrace_function_return(cx, fp, fp->fun);
272 #endif
273
274 /* Restore context version only if callee hasn't set version. */
275 if (JS_LIKELY(cx->version == currentVersion)) {
276 currentVersion = ifp->callerVersion;
277 if (currentVersion != cx->version)
278 js_SetVersion(cx, currentVersion);
279 }
280
281 /*
282 * If inline-constructing, replace primitive rval with the new
283 * object passed in via |this|, and instrument this constructor
284 * invocation
285 */
286 if (fp->flags & JSFRAME_CONSTRUCTING) {
287 if (JSVAL_IS_PRIMITIVE(fp->rval))
288 fp->rval = OBJECT_TO_JSVAL(fp->thisp);
289 JS_RUNTIME_METER(cx->runtime, constructs);
290 }
291
292 /* Restore caller's registers. */
293 regs = ifp->callerRegs;
294
295 /* Store the return value in the caller's operand frame. */
296 regs.sp -= 1 + (size_t) ifp->frame.argc;
297 regs.sp[-1] = fp->rval;
298
299 /* Restore cx->fp and release the inline frame's space. */
300 cx->fp = fp = fp->down;
301 JS_ASSERT(fp->regs == &ifp->callerRegs);
302 fp->regs = &regs;
303 JS_ARENA_RELEASE(&cx->stackPool, ifp->mark);
304
305 /* Restore the calling script's interpreter registers. */
306 script = fp->script;
307 atoms = FrameAtomBase(cx, fp);
308
309 /* Resume execution in the calling frame. */
310 inlineCallCount--;
311 if (JS_LIKELY(ok)) {
312 TRACE_0(LeaveFrame);
313 JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, script, regs.pc)].length
314 == JSOP_CALL_LENGTH);
315 len = JSOP_CALL_LENGTH;
316 DO_NEXT_OP(len);
317 }
318 goto error;
319 }
320 goto exit;
321
322 BEGIN_CASE(JSOP_DEFAULT)
323 (void) POP();
324 /* FALL THROUGH */
325 BEGIN_CASE(JSOP_GOTO)
326 len = GET_JUMP_OFFSET(regs.pc);
327 BRANCH(len);
328 END_CASE(JSOP_GOTO)
329
330 BEGIN_CASE(JSOP_IFEQ)
331 POP_BOOLEAN(cx, rval, cond);
332 if (cond == JS_FALSE) {
333 len = GET_JUMP_OFFSET(regs.pc);
334 BRANCH(len);
335 }
336 END_CASE(JSOP_IFEQ)
337
338 BEGIN_CASE(JSOP_IFNE)
339 POP_BOOLEAN(cx, rval, cond);
340 if (cond != JS_FALSE) {
341 len = GET_JUMP_OFFSET(regs.pc);
342 BRANCH(len);
343 }
344 END_CASE(JSOP_IFNE)
345
346 BEGIN_CASE(JSOP_OR)
347 POP_BOOLEAN(cx, rval, cond);
348 if (cond == JS_TRUE) {
349 len = GET_JUMP_OFFSET(regs.pc);
350 PUSH_OPND(rval);
351 DO_NEXT_OP(len);
352 }
353 END_CASE(JSOP_OR)
354
355 BEGIN_CASE(JSOP_AND)
356 POP_BOOLEAN(cx, rval, cond);
357 if (cond == JS_FALSE) {
358 len = GET_JUMP_OFFSET(regs.pc);
359 PUSH_OPND(rval);
360 DO_NEXT_OP(len);
361 }
362 END_CASE(JSOP_AND)
363
364 BEGIN_CASE(JSOP_DEFAULTX)
365 (void) POP();
366 /* FALL THROUGH */
367 BEGIN_CASE(JSOP_GOTOX)
368 len = GET_JUMPX_OFFSET(regs.pc);
369 BRANCH(len);
370 END_CASE(JSOP_GOTOX);
371
372 BEGIN_CASE(JSOP_IFEQX)
373 POP_BOOLEAN(cx, rval, cond);
374 if (cond == JS_FALSE) {
375 len = GET_JUMPX_OFFSET(regs.pc);
376 BRANCH(len);
377 }
378 END_CASE(JSOP_IFEQX)
379
380 BEGIN_CASE(JSOP_IFNEX)
381 POP_BOOLEAN(cx, rval, cond);
382 if (cond != JS_FALSE) {
383 len = GET_JUMPX_OFFSET(regs.pc);
384 BRANCH(len);
385 }
386 END_CASE(JSOP_IFNEX)
387
388 BEGIN_CASE(JSOP_ORX)
389 POP_BOOLEAN(cx, rval, cond);
390 if (cond == JS_TRUE) {
391 len = GET_JUMPX_OFFSET(regs.pc);
392 PUSH_OPND(rval);
393 DO_NEXT_OP(len);
394 }
395 END_CASE(JSOP_ORX)
396
397 BEGIN_CASE(JSOP_ANDX)
398 POP_BOOLEAN(cx, rval, cond);
399 if (cond == JS_FALSE) {
400 len = GET_JUMPX_OFFSET(regs.pc);
401 PUSH_OPND(rval);
402 DO_NEXT_OP(len);
403 }
404 END_CASE(JSOP_ANDX)
405
406 /*
407 * If the index value at sp[n] is not an int that fits in a jsval, it could
408 * be an object (an XML QName, AttributeName, or AnyName), but only if we are
409 * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a
410 * string atom id.
411 */
412 #define FETCH_ELEMENT_ID(obj, n, id) \
413 JS_BEGIN_MACRO \
414 jsval idval_ = FETCH_OPND(n); \
415 if (JSVAL_IS_INT(idval_)) { \
416 id = INT_JSVAL_TO_JSID(idval_); \
417 } else { \
418 if (!js_InternNonIntElementId(cx, obj, idval_, &id)) \
419 goto error; \
420 regs.sp[n] = ID_TO_VALUE(id); \
421 } \
422 JS_END_MACRO
423
424 #define TRY_BRANCH_AFTER_COND(cond,spdec) \
425 JS_BEGIN_MACRO \
426 uintN diff_; \
427 JS_ASSERT(js_CodeSpec[op].length == 1); \
428 diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ; \
429 if (diff_ <= 1) { \
430 regs.sp -= spdec; \
431 if (cond == (diff_ != 0)) { \
432 ++regs.pc; \
433 len = GET_JUMP_OFFSET(regs.pc); \
434 BRANCH(len); \
435 } \
436 len = 1 + JSOP_IFEQ_LENGTH; \
437 DO_NEXT_OP(len); \
438 } \
439 JS_END_MACRO
440
441 BEGIN_CASE(JSOP_IN)
442 rval = FETCH_OPND(-1);
443 if (JSVAL_IS_PRIMITIVE(rval)) {
444 js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rval, NULL);
445 goto error;
446 }
447 obj = JSVAL_TO_OBJECT(rval);
448 FETCH_ELEMENT_ID(obj, -2, id);
449 if (!obj->lookupProperty(cx, id, &obj2, &prop))
450 goto error;
451 cond = prop != NULL;
452 if (prop)
453 obj2->dropProperty(cx, prop);
454 TRY_BRANCH_AFTER_COND(cond, 2);
455 regs.sp--;
456 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));
457 END_CASE(JSOP_IN)
458
459 BEGIN_CASE(JSOP_ITER)
460 JS_ASSERT(regs.sp > StackBase(fp));
461 flags = regs.pc[1];
462 if (!js_ValueToIterator(cx, flags, &regs.sp[-1]))
463 goto error;
464 CHECK_INTERRUPT_HANDLER();
465 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));
466 PUSH(JSVAL_VOID);
467 END_CASE(JSOP_ITER)
468
469 BEGIN_CASE(JSOP_NEXTITER)
470 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
471 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-2]));
472 if (!js_CallIteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-2]), &regs.sp[-1]))
473 goto error;
474 CHECK_INTERRUPT_HANDLER();
475 rval = BOOLEAN_TO_JSVAL(regs.sp[-1] != JSVAL_HOLE);
476 PUSH(rval);
477 END_CASE(JSOP_NEXTITER)
478
479 BEGIN_CASE(JSOP_ENDITER)
480 /*
481 * Decrease the stack pointer even when !ok -- see comments in the
482 * exception capturing code for details.
483 */
484 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
485 ok = js_CloseIterator(cx, regs.sp[-2]);
486 regs.sp -= 2;
487 if (!ok)
488 goto error;
489 END_CASE(JSOP_ENDITER)
490
491 BEGIN_CASE(JSOP_FORARG)
492 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
493 slot = GET_ARGNO(regs.pc);
494 JS_ASSERT(slot < fp->fun->nargs);
495 fp->argv[slot] = regs.sp[-1];
496 END_CASE(JSOP_FORARG)
497
498 BEGIN_CASE(JSOP_FORLOCAL)
499 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
500 slot = GET_SLOTNO(regs.pc);
501 JS_ASSERT(slot < fp->script->nslots);
502 fp->slots[slot] = regs.sp[-1];
503 END_CASE(JSOP_FORLOCAL)
504
505 BEGIN_CASE(JSOP_FORNAME)
506 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
507 LOAD_ATOM(0);
508 id = ATOM_TO_JSID(atom);
509 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
510 goto error;
511 if (prop)
512 obj2->dropProperty(cx, prop);
513 ok = obj->setProperty(cx, id, &regs.sp[-1]);
514 if (!ok)
515 goto error;
516 END_CASE(JSOP_FORNAME)
517
518 BEGIN_CASE(JSOP_FORPROP)
519 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
520 LOAD_ATOM(0);
521 id = ATOM_TO_JSID(atom);
522 FETCH_OBJECT(cx, -1, lval, obj);
523 ok = obj->setProperty(cx, id, &regs.sp[-2]);
524 if (!ok)
525 goto error;
526 regs.sp--;
527 END_CASE(JSOP_FORPROP)
528
529 BEGIN_CASE(JSOP_FORELEM)
530 /*
531 * JSOP_FORELEM simply dups the property identifier at top of stack
532 * and lets the subsequent JSOP_ENUMELEM opcode sequence handle the
533 * left-hand side expression evaluation and assignment. This opcode
534 * exists solely to help the decompiler.
535 */
536 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
537 rval = FETCH_OPND(-1);
538 PUSH(rval);
539 END_CASE(JSOP_FORELEM)
540
541 BEGIN_CASE(JSOP_DUP)
542 JS_ASSERT(regs.sp > StackBase(fp));
543 rval = FETCH_OPND(-1);
544 PUSH(rval);
545 END_CASE(JSOP_DUP)
546
547 BEGIN_CASE(JSOP_DUP2)
548 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
549 lval = FETCH_OPND(-2);
550 rval = FETCH_OPND(-1);
551 PUSH(lval);
552 PUSH(rval);
553 END_CASE(JSOP_DUP2)
554
555 BEGIN_CASE(JSOP_SWAP)
556 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
557 lval = FETCH_OPND(-2);
558 rval = FETCH_OPND(-1);
559 STORE_OPND(-1, lval);
560 STORE_OPND(-2, rval);
561 END_CASE(JSOP_SWAP)
562
563 BEGIN_CASE(JSOP_PICK)
564 i = regs.pc[1];
565 JS_ASSERT(regs.sp - (i+1) >= StackBase(fp));
566 lval = regs.sp[-(i+1)];
567 memmove(regs.sp - (i+1), regs.sp - i, sizeof(jsval)*i);
568 regs.sp[-1] = lval;
569 END_CASE(JSOP_PICK)
570
571 #define PROPERTY_OP(n, call) \
572 JS_BEGIN_MACRO \
573 /* Fetch the left part and resolve it to a non-null object. */ \
574 FETCH_OBJECT(cx, n, lval, obj); \
575 \
576 /* Get or set the property. */ \
577 if (!call) \
578 goto error; \
579 JS_END_MACRO
580
581 #define ELEMENT_OP(n, call) \
582 JS_BEGIN_MACRO \
583 /* Fetch the left part and resolve it to a non-null object. */ \
584 FETCH_OBJECT(cx, n - 1, lval, obj); \
585 \
586 /* Fetch index and convert it to id suitable for use with obj. */ \
587 FETCH_ELEMENT_ID(obj, n, id); \
588 \
589 /* Get or set the element. */ \
590 if (!call) \
591 goto error; \
592 JS_END_MACRO
593
594 #define NATIVE_GET(cx,obj,pobj,sprop,vp) \
595 JS_BEGIN_MACRO \
596 if (SPROP_HAS_STUB_GETTER(sprop)) { \
597 /* Fast path for Object instance properties. */ \
598 JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT || \
599 !SPROP_HAS_STUB_SETTER(sprop)); \
600 *vp = ((sprop)->slot != SPROP_INVALID_SLOT) \
601 ? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot) \
602 : JSVAL_VOID; \
603 } else { \
604 if (!js_NativeGet(cx, obj, pobj, sprop, vp)) \
605 goto error; \
606 } \
607 JS_END_MACRO
608
609 #define NATIVE_SET(cx,obj,sprop,entry,vp) \
610 JS_BEGIN_MACRO \
611 TRACE_2(SetPropHit, entry, sprop); \
612 if (SPROP_HAS_STUB_SETTER(sprop) && \
613 (sprop)->slot != SPROP_INVALID_SLOT) { \
614 /* Fast path for, e.g., Object instance properties. */ \
615 LOCKED_OBJ_WRITE_SLOT(cx, obj, (sprop)->slot, *vp); \
616 } else { \
617 if (!js_NativeSet(cx, obj, sprop, vp)) \
618 goto error; \
619 } \
620 JS_END_MACRO
621
622 /*
623 * Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is
624 * the constant length of the SET opcode sequence, and spdec is the constant
625 * by which to decrease the stack pointer to pop all of the SET op's operands.
626 *
627 * NB: unlike macros that could conceivably be replaced by functions (ignoring
628 * goto error), where a call should not have to be braced in order to expand
629 * correctly (e.g., in if (cond) FOO(); else BAR()), these three macros lack
630 * JS_{BEGIN,END}_MACRO brackets. They are also indented so as to align with
631 * nearby opcode code.
632 */
633 #define SKIP_POP_AFTER_SET(oplen,spdec) \
634 if (regs.pc[oplen] == JSOP_POP) { \
635 regs.sp -= spdec; \
636 regs.pc += oplen + JSOP_POP_LENGTH; \
637 op = (JSOp) *regs.pc; \
638 DO_OP(); \
639 }
640
641 #define END_SET_CASE(OP) \
642 SKIP_POP_AFTER_SET(OP##_LENGTH, 1); \
643 END_CASE(OP)
644
645 #define END_SET_CASE_STORE_RVAL(OP,spdec) \
646 SKIP_POP_AFTER_SET(OP##_LENGTH, spdec); \
647 rval = FETCH_OPND(-1); \
648 regs.sp -= (spdec) - 1; \
649 STORE_OPND(-1, rval); \
650 END_CASE(OP)
651
652 BEGIN_CASE(JSOP_SETCONST)
653 LOAD_ATOM(0);
654 obj = fp->varobj;
655 rval = FETCH_OPND(-1);
656 if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), rval,
657 JS_PropertyStub, JS_PropertyStub,
658 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) {
659 goto error;
660 }
661 END_SET_CASE(JSOP_SETCONST);
662
663 #if JS_HAS_DESTRUCTURING
664 BEGIN_CASE(JSOP_ENUMCONSTELEM)
665 rval = FETCH_OPND(-3);
666 FETCH_OBJECT(cx, -2, lval, obj);
667 FETCH_ELEMENT_ID(obj, -1, id);
668 if (!obj->defineProperty(cx, id, rval,
669 JS_PropertyStub, JS_PropertyStub,
670 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) {
671 goto error;
672 }
673 regs.sp -= 3;
674 END_CASE(JSOP_ENUMCONSTELEM)
675 #endif
676
677 BEGIN_CASE(JSOP_BINDNAME)
678 do {
679 JSPropCacheEntry *entry;
680
681 /*
682 * We can skip the property lookup for the global object. If
683 * the property does not exist anywhere on the scope chain,
684 * JSOP_SETNAME adds the property to the global.
685 *
686 * As a consequence of this optimization for the global object
687 * we run its JSRESOLVE_ASSIGNING-tolerant resolve hooks only
688 * in JSOP_SETNAME, after the interpreter evaluates the right-
689 * hand-side of the assignment, and not here.
690 *
691 * This should be transparent to the hooks because the script,
692 * instead of name = rhs, could have used global.name = rhs
693 * given a global object reference, which also calls the hooks
694 * only after evaluating the rhs. We desire such resolve hook
695 * equivalence between the two forms.
696 */
697 obj = fp->scopeChain;
698 if (!OBJ_GET_PARENT(cx, obj))
699 break;
700 if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
701 PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);
702 if (!atom) {
703 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
704 JS_UNLOCK_OBJ(cx, obj2);
705 break;
706 }
707 } else {
708 entry = NULL;
709 LOAD_ATOM(0);
710 }
711 id = ATOM_TO_JSID(atom);
712 obj = js_FindIdentifierBase(cx, fp->scopeChain, id);
713 if (!obj)
714 goto error;
715 } while (0);
716 PUSH_OPND(OBJECT_TO_JSVAL(obj));
717 END_CASE(JSOP_BINDNAME)
718
719 BEGIN_CASE(JSOP_IMACOP)
720 JS_ASSERT(JS_UPTRDIFF(fp->imacpc, script->code) < script->length);
721 op = JSOp(*fp->imacpc);
722 DO_OP();
723
724 #define BITWISE_OP(OP) \
725 JS_BEGIN_MACRO \
726 FETCH_INT(cx, -2, i); \
727 FETCH_INT(cx, -1, j); \
728 i = i OP j; \
729 regs.sp--; \
730 STORE_INT(cx, -1, i); \
731 JS_END_MACRO
732
733 BEGIN_CASE(JSOP_BITOR)
734 BITWISE_OP(|);
735 END_CASE(JSOP_BITOR)
736
737 BEGIN_CASE(JSOP_BITXOR)
738 BITWISE_OP(^);
739 END_CASE(JSOP_BITXOR)
740
741 BEGIN_CASE(JSOP_BITAND)
742 BITWISE_OP(&);
743 END_CASE(JSOP_BITAND)
744
745 #define RELATIONAL_OP(OP) \
746 JS_BEGIN_MACRO \
747 rval = FETCH_OPND(-1); \
748 lval = FETCH_OPND(-2); \
749 /* Optimize for two int-tagged operands (typical loop control). */ \
750 if ((lval & rval) & JSVAL_INT) { \
751 cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval); \
752 } else { \
753 if (!JSVAL_IS_PRIMITIVE(lval)) \
754 DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \
755 if (!JSVAL_IS_PRIMITIVE(rval)) \
756 DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \
757 if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { \
758 str = JSVAL_TO_STRING(lval); \
759 str2 = JSVAL_TO_STRING(rval); \
760 cond = js_CompareStrings(str, str2) OP 0; \
761 } else { \
762 VALUE_TO_NUMBER(cx, -2, lval, d); \
763 VALUE_TO_NUMBER(cx, -1, rval, d2); \
764 cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \
765 } \
766 } \
767 TRY_BRANCH_AFTER_COND(cond, 2); \
768 regs.sp--; \
769 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
770 JS_END_MACRO
771
772 /*
773 * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies
774 * because they begin if/else chains, so callers must not put semicolons after
775 * the call expressions!
776 */
777 #if JS_HAS_XML_SUPPORT
778 #define XML_EQUALITY_OP(OP) \
779 if ((ltmp == JSVAL_OBJECT && \
780 (obj2 = JSVAL_TO_OBJECT(lval)) && \
781 OBJECT_IS_XML(cx, obj2)) || \
782 (rtmp == JSVAL_OBJECT && \
783 (obj2 = JSVAL_TO_OBJECT(rval)) && \
784 OBJECT_IS_XML(cx, obj2))) { \
785 if (JSVAL_IS_OBJECT(rval) && obj2 == JSVAL_TO_OBJECT(rval)) \
786 rval = lval; \
787 if (!js_TestXMLEquality(cx, obj2, rval, &cond)) \
788 goto error; \
789 cond = cond OP JS_TRUE; \
790 } else
791
792 #define EXTENDED_EQUALITY_OP(OP) \
793 if (ltmp == JSVAL_OBJECT && \
794 (obj2 = JSVAL_TO_OBJECT(lval)) && \
795 ((clasp = OBJ_GET_CLASS(cx, obj2))->flags & JSCLASS_IS_EXTENDED)) { \
796 JSExtendedClass *xclasp; \
797 \
798 xclasp = (JSExtendedClass *) clasp; \
799 if (!xclasp->equality(cx, obj2, rval, &cond)) \
800 goto error; \
801 cond = cond OP JS_TRUE; \
802 } else
803 #else
804 #define XML_EQUALITY_OP(OP) /* nothing */
805 #define EXTENDED_EQUALITY_OP(OP) /* nothing */
806 #endif
807
808 #define EQUALITY_OP(OP, IFNAN) \
809 JS_BEGIN_MACRO \
810 rval = FETCH_OPND(-1); \
811 lval = FETCH_OPND(-2); \
812 ltmp = JSVAL_TAG(lval); \
813 rtmp = JSVAL_TAG(rval); \
814 XML_EQUALITY_OP(OP) \
815 if (ltmp == rtmp) { \
816 if (ltmp == JSVAL_STRING) { \
817 str = JSVAL_TO_STRING(lval); \
818 str2 = JSVAL_TO_STRING(rval); \
819 cond = js_EqualStrings(str, str2) OP JS_TRUE; \
820 } else if (ltmp == JSVAL_DOUBLE) { \
821 d = *JSVAL_TO_DOUBLE(lval); \
822 d2 = *JSVAL_TO_DOUBLE(rval); \
823 cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \
824 } else { \
825 EXTENDED_EQUALITY_OP(OP) \
826 /* Handle all undefined (=>NaN) and int combinations. */ \
827 cond = lval OP rval; \
828 } \
829 } else { \
830 if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) { \
831 cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) OP 1; \
832 } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) { \
833 cond = 1 OP 0; \
834 } else { \
835 if (ltmp == JSVAL_OBJECT) { \
836 DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval); \
837 ltmp = JSVAL_TAG(lval); \
838 } else if (rtmp == JSVAL_OBJECT) { \
839 DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); \
840 rtmp = JSVAL_TAG(rval); \
841 } \
842 if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) { \
843 str = JSVAL_TO_STRING(lval); \
844 str2 = JSVAL_TO_STRING(rval); \
845 cond = js_EqualStrings(str, str2) OP JS_TRUE; \
846 } else { \
847 VALUE_TO_NUMBER(cx, -2, lval, d); \
848 VALUE_TO_NUMBER(cx, -1, rval, d2); \
849 cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \
850 } \
851 } \
852 } \
853 TRY_BRANCH_AFTER_COND(cond, 2); \
854 regs.sp--; \
855 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
856 JS_END_MACRO
857
858 BEGIN_CASE(JSOP_EQ)
859 EQUALITY_OP(==, JS_FALSE);
860 END_CASE(JSOP_EQ)
861
862 BEGIN_CASE(JSOP_NE)
863 EQUALITY_OP(!=, JS_TRUE);
864 END_CASE(JSOP_NE)
865
866 #define STRICT_EQUALITY_OP(OP) \
867 JS_BEGIN_MACRO \
868 rval = FETCH_OPND(-1); \
869 lval = FETCH_OPND(-2); \
870 cond = js_StrictlyEqual(cx, lval, rval) OP JS_TRUE; \
871 regs.sp--; \
872 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
873 JS_END_MACRO
874
875 BEGIN_CASE(JSOP_STRICTEQ)
876 STRICT_EQUALITY_OP(==);
877 END_CASE(JSOP_STRICTEQ)
878
879 BEGIN_CASE(JSOP_STRICTNE)
880 STRICT_EQUALITY_OP(!=);
881 END_CASE(JSOP_STRICTNE)
882
883 BEGIN_CASE(JSOP_CASE)
884 STRICT_EQUALITY_OP(==);
885 (void) POP();
886 if (cond) {
887 len = GET_JUMP_OFFSET(regs.pc);
888 BRANCH(len);
889 }
890 PUSH(lval);
891 END_CASE(JSOP_CASE)
892
893 BEGIN_CASE(JSOP_CASEX)
894 STRICT_EQUALITY_OP(==);
895 (void) POP();
896 if (cond) {
897 len = GET_JUMPX_OFFSET(regs.pc);
898 BRANCH(len);
899 }
900 PUSH(lval);
901 END_CASE(JSOP_CASEX)
902
903 BEGIN_CASE(JSOP_LT)
904 RELATIONAL_OP(<);
905 END_CASE(JSOP_LT)
906
907 BEGIN_CASE(JSOP_LE)
908 RELATIONAL_OP(<=);
909 END_CASE(JSOP_LE)
910
911 BEGIN_CASE(JSOP_GT)
912 RELATIONAL_OP(>);
913 END_CASE(JSOP_GT)
914
915 BEGIN_CASE(JSOP_GE)
916 RELATIONAL_OP(>=);
917 END_CASE(JSOP_GE)
918
919 #undef EQUALITY_OP
920 #undef RELATIONAL_OP
921
922 #define SIGNED_SHIFT_OP(OP) \
923 JS_BEGIN_MACRO \
924 FETCH_INT(cx, -2, i); \
925 FETCH_INT(cx, -1, j); \
926 i = i OP (j & 31); \
927 regs.sp--; \
928 STORE_INT(cx, -1, i); \
929 JS_END_MACRO
930
931 BEGIN_CASE(JSOP_LSH)
932 SIGNED_SHIFT_OP(<<);
933 END_CASE(JSOP_LSH)
934
935 BEGIN_CASE(JSOP_RSH)
936 SIGNED_SHIFT_OP(>>);
937 END_CASE(JSOP_RSH)
938
939 BEGIN_CASE(JSOP_URSH)
940 {
941 uint32 u;
942
943 FETCH_UINT(cx, -2, u);
944 FETCH_INT(cx, -1, j);
945 u >>= (j & 31);
946 regs.sp--;
947 STORE_UINT(cx, -1, u);
948 }
949 END_CASE(JSOP_URSH)
950
951 #undef BITWISE_OP
952 #undef SIGNED_SHIFT_OP
953
954 BEGIN_CASE(JSOP_ADD)
955 rval = FETCH_OPND(-1);
956 lval = FETCH_OPND(-2);
957 #if JS_HAS_XML_SUPPORT
958 if (!JSVAL_IS_PRIMITIVE(lval) &&
959 (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) &&
960 VALUE_IS_XML(cx, rval)) {
961 if (!js_ConcatenateXML(cx, obj2, rval, &rval))
962 goto error;
963 regs.sp--;
964 STORE_OPND(-1, rval);
965 } else
966 #endif
967 {
968 if (!JSVAL_IS_PRIMITIVE(lval))
969 DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval);
970 if (!JSVAL_IS_PRIMITIVE(rval))
971 DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval);
972 if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) {
973 if (cond) {
974 str = JSVAL_TO_STRING(lval);
975 str2 = js_ValueToString(cx, rval);
976 if (!str2)
977 goto error;
978 regs.sp[-1] = STRING_TO_JSVAL(str2);
979 } else {
980 str2 = JSVAL_TO_STRING(rval);
981 str = js_ValueToString(cx, lval);
982 if (!str)
983 goto error;
984 regs.sp[-2] = STRING_TO_JSVAL(str);
985 }
986 str = js_ConcatStrings(cx, str, str2);
987 if (!str)
988 goto error;
989 regs.sp--;
990 STORE_OPND(-1, STRING_TO_JSVAL(str));
991 } else {
992 VALUE_TO_NUMBER(cx, -2, lval, d);
993 VALUE_TO_NUMBER(cx, -1, rval, d2);
994 d += d2;
995 regs.sp--;
996 STORE_NUMBER(cx, -1, d);
997 }
998 }
999 END_CASE(JSOP_ADD)
1000
1001 BEGIN_CASE(JSOP_CONCATN)
1002 {
1003 #ifdef JS_TRACER
1004 JS_ASSERT_IF(fp->imacpc,
1005 *fp->imacpc == JSOP_CONCATN && *regs.pc == JSOP_IMACOP);
1006
1007 /*
1008 * This instruction can be executed in three contexts. (1) is normal
1009 * execution. (2) is while recording, during an imacro 'imacop'.
1010 * (3) is during a failed recording or when trace execution aborts
1011 * during a recorded imacro.
1012 * 1. !imacro : N args on stack, pc is regs.pc
1013 * 2. imacro && recording : N args on stack, pc is fp->imacpc
1014 * 3. imacro && !recording : N+2 args on stack, pc is fp->imacpc
1015 */
1016 bool imacro = fp->imacpc != NULL;
1017 bool recording = TRACE_RECORDER(cx) != NULL;
1018 if (imacro) {
1019 argc = GET_ARGC(fp->imacpc);
1020 if (!recording)
1021 js_ConcatPostImacroStackCleanup(argc, regs, NULL);
1022 } else {
1023 #endif /* JS_TRACER */
1024 argc = GET_ARGC(regs.pc);
1025 #ifdef JS_TRACER
1026 }
1027 #endif /* JS_TRACER */
1028
1029 JSCharBuffer buf(cx);
1030 for (vp = regs.sp - argc; vp < regs.sp; vp++) {
1031 if ((!JSVAL_IS_PRIMITIVE(*vp) &&
1032 !JSVAL_TO_OBJECT(*vp)->defaultValue(cx, JSTYPE_VOID, vp)) ||
1033 !js_ValueToCharBuffer(cx, *vp, buf)) {
1034 goto error;
1035 }
1036 }
1037
1038 str = js_NewStringFromCharBuffer(cx, buf);
1039 if (!str)
1040 goto error;
1041
1042 regs.sp -= argc - 1;
1043 STORE_OPND(-1, STRING_TO_JSVAL(str));
1044
1045 #ifdef JS_TRACER
1046 if (imacro) {
1047 /* END_CASE does pc += CONCATN_LENGTH. (IMACOP YOU IDIOT!) */
1048 regs.pc -= JSOP_CONCATN_LENGTH - JSOP_IMACOP_LENGTH;
1049 }
1050 #endif /* JS_TRACER */
1051 }
1052 END_CASE(JSOP_CONCATN)
1053
1054 #define BINARY_OP(OP) \
1055 JS_BEGIN_MACRO \
1056 FETCH_NUMBER(cx, -2, d); \
1057 FETCH_NUMBER(cx, -1, d2); \
1058 d = d OP d2; \
1059 regs.sp--; \
1060 STORE_NUMBER(cx, -1, d); \
1061 JS_END_MACRO
1062
1063 BEGIN_CASE(JSOP_SUB)
1064 BINARY_OP(-);
1065 END_CASE(JSOP_SUB)
1066
1067 BEGIN_CASE(JSOP_MUL)
1068 BINARY_OP(*);
1069 END_CASE(JSOP_MUL)
1070
1071 BEGIN_CASE(JSOP_DIV)
1072 FETCH_NUMBER(cx, -1, d2);
1073 FETCH_NUMBER(cx, -2, d);
1074 regs.sp--;
1075 if (d2 == 0) {
1076 #ifdef XP_WIN
1077 /* XXX MSVC miscompiles such that (NaN == 0) */
1078 if (JSDOUBLE_IS_NaN(d2))
1079 rval = DOUBLE_TO_JSVAL(rt->jsNaN);
1080 else
1081 #endif
1082 if (d == 0 || JSDOUBLE_IS_NaN(d))
1083 rval = DOUBLE_TO_JSVAL(rt->jsNaN);
1084 else if (JSDOUBLE_IS_NEG(d) != JSDOUBLE_IS_NEG(d2))
1085 rval = DOUBLE_TO_JSVAL(rt->jsNegativeInfinity);
1086 else
1087 rval = DOUBLE_TO_JSVAL(rt->jsPositiveInfinity);
1088 STORE_OPND(-1, rval);
1089 } else {
1090 d /= d2;
1091 STORE_NUMBER(cx, -1, d);
1092 }
1093 END_CASE(JSOP_DIV)
1094
1095 BEGIN_CASE(JSOP_MOD)
1096 FETCH_NUMBER(cx, -1, d2);
1097 FETCH_NUMBER(cx, -2, d);
1098 regs.sp--;
1099 if (d2 == 0) {
1100 STORE_OPND(-1, DOUBLE_TO_JSVAL(rt->jsNaN));
1101 } else {
1102 d = js_fmod(d, d2);
1103 STORE_NUMBER(cx, -1, d);
1104 }
1105 END_CASE(JSOP_MOD)
1106
1107 BEGIN_CASE(JSOP_NOT)
1108 POP_BOOLEAN(cx, rval, cond);
1109 PUSH_OPND(BOOLEAN_TO_JSVAL(!cond));
1110 END_CASE(JSOP_NOT)
1111
1112 BEGIN_CASE(JSOP_BITNOT)
1113 FETCH_INT(cx, -1, i);
1114 i = ~i;
1115 STORE_INT(cx, -1, i);
1116 END_CASE(JSOP_BITNOT)
1117
1118 BEGIN_CASE(JSOP_NEG)
1119 /*
1120 * When the operand is int jsval, INT_FITS_IN_JSVAL(i) implies
1121 * INT_FITS_IN_JSVAL(-i) unless i is 0 or JSVAL_INT_MIN when the
1122 * results, -0.0 or JSVAL_INT_MAX + 1, are jsdouble values.
1123 */
1124 rval = FETCH_OPND(-1);
1125 if (JSVAL_IS_INT(rval) &&
1126 rval != INT_TO_JSVAL(JSVAL_INT_MIN) &&
1127 (i = JSVAL_TO_INT(rval)) != 0) {
1128 JS_STATIC_ASSERT(!INT_FITS_IN_JSVAL(-JSVAL_INT_MIN));
1129 i = -i;
1130 JS_ASSERT(INT_FITS_IN_JSVAL(i));
1131 regs.sp[-1] = INT_TO_JSVAL(i);
1132 } else {
1133 if (JSVAL_IS_DOUBLE(rval)) {
1134 d = *JSVAL_TO_DOUBLE(rval);
1135 } else {
1136 d = js_ValueToNumber(cx, &regs.sp[-1]);
1137 if (JSVAL_IS_NULL(regs.sp[-1]))
1138 goto error;
1139 JS_ASSERT(JSVAL_IS_NUMBER(regs.sp[-1]) ||
1140 regs.sp[-1] == JSVAL_TRUE);
1141 }
1142 d = -d;
1143 if (!js_NewNumberInRootedValue(cx, d, &regs.sp[-1]))
1144 goto error;
1145 }
1146 END_CASE(JSOP_NEG)
1147
1148 BEGIN_CASE(JSOP_POS)
1149 rval = FETCH_OPND(-1);
1150 if (!JSVAL_IS_NUMBER(rval)) {
1151 d = js_ValueToNumber(cx, &regs.sp[-1]);
1152 rval = regs.sp[-1];
1153 if (JSVAL_IS_NULL(rval))
1154 goto error;
1155 if (rval == JSVAL_TRUE) {
1156 if (!js_NewNumberInRootedValue(cx, d, &regs.sp[-1]))
1157 goto error;
1158 } else {
1159 JS_ASSERT(JSVAL_IS_NUMBER(rval));
1160 }
1161 }
1162 END_CASE(JSOP_POS)
1163
1164 BEGIN_CASE(JSOP_DELNAME)
1165 LOAD_ATOM(0);
1166 id = ATOM_TO_JSID(atom);
1167 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
1168 goto error;
1169
1170 /* ECMA says to return true if name is undefined or inherited. */
1171 PUSH_OPND(JSVAL_TRUE);
1172 if (prop) {
1173 obj2->dropProperty(cx, prop);
1174 if (!obj->deleteProperty(cx, id, &regs.sp[-1]))
1175 goto error;
1176 }
1177 END_CASE(JSOP_DELNAME)
1178
1179 BEGIN_CASE(JSOP_DELPROP)
1180 LOAD_ATOM(0);
1181 id = ATOM_TO_JSID(atom);
1182 PROPERTY_OP(-1, obj->deleteProperty(cx, id, &rval));
1183 STORE_OPND(-1, rval);
1184 END_CASE(JSOP_DELPROP)
1185
1186 BEGIN_CASE(JSOP_DELELEM)
1187 ELEMENT_OP(-1, obj->deleteProperty(cx, id, &rval));
1188 regs.sp--;
1189 STORE_OPND(-1, rval);
1190 END_CASE(JSOP_DELELEM)
1191
1192 BEGIN_CASE(JSOP_TYPEOFEXPR)
1193 BEGIN_CASE(JSOP_TYPEOF)
1194 rval = FETCH_OPND(-1);
1195 type = JS_TypeOfValue(cx, rval);
1196 atom = rt->atomState.typeAtoms[type];
1197 STORE_OPND(-1, ATOM_KEY(atom));
1198 END_CASE(JSOP_TYPEOF)
1199
1200 BEGIN_CASE(JSOP_VOID)
1201 STORE_OPND(-1, JSVAL_VOID);
1202 END_CASE(JSOP_VOID)
1203
1204 BEGIN_CASE(JSOP_INCELEM)
1205 BEGIN_CASE(JSOP_DECELEM)
1206 BEGIN_CASE(JSOP_ELEMINC)
1207 BEGIN_CASE(JSOP_ELEMDEC)
1208 /*
1209 * Delay fetching of id until we have the object to ensure
1210 * the proper evaluation order. See bug 372331.
1211 */
1212 id = 0;
1213 i = -2;
1214 goto fetch_incop_obj;
1215
1216 BEGIN_CASE(JSOP_INCPROP)
1217 BEGIN_CASE(JSOP_DECPROP)
1218 BEGIN_CASE(JSOP_PROPINC)
1219 BEGIN_CASE(JSOP_PROPDEC)
1220 LOAD_ATOM(0);
1221 id = ATOM_TO_JSID(atom);
1222 i = -1;
1223
1224 fetch_incop_obj:
1225 FETCH_OBJECT(cx, i, lval, obj);
1226 if (id == 0)
1227 FETCH_ELEMENT_ID(obj, -1, id);
1228 goto do_incop;
1229
1230 BEGIN_CASE(JSOP_INCNAME)
1231 BEGIN_CASE(JSOP_DECNAME)
1232 BEGIN_CASE(JSOP_NAMEINC)
1233 BEGIN_CASE(JSOP_NAMEDEC)
1234 {
1235 JSPropCacheEntry *entry;
1236
1237 obj = fp->scopeChain;
1238 if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
1239 PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);
1240 if (!atom) {
1241 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
1242 if (obj == obj2 && PCVAL_IS_SLOT(entry->vword)) {
1243 slot = PCVAL_TO_SLOT(entry->vword);
1244 JS_ASSERT(slot < OBJ_SCOPE(obj)->freeslot);
1245 rval = LOCKED_OBJ_GET_SLOT(obj, slot);
1246 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {
1247 rtmp = rval;
1248 rval += (js_CodeSpec[op].format & JOF_INC) ? 2 : -2;
1249 if (!(js_CodeSpec[op].format & JOF_POST))
1250 rtmp = rval;
1251 LOCKED_OBJ_SET_SLOT(obj, slot, rval);
1252 JS_UNLOCK_OBJ(cx, obj);
1253 PUSH_OPND(rtmp);
1254 len = JSOP_INCNAME_LENGTH;
1255 DO_NEXT_OP(len);
1256 }
1257 }
1258 JS_UNLOCK_OBJ(cx, obj2);
1259 LOAD_ATOM(0);
1260 }
1261 } else {
1262 LOAD_ATOM(0);
1263 }
1264 id = ATOM_TO_JSID(atom);
1265 if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
1266 goto error;
1267 if (!prop)
1268 goto atom_not_defined;
1269 obj2->dropProperty(cx, prop);
1270 }
1271
1272 do_incop:
1273 {
1274 const JSCodeSpec *cs;
1275 jsval v;
1276
1277 /*
1278 * We need a root to store the value to leave on the stack until
1279 * we have done with obj->setProperty.
1280 */
1281 PUSH_OPND(JSVAL_NULL);
1282 if (!obj->getProperty(cx, id, &regs.sp[-1]))
1283 goto error;
1284
1285 cs = &js_CodeSpec[op];
1286 JS_ASSERT(cs->ndefs == 1);
1287 JS_ASSERT((cs->format & JOF_TMPSLOT_MASK) == JOF_TMPSLOT2);
1288 v = regs.sp[-1];
1289 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(v))) {
1290 jsval incr;
1291
1292 incr = (cs->format & JOF_INC) ? 2 : -2;
1293 if (cs->format & JOF_POST) {
1294 regs.sp[-1] = v + incr;
1295 } else {
1296 v += incr;
1297 regs.sp[-1] = v;
1298 }
1299 fp->flags |= JSFRAME_ASSIGNING;
1300 ok = obj->setProperty(cx, id, &regs.sp[-1]);
1301 fp->flags &= ~JSFRAME_ASSIGNING;
1302 if (!ok)
1303 goto error;
1304
1305 /*
1306 * We must set regs.sp[-1] to v for both post and pre increments
1307 * as the setter overwrites regs.sp[-1].
1308 */
1309 regs.sp[-1] = v;
1310 } else {
1311 /* We need an extra root for the result. */
1312 PUSH_OPND(JSVAL_NULL);
1313 if (!js_DoIncDec(cx, cs, &regs.sp[-2], &regs.sp[-1]))
1314 goto error;
1315 fp->flags |= JSFRAME_ASSIGNING;
1316 ok = obj->setProperty(cx, id, &regs.sp[-1]);
1317 fp->flags &= ~JSFRAME_ASSIGNING;
1318 if (!ok)
1319 goto error;
1320 regs.sp--;
1321 }
1322
1323 if (cs->nuses == 0) {
1324 /* regs.sp[-1] already contains the result of name increment. */
1325 } else {
1326 rtmp = regs.sp[-1];
1327 regs.sp -= cs->nuses;
1328 regs.sp[-1] = rtmp;
1329 }
1330 len = cs->length;
1331 DO_NEXT_OP(len);
1332 }
1333
1334 {
1335 jsval incr, incr2;
1336
1337 /* Position cases so the most frequent i++ does not need a jump. */
1338 BEGIN_CASE(JSOP_DECARG)
1339 incr = -2; incr2 = -2; goto do_arg_incop;
1340 BEGIN_CASE(JSOP_ARGDEC)
1341 incr = -2; incr2 = 0; goto do_arg_incop;
1342 BEGIN_CASE(JSOP_INCARG)
1343 incr = 2; incr2 = 2; goto do_arg_incop;
1344 BEGIN_CASE(JSOP_ARGINC)
1345 incr = 2; incr2 = 0;
1346
1347 do_arg_incop:
1348 slot = GET_ARGNO(regs.pc);
1349 JS_ASSERT(slot < fp->fun->nargs);
1350 METER_SLOT_OP(op, slot);
1351 vp = fp->argv + slot;
1352 goto do_int_fast_incop;
1353
1354 BEGIN_CASE(JSOP_DECLOCAL)
1355 incr = -2; incr2 = -2; goto do_local_incop;
1356 BEGIN_CASE(JSOP_LOCALDEC)
1357 incr = -2; incr2 = 0; goto do_local_incop;
1358 BEGIN_CASE(JSOP_INCLOCAL)
1359 incr = 2; incr2 = 2; goto do_local_incop;
1360 BEGIN_CASE(JSOP_LOCALINC)
1361 incr = 2; incr2 = 0;
1362
1363 /*
1364 * do_local_incop comes right before do_int_fast_incop as we want to
1365 * avoid an extra jump for variable cases as local++ is more frequent
1366 * than arg++.
1367 */
1368 do_local_incop:
1369 slot = GET_SLOTNO(regs.pc);
1370 JS_ASSERT(slot < fp->script->nslots);
1371 vp = fp->slots + slot;
1372 METER_SLOT_OP(op, slot);
1373 vp = fp->slots + slot;
1374
1375 do_int_fast_incop:
1376 rval = *vp;
1377 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {
1378 *vp = rval + incr;
1379 JS_ASSERT(JSOP_INCARG_LENGTH == js_CodeSpec[op].length);
1380 SKIP_POP_AFTER_SET(JSOP_INCARG_LENGTH, 0);
1381 PUSH_OPND(rval + incr2);
1382 } else {
1383 PUSH_OPND(rval);
1384 if (!js_DoIncDec(cx, &js_CodeSpec[op], &regs.sp[-1], vp))
1385 goto error;
1386 }
1387 len = JSOP_INCARG_LENGTH;
1388 JS_ASSERT(len == js_CodeSpec[op].length);
1389 DO_NEXT_OP(len);
1390 }
1391
1392 /* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */
1393 #define FAST_GLOBAL_INCREMENT_OP(SLOWOP,INCR,INCR2) \
1394 op2 = SLOWOP; \
1395 incr = INCR; \
1396 incr2 = INCR2; \
1397 goto do_global_incop
1398
1399 {
1400 jsval incr, incr2;
1401
1402 BEGIN_CASE(JSOP_DECGVAR)
1403 FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, -2, -2);
1404 BEGIN_CASE(JSOP_GVARDEC)
1405 FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, -2, 0);
1406 BEGIN_CASE(JSOP_INCGVAR)
1407 FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, 2, 2);
1408 BEGIN_CASE(JSOP_GVARINC)
1409 FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, 2, 0);
1410
1411 #undef FAST_GLOBAL_INCREMENT_OP
1412
1413 do_global_incop:
1414 JS_ASSERT((js_CodeSpec[op].format & JOF_TMPSLOT_MASK) ==
1415 JOF_TMPSLOT2);
1416 slot = GET_SLOTNO(regs.pc);
1417 JS_ASSERT(slot < GlobalVarCount(fp));
1418 METER_SLOT_OP(op, slot);
1419 lval = fp->slots[slot];
1420 if (JSVAL_IS_NULL(lval)) {
1421 op = op2;
1422 DO_OP();
1423 }
1424 slot = JSVAL_TO_INT(lval);
1425 rval = OBJ_GET_SLOT(cx, fp->varobj, slot);
1426 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {
1427 PUSH_OPND(rval + incr2);
1428 rval += incr;
1429 } else {
1430 PUSH_OPND(rval);
1431 PUSH_OPND(JSVAL_NULL); /* Extra root */
1432 if (!js_DoIncDec(cx, &js_CodeSpec[op], &regs.sp[-2], &regs.sp[-1]))
1433 goto error;
1434 rval = regs.sp[-1];
1435 --regs.sp;
1436 }
1437 OBJ_SET_SLOT(cx, fp->varobj, slot, rval);
1438 len = JSOP_INCGVAR_LENGTH; /* all gvar incops are same length */
1439 JS_ASSERT(len == js_CodeSpec[op].length);
1440 DO_NEXT_OP(len);
1441 }
1442
1443 #define COMPUTE_THIS(cx, fp, obj) \
1444 JS_BEGIN_MACRO \
1445 if (!(obj = js_ComputeThisForFrame(cx, fp))) \
1446 goto error; \
1447 JS_END_MACRO
1448
1449 BEGIN_CASE(JSOP_THIS)
1450 COMPUTE_THIS(cx, fp, obj);
1451 PUSH_OPND(OBJECT_TO_JSVAL(obj));
1452 END_CASE(JSOP_THIS)
1453
1454 BEGIN_CASE(JSOP_GETTHISPROP)
1455 i = 0;
1456 COMPUTE_THIS(cx, fp, obj);
1457 PUSH(JSVAL_NULL);
1458 goto do_getprop_with_obj;
1459
1460 #undef COMPUTE_THIS
1461
1462 BEGIN_CASE(JSOP_GETARGPROP)
1463 i = ARGNO_LEN;
1464 slot = GET_ARGNO(regs.pc);
1465 JS_ASSERT(slot < fp->fun->nargs);
1466 PUSH_OPND(fp->argv[slot]);
1467 goto do_getprop_body;
1468
1469 BEGIN_CASE(JSOP_GETLOCALPROP)
1470 i = SLOTNO_LEN;
1471 slot = GET_SLOTNO(regs.pc);
1472 JS_ASSERT(slot < script->nslots);
1473 PUSH_OPND(fp->slots[slot]);
1474 goto do_getprop_body;
1475
1476 BEGIN_CASE(JSOP_GETPROP)
1477 BEGIN_CASE(JSOP_GETXPROP)
1478 i = 0;
1479
1480 do_getprop_body:
1481 lval = FETCH_OPND(-1);
1482
1483 do_getprop_with_lval:
1484 VALUE_TO_OBJECT(cx, -1, lval, obj);
1485
1486 do_getprop_with_obj:
1487 do {
1488 JSObject *aobj;
1489 JSPropCacheEntry *entry;
1490
1491 aobj = js_GetProtoIfDenseArray(cx, obj);
1492 if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) {
1493 PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom);
1494 if (!atom) {
1495 ASSERT_VALID_PROPERTY_CACHE_HIT(i, aobj, obj2, entry);
1496 if (PCVAL_IS_OBJECT(entry->vword)) {
1497 rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);
1498 } else if (PCVAL_IS_SLOT(entry->vword)) {
1499 slot = PCVAL_TO_SLOT(entry->vword);
1500 JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
1501 rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
1502 } else {
1503 JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
1504 sprop = PCVAL_TO_SPROP(entry->vword);
1505 NATIVE_GET(cx, obj, obj2, sprop, &rval);
1506 }
1507 JS_UNLOCK_OBJ(cx, obj2);
1508 break;
1509 }
1510 } else {
1511 entry = NULL;
1512 if (i < 0)
1513 atom = rt->atomState.lengthAtom;
1514 else
1515 LOAD_ATOM(i);
1516 }
1517 id = ATOM_TO_JSID(atom);
1518 if (entry
1519 ? !js_GetPropertyHelper(cx, obj, id, true, &rval)
1520 : !obj->getProperty(cx, id, &rval)) {
1521 goto error;
1522 }
1523 } while (0);
1524
1525 STORE_OPND(-1, rval);
1526 JS_ASSERT(JSOP_GETPROP_LENGTH + i == js_CodeSpec[op].length);
1527 len = JSOP_GETPROP_LENGTH + i;
1528 END_VARLEN_CASE
1529
1530 BEGIN_CASE(JSOP_LENGTH)
1531 lval = FETCH_OPND(-1);
1532 if (JSVAL_IS_STRING(lval)) {
1533 str = JSVAL_TO_STRING(lval);
1534 regs.sp[-1] = INT_TO_JSVAL(str->length());
1535 } else if (!JSVAL_IS_PRIMITIVE(lval) &&
1536 (obj = JSVAL_TO_OBJECT(lval), OBJ_IS_ARRAY(cx, obj))) {
1537 jsuint length;
1538
1539 /*
1540 * We know that the array is created with only its 'length'
1541 * private data in a fixed slot at JSSLOT_ARRAY_LENGTH. See
1542 * also JSOP_ARRAYPUSH, far below.
1543 */
1544 length = obj->fslots[JSSLOT_ARRAY_LENGTH];
1545 if (length <= JSVAL_INT_MAX) {
1546 regs.sp[-1] = INT_TO_JSVAL(length);
1547 } else if (!js_NewDoubleInRootedValue(cx, (jsdouble) length,
1548 &regs.sp[-1])) {
1549 goto error;
1550 }
1551 } else {
1552 i = -2;
1553 goto do_getprop_with_lval;
1554 }
1555 END_CASE(JSOP_LENGTH)
1556
1557 BEGIN_CASE(JSOP_CALLPROP)
1558 {
1559 JSObject *aobj;
1560 JSPropCacheEntry *entry;
1561
1562 lval = FETCH_OPND(-1);
1563 if (!JSVAL_IS_PRIMITIVE(lval)) {
1564 obj = JSVAL_TO_OBJECT(lval);
1565 } else {
1566 if (JSVAL_IS_STRING(lval)) {
1567 i = JSProto_String;
1568 } else if (JSVAL_IS_NUMBER(lval)) {
1569 i = JSProto_Number;
1570 } else if (JSVAL_IS_BOOLEAN(lval)) {
1571 i = JSProto_Boolean;
1572 } else {
1573 JS_ASSERT(JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval));
1574 js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
1575 goto error;
1576 }
1577
1578 if (!js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj))
1579 goto error;
1580 }
1581
1582 aobj = js_GetProtoIfDenseArray(cx, obj);
1583 if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) {
1584 PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom);
1585 if (!atom) {
1586 ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry);
1587 if (PCVAL_IS_OBJECT(entry->vword)) {
1588 rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);
1589 } else if (PCVAL_IS_SLOT(entry->vword)) {
1590 slot = PCVAL_TO_SLOT(entry->vword);
1591 JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
1592 rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
1593 } else {
1594 JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
1595 sprop = PCVAL_TO_SPROP(entry->vword);
1596 NATIVE_GET(cx, obj, obj2, sprop, &rval);
1597 }
1598 JS_UNLOCK_OBJ(cx, obj2);
1599 STORE_OPND(-1, rval);
1600 PUSH_OPND(lval);
1601 goto end_callprop;
1602 }
1603 } else {
1604 entry = NULL;
1605 LOAD_ATOM(0);
1606 }
1607
1608 /*
1609 * Cache miss: use the immediate atom that was loaded for us under
1610 * PROPERTY_CACHE_TEST.
1611 */
1612 id = ATOM_TO_JSID(atom);
1613 PUSH(JSVAL_NULL);
1614 if (!JSVAL_IS_PRIMITIVE(lval)) {
1615 if (!js_GetMethod(cx, obj, id, !!entry, &rval))
1616 goto error;
1617 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
1618 STORE_OPND(-2, rval);
1619 } else {
1620 JS_ASSERT(obj->map->ops->getProperty == js_GetProperty);
1621 if (!js_GetPropertyHelper(cx, obj, id, true, &rval))
1622 goto error;
1623 STORE_OPND(-1, lval);
1624 STORE_OPND(-2, rval);
1625 }
1626
1627 end_callprop:
1628 /* Wrap primitive lval in object clothing if necessary. */
1629 if (JSVAL_IS_PRIMITIVE(lval)) {
1630 /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */
1631 if (!VALUE_IS_FUNCTION(cx, rval) ||
1632 (obj = JSVAL_TO_OBJECT(rval),
1633 fun = GET_FUNCTION_PRIVATE(cx, obj),
1634 !PRIMITIVE_THIS_TEST(fun, lval))) {
1635 if (!js_PrimitiveToObject(cx, &regs.sp[-1]))
1636 goto error;
1637 }
1638 }
1639 #if JS_HAS_NO_SUCH_METHOD
1640 if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) {
1641 LOAD_ATOM(0);
1642 regs.sp[-2] = ATOM_KEY(atom);
1643 if (!js_OnUnknownMethod(cx, regs.sp - 2))
1644 goto error;
1645 }
1646 #endif
1647 }
1648 END_CASE(JSOP_CALLPROP)
1649
1650 BEGIN_CASE(JSOP_SETNAME)
1651 BEGIN_CASE(JSOP_SETPROP)
1652 rval = FETCH_OPND(-1);
1653 lval = FETCH_OPND(-2);
1654 JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval) || op == JSOP_SETPROP);
1655 VALUE_TO_OBJECT(cx, -2, lval, obj);
1656
1657 do {
1658 JSPropCacheEntry *entry;
1659
1660 entry = NULL;
1661 atom = NULL;
1662 if (JS_LIKELY(obj->map->ops->setProperty == js_SetProperty)) {
1663 JSPropertyCache *cache = &JS_PROPERTY_CACHE(cx);
1664 uint32 kshape = OBJ_SHAPE(obj);
1665
1666 /*
1667 * Open-code PROPERTY_CACHE_TEST, specializing for two
1668 * important set-property cases. First:
1669 *
1670 * function f(a, b, c) {
1671 * var o = {p:a, q:b, r:c};
1672 * return o;
1673 * }
1674 *
1675 * or similar real-world cases, which evolve a newborn
1676 * native object predicatably through some bounded number
1677 * of property additions. And second:
1678 *
1679 * o.p = x;
1680 *
1681 * in a frequently executed method or loop body, where p
1682 * will (possibly after the first iteration) always exist
1683 * in native object o.
1684 */
1685 entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)];
1686 PCMETER(cache->pctestentry = entry);
1687 PCMETER(cache->tests++);
1688 PCMETER(cache->settests++);
1689 if (entry->kpc == regs.pc && entry->kshape == kshape) {
1690 JS_ASSERT(PCVCAP_TAG(entry->vcap) <= 1);
1691 if (JS_LOCK_OBJ_IF_SHAPE(cx, obj, kshape)) {
1692 JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
1693 sprop = PCVAL_TO_SPROP(entry->vword);
1694 JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
1695 JS_ASSERT_IF(!(sprop->attrs & JSPROP_SHARED),
1696 PCVCAP_TAG(entry->vcap) == 0);
1697
1698 JSScope *scope = OBJ_SCOPE(obj);
1699 JS_ASSERT(!scope->sealed());
1700
1701 /*
1702 * Fastest path: check whether the cached sprop is
1703 * already in scope and call NATIVE_SET and break
1704 * to get out of the do-while(0). But we can call
1705 * NATIVE_SET only if obj owns scope or sprop is
1706 * shared.
1707 */
1708 bool checkForAdd;
1709 if (sprop->attrs & JSPROP_SHARED) {
1710 if (PCVCAP_TAG(entry->vcap) == 0 ||
1711 ((obj2 = OBJ_GET_PROTO(cx, obj)) &&
1712 OBJ_IS_NATIVE(obj2) &&
1713 OBJ_SHAPE(obj2) == PCVCAP_SHAPE(entry->vcap))) {
1714 goto fast_set_propcache_hit;
1715 }
1716
1717 /* The cache entry doesn't apply. vshape mismatch. */
1718 checkForAdd = false;
1719 } else if (scope->owned()) {
1720 if (sprop == scope->lastProp || scope->has(sprop)) {
1721 fast_set_propcache_hit:
1722 PCMETER(cache->pchits++);
1723 PCMETER(cache->setpchits++);
1724 NATIVE_SET(cx, obj, sprop, entry, &rval);
1725 JS_UNLOCK_SCOPE(cx, scope);
1726 break;
1727 }
1728 checkForAdd =
1729 !(sprop->attrs & JSPROP_SHARED) &&
1730 sprop->parent == scope->lastProp &&
1731 !scope->hadMiddleDelete();
1732 } else {
1733 scope = js_GetMutableScope(cx, obj);
1734 if (!scope) {
1735 JS_UNLOCK_OBJ(cx, obj);
1736 goto error;
1737 }
1738 checkForAdd = !sprop->parent;
1739 }
1740
1741 if (checkForAdd &&
1742 SPROP_HAS_STUB_SETTER(sprop) &&
1743 (slot = sprop->slot) == scope->freeslot) {
1744 /*
1745 * Fast path: adding a plain old property that
1746 * was once at the frontier of the property
1747 * tree, whose slot is next to claim among the
1748 * allocated slots in obj, where scope->table
1749 * has not been created yet.
1750 *
1751 * We may want to remove hazard conditions
1752 * above and inline compensation code here,
1753 * depending on real-world workloads.
1754 */
1755 JS_ASSERT(!(obj->getClass()->flags &
1756 JSCLASS_SHARE_ALL_PROPERTIES));
1757
1758 PCMETER(cache->pchits++);
1759 PCMETER(cache->addpchits++);
1760
1761 /*
1762 * Beware classes such as Function that use
1763 * the reserveSlots hook to allocate a number
1764 * of reserved slots that may vary with obj.
1765 */
1766 if (slot < STOBJ_NSLOTS(obj) &&
1767 !OBJ_GET_CLASS(cx, obj)->reserveSlots) {
1768 ++scope->freeslot;
1769 } else {
1770 if (!js_AllocSlot(cx, obj, &slot)) {
1771 JS_UNLOCK_SCOPE(cx, scope);
1772 goto error;
1773 }
1774 }
1775
1776 /*
1777 * If this obj's number of reserved slots
1778 * differed, or if something created a hash
1779 * table for scope, we must pay the price of
1780 * JSScope::add.
1781 *
1782 * If slot does not match the cached sprop's
1783 * slot, update the cache entry in the hope
1784 * that obj and other instances with the same
1785 * number of reserved slots are now "hot".
1786 */
1787 if (slot != sprop->slot || scope->table) {
1788 JSScopeProperty *sprop2 =
1789 scope->add(cx, sprop->id,
1790 sprop->getter, sprop->setter,
1791 slot, sprop->attrs,
1792 sprop->flags, sprop->shortid);
1793 if (!sprop2) {
1794 js_FreeSlot(cx, obj, slot);
1795 JS_UNLOCK_SCOPE(cx, scope);
1796 goto error;
1797 }
1798 if (sprop2 != sprop) {
1799 PCMETER(cache->slotchanges++);
1800 JS_ASSERT(slot != sprop->slot &&
1801 slot == sprop2->slot &&
1802 sprop2->id == sprop->id);
1803 entry->vword = SPROP_TO_PCVAL(sprop2);
1804 }
1805 sprop = sprop2;
1806 } else {
1807 scope->extend(cx, sprop);
1808 }
1809
1810 LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, rval);
1811 TRACE_2(SetPropHit, entry, sprop);
1812 LOCKED_OBJ_SET_SLOT(obj, slot, rval);
1813 JS_UNLOCK_SCOPE(cx, scope);
1814
1815 /*
1816 * Purge the property cache of the id we may
1817 * have just shadowed in obj's scope and proto
1818 * chains. We do this after unlocking obj's
1819 * scope to avoid lock nesting.
1820 */
1821 js_PurgeScopeChain(cx, obj, sprop->id);
1822 break;
1823 }
1824 JS_UNLOCK_SCOPE(cx, scope);
1825 PCMETER(cache->setpcmisses++);
1826 }
1827 }
1828
1829 atom = js_FullTestPropertyCache(cx, regs.pc, &obj, &obj2,
1830 &entry);
1831 if (atom) {
1832 PCMETER(cache->misses++);
1833 PCMETER(cache->setmisses++);
1834 } else {
1835 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
1836 sprop = NULL;
1837 if (obj == obj2) {
1838 JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
1839 sprop = PCVAL_TO_SPROP(entry->vword);
1840 JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
1841 JS_ASSERT(!OBJ_SCOPE(obj2)->sealed());
1842 NATIVE_SET(cx, obj, sprop, entry, &rval);
1843 }
1844 JS_UNLOCK_OBJ(cx, obj2);
1845 if (sprop)
1846 break;
1847 }
1848 }
1849
1850 if (!atom)
1851 LOAD_ATOM(0);
1852 id = ATOM_TO_JSID(atom);
1853 if (entry) {
1854 if (!js_SetPropertyHelper(cx, obj, id, true, &rval))
1855 goto error;
1856 } else {
1857 if (!obj->setProperty(cx, id, &rval))
1858 goto error;
1859 ABORT_RECORDING(cx, "Non-native set");
1860 }
1861 } while (0);
1862 END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2);
1863
1864 BEGIN_CASE(JSOP_GETELEM)
1865 /* Open-coded ELEMENT_OP optimized for strings and dense arrays. */
1866 lval = FETCH_OPND(-2);
1867 rval = FETCH_OPND(-1);
1868 if (JSVAL_IS_STRING(lval) && JSVAL_IS_INT(rval)) {
1869 str = JSVAL_TO_STRING(lval);
1870 i = JSVAL_TO_INT(rval);
1871 if ((size_t)i < str->length()) {
1872 str = JSString::getUnitString(cx, str, size_t(i));
1873 if (!str)
1874 goto error;
1875 rval = STRING_TO_JSVAL(str);
1876 goto end_getelem;
1877 }
1878 }
1879
1880 VALUE_TO_OBJECT(cx, -2, lval, obj);
1881 if (JSVAL_IS_INT(rval)) {
1882 if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
1883 jsuint length;
1884
1885 length = js_DenseArrayCapacity(obj);
1886 i = JSVAL_TO_INT(rval);
1887 if ((jsuint)i < length &&
1888 i < obj->fslots[JSSLOT_ARRAY_LENGTH]) {
1889 rval = obj->dslots[i];
1890 if (rval != JSVAL_HOLE)
1891 goto end_getelem;
1892
1893 /* Reload rval from the stack in the rare hole case. */
1894 rval = FETCH_OPND(-1);
1895 }
1896 }
1897 id = INT_JSVAL_TO_JSID(rval);
1898 } else {
1899 if (!js_InternNonIntElementId(cx, obj, rval, &id))
1900 goto error;
1901 }
1902
1903 if (!obj->getProperty(cx, id, &rval))
1904 goto error;
1905 end_getelem:
1906 regs.sp--;
1907 STORE_OPND(-1, rval);
1908 END_CASE(JSOP_GETELEM)
1909
1910 BEGIN_CASE(JSOP_CALLELEM)
1911 ELEMENT_OP(-1, js_GetMethod(cx, obj, id, false, &rval));
1912 #if JS_HAS_NO_SUCH_METHOD
1913 if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) {
1914 regs.sp[-2] = regs.sp[-1];
1915 regs.sp[-1] = OBJECT_TO_JSVAL(obj);
1916 if (!js_OnUnknownMethod(cx, regs.sp - 2))
1917 goto error;
1918 } else
1919 #endif
1920 {
1921 STORE_OPND(-2, rval);
1922 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
1923 }
1924 END_CASE(JSOP_CALLELEM)
1925
1926 BEGIN_CASE(JSOP_SETELEM)
1927 rval = FETCH_OPND(-1);
1928 FETCH_OBJECT(cx, -3, lval, obj);
1929 FETCH_ELEMENT_ID(obj, -2, id);
1930 do {
1931 if (OBJ_IS_DENSE_ARRAY(cx, obj) && JSID_IS_INT(id)) {
1932 jsuint length;
1933
1934 length = js_DenseArrayCapacity(obj);
1935 i = JSID_TO_INT(id);
1936 if ((jsuint)i < length) {
1937 if (obj->dslots[i] == JSVAL_HOLE) {
1938 if (js_PrototypeHasIndexedProperties(cx, obj))
1939 break;
1940 if (i >= obj->fslots[JSSLOT_ARRAY_LENGTH])
1941 obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1;
1942 obj->fslots[JSSLOT_ARRAY_COUNT]++;
1943 }
1944 obj->dslots[i] = rval;
1945 goto end_setelem;
1946 }
1947 }
1948 } while (0);
1949 if (!obj->setProperty(cx, id, &rval))
1950 goto error;
1951 end_setelem:
1952 END_SET_CASE_STORE_RVAL(JSOP_SETELEM, 3)
1953
1954 BEGIN_CASE(JSOP_ENUMELEM)
1955 /* Funky: the value to set is under the [obj, id] pair. */
1956 rval = FETCH_OPND(-3);
1957 FETCH_OBJECT(cx, -2, lval, obj);
1958 FETCH_ELEMENT_ID(obj, -1, id);
1959 if (!obj->setProperty(cx, id, &rval))
1960 goto error;
1961 regs.sp -= 3;
1962 END_CASE(JSOP_ENUMELEM)
1963
1964 BEGIN_CASE(JSOP_NEW)
1965 /* Get immediate argc and find the constructor function. */
1966 argc = GET_ARGC(regs.pc);
1967 vp = regs.sp - (2 + argc);
1968 JS_ASSERT(vp >= StackBase(fp));
1969
1970 /*
1971 * Assign lval, obj, and fun exactly as the code at inline_call:
1972 * expects to find them, to avoid nesting a js_Interpret call via
1973 * js_InvokeConstructor.
1974 */
1975 lval = *vp;
1976 if (VALUE_IS_FUNCTION(cx, lval)) {
1977 obj = JSVAL_TO_OBJECT(lval);
1978 fun = GET_FUNCTION_PRIVATE(cx, obj);
1979 if (FUN_INTERPRETED(fun)) {
1980 /* Root as we go using vp[1]. */
1981 if (!obj->getProperty(cx,
1982 ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom),
1983 &vp[1])) {
1984 goto error;
1985 }
1986 rval = vp[1];
1987 obj2 = js_NewObject(cx, &js_ObjectClass,
1988 JSVAL_IS_OBJECT(rval)
1989 ? JSVAL_TO_OBJECT(rval)
1990 : NULL,
1991 OBJ_GET_PARENT(cx, obj));
1992 if (!obj2)
1993 goto error;
1994 vp[1] = OBJECT_TO_JSVAL(obj2);
1995 flags = JSFRAME_CONSTRUCTING;
1996 goto inline_call;
1997 }
1998 }
1999
2000 if (!js_InvokeConstructor(cx, argc, JS_FALSE, vp))
2001 goto error;
2002 regs.sp = vp + 1;
2003 CHECK_INTERRUPT_HANDLER();
2004 TRACE_0(NativeCallComplete);
2005 END_CASE(JSOP_NEW)
2006
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);
2012
2013 lval = *vp;
2014 if (VALUE_IS_FUNCTION(cx, lval)) {
2015 obj = JSVAL_TO_OBJECT(lval);
2016 fun = GET_FUNCTION_PRIVATE(cx, obj);
2017
2018 /* Clear frame flags since this is not a constructor call. */
2019 flags = 0;
2020 if (FUN_INTERPRETED(fun))
2021 inline_call:
2022 {
2023 uintN nframeslots, nvars, missing;
2024 JSArena *a;
2025 jsuword nbytes;
2026 void *newmark;
2027 jsval *newsp;
2028 JSInlineFrame *newifp;
2029 JSInterpreterHook hook;
2030
2031 /* Restrict recursion of lightweight functions. */
2032 if (inlineCallCount == MAX_INLINE_CALL_COUNT) {
2033 js_ReportOverRecursed(cx);
2034 goto error;
2035 }
2036
2037 /* Compute the total number of stack slots needed by fun. */
2038 nframeslots = JS_HOWMANY(sizeof(JSInlineFrame),
2039 sizeof(jsval));
2040 script = fun->u.i.script;
2041 atoms = script->atomMap.vector;
2042 nbytes = (nframeslots + script->nslots) * sizeof(jsval);
2043
2044 /* Allocate missing expected args adjacent to actuals. */
2045 a = cx->stackPool.current;
2046 newmark = (void *) a->avail;
2047 if (fun->nargs <= argc) {
2048 missing = 0;
2049 } else {
2050 newsp = vp + 2 + fun->nargs;
2051 JS_ASSERT(newsp > regs.sp);
2052 if ((jsuword) newsp <= a->limit) {
2053 if ((jsuword) newsp > a->avail)
2054 a->avail = (jsuword) newsp;
2055 jsval *argsp = newsp;
2056 do {
2057 *--argsp = JSVAL_VOID;
2058 } while (argsp != regs.sp);
2059 missing = 0;
2060 } else {
2061 missing = fun->nargs - argc;
2062 nbytes += (2 + fun->nargs) * sizeof(jsval);
2063 }
2064 }
2065
2066 /* Allocate the inline frame with its slots and operands. */
2067 if (a->avail + nbytes <= a->limit) {
2068 newsp = (jsval *) a->avail;
2069 a->avail += nbytes;
2070 JS_ASSERT(missing == 0);
2071 } else {
2072 JS_ARENA_ALLOCATE_CAST(newsp, jsval *, &cx->stackPool,
2073 nbytes);
2074 if (!newsp) {
2075 js_ReportOutOfScriptQuota(cx);
2076 goto bad_inline_call;
2077 }
2078
2079 /*
2080 * Move args if the missing ones overflow arena a, then
2081 * push undefined for the missing args.
2082 */
2083 if (missing) {
2084 memcpy(newsp, vp, (2 + argc) * sizeof(jsval));
2085 vp = newsp;
2086 newsp = vp + 2 + argc;
2087 do {
2088 *newsp++ = JSVAL_VOID;
2089 } while (--missing != 0);
2090 }
2091 }
2092
2093 /* Claim space for the stack frame and initialize it. */
2094 newifp = (JSInlineFrame *) newsp;
2095 newsp += nframeslots;
2096 newifp->frame.callobj = NULL;
2097 newifp->frame.argsobj = NULL;
2098 newifp->frame.varobj = NULL;
2099 newifp->frame.script = script;
2100 newifp->frame.fun = fun;
2101 newifp->frame.argc = argc;
2102 newifp->frame.argv = vp + 2;
2103 newifp->frame.rval = JSVAL_VOID;
2104 newifp->frame.down = fp;
2105 newifp->frame.annotation = NULL;
2106 newifp->frame.scopeChain = parent = OBJ_GET_PARENT(cx, obj);
2107 newifp->frame.sharpDepth = 0;
2108 newifp->frame.sharpArray = NULL;
2109 newifp->frame.flags = flags;
2110 newifp->frame.dormantNext = NULL;
2111 newifp->frame.blockChain = NULL;
2112 if (script->staticLevel < JS_DISPLAY_SIZE) {
2113 JSStackFrame **disp = &cx->display[script->staticLevel];
2114 newifp->frame.displaySave = *disp;
2115 *disp = &newifp->frame;
2116 }
2117 newifp->mark = newmark;
2118
2119 /* Compute the 'this' parameter now that argv is set. */
2120 JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags));
2121 JS_ASSERT(JSVAL_IS_OBJECT(vp[1]));
2122 newifp->frame.thisp = (JSObject *)vp[1];
2123
2124 newifp->frame.regs = NULL;
2125 newifp->frame.imacpc = NULL;
2126 newifp->frame.slots = newsp;
2127
2128 /* Push void to initialize local variables. */
2129 nvars = fun->u.i.nvars;
2130 while (nvars--)
2131 *newsp++ = JSVAL_VOID;
2132
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;
2137 }
2138
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);
2145 }
2146
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;
2154
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;
2163 }
2164
2165 TRACE_0(EnterFrame);
2166
2167 inlineCallCount++;
2168 JS_RUNTIME_METER(rt, inlineCalls);
2169
2170 #ifdef INCLUDE_MOZILLA_DTRACE
2171 /* DTrace function entry, inlines */
2172 if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
2173 jsdtrace_function_entry(cx, fp, fun);
2174 if (JAVASCRIPT_FUNCTION_INFO_ENABLED())
2175 jsdtrace_function_info(cx, fp, fp->down, fun);
2176 if (JAVASCRIPT_FUNCTION_ARGS_ENABLED())
2177 jsdtrace_function_args(cx, fp, fun, fp->argc, fp->argv);
2178 #endif
2179
2180 /* Load first op and dispatch it (safe since JSOP_STOP). */
2181 op = (JSOp) *regs.pc;
2182 DO_OP();
2183
2184 bad_inline_call:
2185 JS_ASSERT(fp->regs == &regs);
2186 script = fp->script;
2187 atoms = script->atomMap.vector;
2188 js_FreeRawStack(cx, newmark);
2189 goto error;
2190 }
2191
2192 if (fun->flags & JSFUN_FAST_NATIVE) {
2193 #ifdef INCLUDE_MOZILLA_DTRACE
2194 /* DTrace function entry, non-inlines */
2195 if (VALUE_IS_FUNCTION(cx, lval)) {
2196 if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
2197 jsdtrace_function_entry(cx, NULL, fun);
2198 if (JAVASCRIPT_FUNCTION_INFO_ENABLED())
2199 jsdtrace_function_info(cx, NULL, fp, fun);
2200 if (JAVASCRIPT_FUNCTION_ARGS_ENABLED())
2201 jsdtrace_function_args(cx, fp, fun, argc, vp+2);
2202 }
2203 #endif
2204
2205 JS_ASSERT(fun->u.n.extra == 0);
2206 JS_ASSERT(JSVAL_IS_OBJECT(vp[1]) ||
2207 PRIMITIVE_THIS_TEST(fun, vp[1]));
2208 ok = ((JSFastNative) fun->u.n.native)(cx, argc, vp);
2209 #ifdef INCLUDE_MOZILLA_DTRACE
2210 if (VALUE_IS_FUNCTION(cx, lval)) {
2211 if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
2212 jsdtrace_function_rval(cx, NULL, fun, vp);
2213 if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
2214 jsdtrace_function_return(cx, NULL, fun);
2215 }
2216 #endif
2217 regs.sp = vp + 1;
2218 if (!ok) {
2219 /*
2220 * If we are executing the JSOP_NEXTITER imacro and a Stopiteration
2221 * exception is raised, transform it into a JSVAL_HOLE return value.
2222 * The tracer generates equivalent code by calling CatchStopIteration_tn.
2223 */
2224 if (fp->imacpc && *fp->imacpc == JSOP_NEXTITER &&
2225 cx->throwing && js_ValueIsStopIteration(cx->exception)) {
2226 // pc may point to JSOP_DUP here due to bug 474854.
2227 JS_ASSERT(*regs.pc == JSOP_CALL || *regs.pc == JSOP_DUP);
2228 cx->throwing = JS_FALSE;
2229 cx->exception = JSVAL_VOID;
2230 regs.sp[-1] = JSVAL_HOLE;
2231 } else {
2232 goto error;
2233 }
2234 }
2235 TRACE_0(NativeCallComplete);
2236 goto end_call;
2237 }
2238 }
2239
2240 ok = js_Invoke(cx, argc, vp, 0);
2241 regs.sp = vp + 1;
2242 CHECK_INTERRUPT_HANDLER();
2243 if (!ok)
2244 goto error;
2245 JS_RUNTIME_METER(rt, nonInlineCalls);
2246 TRACE_0(NativeCallComplete);
2247
2248 end_call:
2249 #if JS_HAS_LVALUE_RETURN
2250 if (cx->rval2set) {
2251 /*
2252 * Use the stack depth we didn't claim in our budget, but that
2253 * we know is there on account of [fun, this] already having
2254 * been pushed, at a minimum (if no args). Those two slots
2255 * have been popped and [rval] has been pushed, which leaves
2256 * one more slot for rval2 before we might overflow.
2257 *
2258 * NB: rval2 must be the property identifier, and rval the
2259 * object from which to get the property. The pair form an
2260 * ECMA "reference type", which can be used on the right- or
2261 * left-hand side of assignment ops. Note well: only native
2262 * methods can return reference types. See JSOP_SETCALL just
2263 * below for the left-hand-side case.
2264 */
2265 PUSH_OPND(cx->rval2);
2266 ELEMENT_OP(-1, obj->getProperty(cx, id, &rval));
2267
2268 regs.sp--;
2269 STORE_OPND(-1, rval);
2270 cx->rval2set = JS_FALSE;
2271 }
2272 #endif /* JS_HAS_LVALUE_RETURN */
2273 END_CASE(JSOP_CALL)
2274
2275 #if JS_HAS_LVALUE_RETURN
2276 BEGIN_CASE(JSOP_SETCALL)
2277 argc = GET_ARGC(regs.pc);
2278 vp = regs.sp - argc - 2;
2279 ok = js_Invoke(cx, argc, vp, 0);
2280 regs.sp = vp + 1;
2281 CHECK_INTERRUPT_HANDLER();
2282 if (!ok)
2283 goto error;
2284 if (!cx->rval2set) {
2285 op2 = js_GetOpcode(cx, script, regs.pc + JSOP_SETCALL_LENGTH);
2286 if (op2 != JSOP_DELELEM) {
2287 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2288 JSMSG_BAD_LEFTSIDE_OF_ASS);
2289 goto error;
2290 }
2291
2292 /*
2293 * Store true as the result of the emulated delete of a
2294 * non-existent property. NB: We don't METER_OP_PAIR here;
2295 * it doesn't seem worth the code for this obscure case.
2296 */
2297 *vp = JSVAL_TRUE;
2298 regs.pc += JSOP_SETCALL_LENGTH + JSOP_DELELEM_LENGTH;
2299 op = (JSOp) *regs.pc;
2300 DO_OP();
2301 }
2302 PUSH_OPND(cx->rval2);
2303 cx->rval2set = JS_FALSE;
2304 END_CASE(JSOP_SETCALL)
2305 #endif
2306
2307 BEGIN_CASE(JSOP_NAME)
2308 BEGIN_CASE(JSOP_CALLNAME)
2309 {
2310 JSPropCacheEntry *entry;
2311
2312 obj = fp->scopeChain;
2313 if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
2314 PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);
2315 if (!atom) {
2316 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
2317 if (PCVAL_IS_OBJECT(entry->vword)) {
2318 rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);
2319 JS_UNLOCK_OBJ(cx, obj2);
2320 goto do_push_rval;
2321 }
2322
2323 if (PCVAL_IS_SLOT(entry->vword)) {
2324 slot = PCVAL_TO_SLOT(entry->vword);
2325 JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
2326 rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
2327 JS_UNLOCK_OBJ(cx, obj2);
2328 goto do_push_rval;
2329 }
2330
2331 JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
2332 sprop = PCVAL_TO_SPROP(entry->vword);
2333 goto do_native_get;
2334 }
2335 } else {
2336 LOAD_ATOM(0);
2337 }
2338
2339 id = ATOM_TO_JSID(atom);
2340 if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
2341 goto error;
2342 if (!prop) {
2343 /* Kludge to allow (typeof foo == "undefined") tests. */
2344 endpc = script->code + script->length;
2345 op2 = js_GetOpcode(cx, script, regs.pc + JSOP_NAME_LENGTH);
2346 if (op2 == JSOP_TYPEOF) {
2347 PUSH_OPND(JSVAL_VOID);
2348 len = JSOP_NAME_LENGTH;
2349 DO_NEXT_OP(len);
2350 }
2351 goto atom_not_defined;
2352 }
2353
2354 /* Take the slow path if prop was not found in a native object. */
2355 if (!OBJ_IS_NATIVE(obj) || !OBJ_IS_NATIVE(obj2)) {
2356 obj2->dropProperty(cx, prop);
2357 if (!obj->getProperty(cx, id, &rval))
2358 goto error;
2359 } else {
2360 sprop = (JSScopeProperty *)prop;
2361 do_native_get:
2362 NATIVE_GET(cx, obj, obj2, sprop, &rval);
2363 obj2->dropProperty(cx, (JSProperty *) sprop);
2364 }
2365
2366 do_push_rval:
2367 PUSH_OPND(rval);
2368 if (op == JSOP_CALLNAME)
2369 PUSH_OPND(OBJECT_TO_JSVAL(obj));
2370 }
2371 END_CASE(JSOP_NAME)
2372
2373 BEGIN_CASE(JSOP_UINT16)
2374 i = (jsint) GET_UINT16(regs.pc);
2375 rval = INT_TO_JSVAL(i);
2376 PUSH_OPND(rval);
2377 END_CASE(JSOP_UINT16)
2378
2379 BEGIN_CASE(JSOP_UINT24)
2380 i = (jsint) GET_UINT24(regs.pc);
2381 rval = INT_TO_JSVAL(i);
2382 PUSH_OPND(rval);
2383 END_CASE(JSOP_UINT24)
2384
2385 BEGIN_CASE(JSOP_INT8)
2386 i = GET_INT8(regs.pc);
2387 rval = INT_TO_JSVAL(i);
2388 PUSH_OPND(rval);
2389 END_CASE(JSOP_INT8)
2390
2391 BEGIN_CASE(JSOP_INT32)
2392 i = GET_INT32(regs.pc);
2393 rval = INT_TO_JSVAL(i);
2394 PUSH_OPND(rval);
2395 END_CASE(JSOP_INT32)
2396
2397 BEGIN_CASE(JSOP_INDEXBASE)
2398 /*
2399 * Here atoms can exceed script->atomMap.length as we use atoms
2400 * as a segment register for object literals as well.
2401 */
2402 atoms += GET_INDEXBASE(regs.pc);
2403 END_CASE(JSOP_INDEXBASE)
2404
2405 BEGIN_CASE(JSOP_INDEXBASE1)
2406 BEGIN_CASE(JSOP_INDEXBASE2)
2407 BEGIN_CASE(JSOP_INDEXBASE3)
2408 atoms += (op - JSOP_INDEXBASE1 + 1) << 16;
2409 END_CASE(JSOP_INDEXBASE3)
2410
2411 BEGIN_CASE(JSOP_RESETBASE0)
2412 BEGIN_CASE(JSOP_RESETBASE)
2413 atoms = script->atomMap.vector;
2414 END_CASE(JSOP_RESETBASE)
2415
2416 BEGIN_CASE(JSOP_DOUBLE)
2417 JS_ASSERT(!fp->imacpc);
2418 JS_ASSERT(size_t(atoms - script->atomMap.vector) < script->atomMap.length);
2419 /* FALL THROUGH */
2420
2421 BEGIN_CASE(JSOP_STRING)
2422 LOAD_ATOM(0);
2423 PUSH_OPND(ATOM_KEY(atom));
2424 END_CASE(JSOP_DOUBLE)
2425
2426 BEGIN_CASE(JSOP_OBJECT)
2427 LOAD_OBJECT(0);
2428 PUSH_OPND(OBJECT_TO_JSVAL(obj));
2429 END_CASE(JSOP_OBJECT)
2430
2431 BEGIN_CASE(JSOP_REGEXP)
2432 {
2433 JSObject *funobj;
2434
2435 /*
2436 * Push a regexp object for the atom mapped by the bytecode at pc,
2437 * cloning the literal's regexp object if necessary, to simulate in
2438 * the pre-compile/execute-later case what ECMA specifies for the
2439 * compile-and-go case: that scanning each regexp literal creates
2440 * a single corresponding RegExp object.
2441 *
2442 * To support pre-compilation transparently, we must handle the
2443 * case where a regexp object literal is used in a different global
2444 * at execution time from the global with which it was scanned at
2445 * compile time. We do this by re-wrapping the JSRegExp private
2446 * data struct with a cloned object having the right prototype and
2447 * parent, and having its own lastIndex property value storage.
2448 *
2449 * Unlike JSOP_DEFFUN and other prolog bytecodes that may clone
2450 * literal objects, we don't want to pay a script prolog execution
2451 * price for all regexp literals in a script (many may not be used
2452 * by a particular execution of that script, depending on control
2453 * flow), so we initialize lazily here.
2454 *
2455 * XXX This code is specific to regular expression objects. If we
2456 * need a similar op for other kinds of object literals, we should
2457 * push cloning down under JSObjectOps and reuse code here.
2458 */
2459 index = GET_FULL_INDEX(0);
2460 JS_ASSERT(index < script->regexps()->length);
2461
2462 slot = index;
2463 if (fp->fun) {
2464 /*
2465 * We're in function code, not global or eval code (in eval
2466 * code, JSOP_REGEXP is never emitted). The cloned funobj
2467 * contains script->regexps()->length reserved slots
2468 * for the cloned regexps; see fun_reserveSlots, jsfun.c.
2469 */
2470 funobj = JSVAL_TO_OBJECT(fp->argv[-2]);
2471 slot += JSCLASS_RESERVED_SLOTS(&js_FunctionClass);
2472 if (script->upvarsOffset != 0)
2473 slot += script->upvars()->length;
2474 if (!JS_GetReservedSlot(cx, funobj, slot, &rval))
2475 goto error;
2476 if (JSVAL_IS_VOID(rval))
2477 rval = JSVAL_NULL;
2478 } else {
2479 /*
2480 * We're in global code. The code generator reserved a slot
2481 * for the regexp among script->nfixed slots. All such slots
2482 * are initialized to null, not void, for faster testing in
2483 * JSOP_*GVAR cases. To simplify index calculations we count
2484 * regexps in the reverse order down from script->nslots - 1.
2485 */
2486 JS_ASSERT(slot < script->nfixed);
2487 slot = script->nfixed - slot - 1;
2488 rval = fp->slots[slot];
2489 #ifdef __GNUC__
2490 funobj = NULL; /* suppress bogus gcc warnings */
2491 #endif
2492 }
2493
2494 if (JSVAL_IS_NULL(rval)) {
2495 /* Compute the current global object in obj2. */
2496 obj2 = fp->scopeChain;
2497 while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL)
2498 obj2 = parent;
2499
2500 /*
2501 * If obj's parent is not obj2, we must clone obj so that it
2502 * has the right parent, and therefore, the right prototype.
2503 *
2504 * Yes, this means we assume that the correct RegExp.prototype
2505 * to which regexp instances (including literals) delegate can
2506 * be distinguished solely by the instance's parent, which was
2507 * set to the parent of the RegExp constructor function object
2508 * when the instance was created. In other words,
2509 *
2510 * (/x/.__parent__ == RegExp.__parent__) implies
2511 * (/x/.__proto__ == RegExp.prototype)
2512 *
2513 * (unless you assign a different object to RegExp.prototype
2514 * at runtime, in which case, ECMA doesn't specify operation,
2515 * and you get what you deserve).
2516 *
2517 * This same coupling between instance parent and constructor
2518 * parent turns up everywhere (see jsobj.c's FindClassObject,
2519 * js_ConstructObject, and js_NewObject). It's fundamental to
2520 * the design of the language when you consider multiple global
2521 * objects and separate compilation and execution, even though
2522 * it is not specified fully in ECMA.
2523 */
2524 obj = script->getRegExp(index);
2525 if (OBJ_GET_PARENT(cx, obj) != obj2) {
2526 obj = js_CloneRegExpObject(cx, obj, obj2);
2527 if (!obj)
2528 goto error;
2529 }
2530 rval = OBJECT_TO_JSVAL(obj);
2531
2532 /* Store the regexp object value in its cloneIndex slot. */
2533 if (fp->fun) {
2534 if (!JS_SetReservedSlot(cx, funobj, slot, rval))
2535 goto error;
2536 } else {
2537 fp->slots[slot] = rval;
2538 }
2539 }
2540
2541 PUSH_OPND(rval);
2542 }
2543 END_CASE(JSOP_REGEXP)
2544
2545 BEGIN_CASE(JSOP_ZERO)
2546 PUSH_OPND(JSVAL_ZERO);
2547 END_CASE(JSOP_ZERO)
2548
2549 BEGIN_CASE(JSOP_ONE)
2550 PUSH_OPND(JSVAL_ONE);
2551 END_CASE(JSOP_ONE)
2552
2553 BEGIN_CASE(JSOP_NULL)
2554 PUSH_OPND(JSVAL_NULL);
2555 END_CASE(JSOP_NULL)
2556
2557 BEGIN_CASE(JSOP_FALSE)
2558 PUSH_OPND(JSVAL_FALSE);
2559 END_CASE(JSOP_FALSE)
2560
2561 BEGIN_CASE(JSOP_TRUE)
2562 PUSH_OPND(JSVAL_TRUE);
2563 END_CASE(JSOP_TRUE)
2564
2565 BEGIN_CASE(JSOP_TABLESWITCH)
2566 pc2 = regs.pc;
2567 len = GET_JUMP_OFFSET(pc2);
2568
2569 /*
2570 * ECMAv2+ forbids conversion of discriminant, so we will skip to
2571 * the default case if the discriminant isn't already an int jsval.
2572 * (This opcode is emitted only for dense jsint-domain switches.)
2573 */
2574 rval = POP_OPND();
2575 if (JSVAL_IS_INT(rval)) {
2576 i = JSVAL_TO_INT(rval);
2577 } else if (JSVAL_IS_DOUBLE(rval) && *JSVAL_TO_DOUBLE(rval) == 0) {
2578 /* Treat -0 (double) as 0. */
2579 i = 0;
2580 } else {
2581 DO_NEXT_OP(len);
2582 }
2583
2584 pc2 += JUMP_OFFSET_LEN;
2585 low = GET_JUMP_OFFSET(pc2);
2586 pc2 += JUMP_OFFSET_LEN;
2587 high = GET_JUMP_OFFSET(pc2);
2588
2589 i -= low;
2590 if ((jsuint)i < (jsuint)(high - low + 1)) {
2591 pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i;
2592 off = (jsint) GET_JUMP_OFFSET(pc2);
2593 if (off)
2594 len = off;
2595 }
2596 END_VARLEN_CASE
2597
2598 BEGIN_CASE(JSOP_TABLESWITCHX)
2599 pc2 = regs.pc;
2600 len = GET_JUMPX_OFFSET(pc2);
2601
2602 /*
2603 * ECMAv2+ forbids conversion of discriminant, so we will skip to
2604 * the default case if the discriminant isn't already an int jsval.
2605 * (This opcode is emitted only for dense jsint-domain switches.)
2606 */
2607 rval = POP_OPND();
2608 if (JSVAL_IS_INT(rval)) {
2609 i = JSVAL_TO_INT(rval);
2610 } else if (JSVAL_IS_DOUBLE(rval) && *JSVAL_TO_DOUBLE(rval) == 0) {
2611 /* Treat -0 (double) as 0. */
2612 i = 0;
2613 } else {
2614 DO_NEXT_OP(len);
2615 }
2616
2617 pc2 += JUMPX_OFFSET_LEN;
2618 low = GET_JUMP_OFFSET(pc2);
2619 pc2 += JUMP_OFFSET_LEN;
2620 high = GET_JUMP_OFFSET(pc2);
2621
2622 i -= low;
2623 if ((jsuint)i < (jsuint)(high - low + 1)) {
2624 pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i;
2625 off = (jsint) GET_JUMPX_OFFSET(pc2);
2626 if (off)
2627 len = off;
2628 }
2629 END_VARLEN_CASE
2630
2631 BEGIN_CASE(JSOP_LOOKUPSWITCHX)
2632 off = JUMPX_OFFSET_LEN;
2633 goto do_lookup_switch;
2634
2635 BEGIN_CASE(JSOP_LOOKUPSWITCH)
2636 off = JUMP_OFFSET_LEN;
2637
2638 do_lookup_switch:
2639 /*
2640 * JSOP_LOOKUPSWITCH and JSOP_LOOKUPSWITCHX are never used if
2641 * any atom index in it would exceed 64K limit.
2642 */
2643 JS_ASSERT(!fp->imacpc);
2644 JS_ASSERT(atoms == script->atomMap.vector);
2645 pc2 = regs.pc;
2646 lval = POP_OPND();
2647
2648 if (!JSVAL_IS_NUMBER(lval) &&
2649 !JSVAL_IS_STRING(lval) &&
2650 !JSVAL_IS_BOOLEAN(lval)) {
2651 goto end_lookup_switch;
2652 }
2653
2654 pc2 += off;
2655 npairs = (jsint) GET_UINT16(pc2);
2656 pc2 += UINT16_LEN;
2657 JS_ASSERT(npairs); /* empty switch uses JSOP_TABLESWITCH */
2658
2659 #define SEARCH_PAIRS(MATCH_CODE) \
2660 for (;;) { \
2661 JS_ASSERT(GET_INDEX(pc2) < script->atomMap.length); \
2662 atom = atoms[GET_INDEX(pc2)]; \
2663 rval = ATOM_KEY(atom); \
2664 MATCH_CODE \
2665 pc2 += INDEX_LEN; \
2666 if (match) \
2667 break; \
2668 pc2 += off; \
2669 if (--npairs == 0) { \
2670 pc2 = regs.pc; \
2671 break; \
2672 } \
2673 }
2674 if (JSVAL_IS_STRING(lval)) {
2675 str = JSVAL_TO_STRING(lval);
2676 SEARCH_PAIRS(
2677 match = (JSVAL_IS_STRING(rval) &&
2678 ((str2 = JSVAL_TO_STRING(rval)) == str ||
2679 js_EqualStrings(str2, str)));
2680 )
2681 } else if (JSVAL_IS_DOUBLE(lval)) {
2682 d = *JSVAL_TO_DOUBLE(lval);
2683 SEARCH_PAIRS(
2684 match = (JSVAL_IS_DOUBLE(rval) &&
2685 *JSVAL_TO_DOUBLE(rval) == d);
2686 )
2687 } else {
2688 SEARCH_PAIRS(
2689 match = (lval == rval);
2690 )
2691 }
2692 #undef SEARCH_PAIRS
2693
2694 end_lookup_switch:
2695 len = (op == JSOP_LOOKUPSWITCH)
2696 ? GET_JUMP_OFFSET(pc2)
2697 : GET_JUMPX_OFFSET(pc2);
2698 END_VARLEN_CASE
2699
2700 BEGIN_CASE(JSOP_TRAP)
2701 {
2702 JSTrapStatus status;
2703
2704 status = JS_HandleTrap(cx, script, regs.pc, &rval);
2705 switch (status) {
2706 case JSTRAP_ERROR:
2707 goto error;
2708 case JSTRAP_RETURN:
2709 fp->rval = rval;
2710 ok = JS_TRUE;
2711 goto forced_return;
2712 case JSTRAP_THROW:
2713 cx->throwing = JS_TRUE;
2714 cx->exception = rval;
2715 goto error;
2716 default:;
2717 break;
2718 }
2719 JS_ASSERT(status == JSTRAP_CONTINUE);
2720 CHECK_INTERRUPT_HANDLER();
2721 JS_ASSERT(JSVAL_IS_INT(rval));
2722 op = (JSOp) JSVAL_TO_INT(rval);
2723 JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
2724 DO_OP();
2725 }
2726
2727 BEGIN_CASE(JSOP_ARGUMENTS)
2728 if (!js_GetArgsValue(cx, fp, &rval))
2729 goto error;
2730 PUSH_OPND(rval);
2731 END_CASE(JSOP_ARGUMENTS)
2732
2733 BEGIN_CASE(JSOP_ARGSUB)
2734 id = INT_TO_JSID(GET_ARGNO(regs.pc));
2735 if (!js_GetArgsProperty(cx, fp, id, &rval))
2736 goto error;
2737 PUSH_OPND(rval);
2738 END_CASE(JSOP_ARGSUB)
2739
2740 BEGIN_CASE(JSOP_ARGCNT)
2741 id = ATOM_TO_JSID(rt->atomState.lengthAtom);
2742 if (!js_GetArgsProperty(cx, fp, id, &rval))
2743 goto error;
2744 PUSH_OPND(rval);
2745 END_CASE(JSOP_ARGCNT)
2746
2747 BEGIN_CASE(JSOP_GETARG)
2748 BEGIN_CASE(JSOP_CALLARG)
2749 slot = GET_ARGNO(regs.pc);
2750 JS_ASSERT(slot < fp->fun->nargs);
2751 METER_SLOT_OP(op, slot);
2752 PUSH_OPND(fp->argv[slot]);
2753 if (op == JSOP_CALLARG)
2754 PUSH_OPND(JSVAL_NULL);
2755 END_CASE(JSOP_GETARG)
2756
2757 BEGIN_CASE(JSOP_SETARG)
2758 slot = GET_ARGNO(regs.pc);
2759 JS_ASSERT(slot < fp->fun->nargs);
2760 METER_SLOT_OP(op, slot);
2761 vp = &fp->argv[slot];
2762 *vp = FETCH_OPND(-1);
2763 END_SET_CASE(JSOP_SETARG)
2764
2765 BEGIN_CASE(JSOP_GETLOCAL)
2766 slot = GET_SLOTNO(regs.pc);
2767 JS_ASSERT(slot < script->nslots);
2768 PUSH_OPND(fp->slots[slot]);
2769 END_CASE(JSOP_GETLOCAL)
2770
2771 BEGIN_CASE(JSOP_CALLLOCAL)
2772 slot = GET_SLOTNO(regs.pc);
2773 JS_ASSERT(slot < script->nslots);
2774 PUSH_OPND(fp->slots[slot]);
2775 PUSH_OPND(JSVAL_NULL);
2776 END_CASE(JSOP_CALLLOCAL)
2777
2778 BEGIN_CASE(JSOP_SETLOCAL)
2779 slot = GET_SLOTNO(regs.pc);
2780 JS_ASSERT(slot < script->nslots);
2781 vp = &fp->slots[slot];
2782 *vp = FETCH_OPND(-1);
2783 END_SET_CASE(JSOP_SETLOCAL)
2784
2785 BEGIN_CASE(JSOP_GETUPVAR)
2786 BEGIN_CASE(JSOP_CALLUPVAR)
2787 {
2788 JSUpvarArray *uva = script->upvars();
2789
2790 index = GET_UINT16(regs.pc);
2791 JS_ASSERT(index < uva->length);
2792
2793 rval = js_GetUpvar(cx, script->staticLevel, uva->vector[index]);
2794 PUSH_OPND(rval);
2795
2796 if (op == JSOP_CALLUPVAR)
2797 PUSH_OPND(JSVAL_NULL);
2798 }
2799 END_CASE(JSOP_GETUPVAR)
2800
2801 BEGIN_CASE(JSOP_GETUPVAR_DBG)
2802 BEGIN_CASE(JSOP_CALLUPVAR_DBG)
2803 fun = fp->fun;
2804 JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
2805 JS_ASSERT(fun->u.i.wrapper);
2806
2807 /* Scope for tempPool mark and local names allocation in it. */
2808 {
2809 void *mark = JS_ARENA_MARK(&cx->tempPool);
2810 jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
2811 if (!names)
2812 goto error;
2813
2814 index = fun->countArgsAndVars() + GET_UINT16(regs.pc);
2815 atom = JS_LOCAL_NAME_TO_ATOM(names[index]);
2816 id = ATOM_TO_JSID(atom);
2817
2818 ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
2819 JS_ARENA_RELEASE(&cx->tempPool, mark);
2820 if (!ok)
2821 goto error;
2822 }
2823
2824 if (!prop)
2825 goto atom_not_defined;
2826
2827 /* Minimize footprint with generic code instead of NATIVE_GET. */
2828 obj2->dropProperty(cx, prop);
2829 vp = regs.sp;
2830 PUSH_OPND(JSVAL_NULL);
2831 if (!obj->getProperty(cx, id, vp))
2832 goto error;
2833
2834 if (op == JSOP_CALLUPVAR_DBG)
2835 PUSH_OPND(JSVAL_NULL);
2836 END_CASE(JSOP_GETUPVAR_DBG)
2837
2838 BEGIN_CASE(JSOP_GETDSLOT)
2839 BEGIN_CASE(JSOP_CALLDSLOT)
2840 JS_ASSERT(fp->argv);
2841 obj = JSVAL_TO_OBJECT(fp->argv[-2]);
2842 JS_ASSERT(obj);
2843 JS_ASSERT(obj->dslots);
2844
2845 index = GET_UINT16(regs.pc);
2846 JS_ASSERT(JS_INITIAL_NSLOTS + index < jsatomid(obj->dslots[-1]));
2847 JS_ASSERT_IF(OBJ_SCOPE(obj)->object == obj,
2848 JS_INITIAL_NSLOTS + index < OBJ_SCOPE(obj)->freeslot);
2849
2850 PUSH_OPND(obj->dslots[index]);
2851 if (op == JSOP_CALLDSLOT)
2852 PUSH_OPND(JSVAL_NULL);
2853 END_CASE(JSOP_GETDSLOT)
2854
2855 BEGIN_CASE(JSOP_GETGVAR)
2856 BEGIN_CASE(JSOP_CALLGVAR)
2857 slot = GET_SLOTNO(regs.pc);
2858 JS_ASSERT(slot < GlobalVarCount(fp));
2859 METER_SLOT_OP(op, slot);
2860 lval = fp->slots[slot];
2861 if (JSVAL_IS_NULL(lval)) {
2862 op = (op == JSOP_GETGVAR) ? JSOP_NAME : JSOP_CALLNAME;
2863 DO_OP();
2864 }
2865 obj = fp->varobj;
2866 slot = JSVAL_TO_INT(lval);
2867 rval = OBJ_GET_SLOT(cx, obj, slot);
2868 PUSH_OPND(rval);
2869 if (op == JSOP_CALLGVAR)
2870 PUSH_OPND(OBJECT_TO_JSVAL(obj));
2871 END_CASE(JSOP_GETGVAR)
2872
2873 BEGIN_CASE(JSOP_SETGVAR)
2874 slot = GET_SLOTNO(regs.pc);
2875 JS_ASSERT(slot < GlobalVarCount(fp));
2876 METER_SLOT_OP(op, slot);
2877 rval = FETCH_OPND(-1);
2878 obj = fp->varobj;
2879 lval = fp->slots[slot];
2880 if (JSVAL_IS_NULL(lval)) {
2881 /*
2882 * Inline-clone and deoptimize JSOP_SETNAME code here because
2883 * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval]
2884 * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME.
2885 */
2886 #ifdef JS_TRACER
2887 if (TRACE_RECORDER(cx))
2888 js_AbortRecording(cx, "SETGVAR with NULL slot");
2889 #endif
2890 LOAD_ATOM(0);
2891 id = ATOM_TO_JSID(atom);
2892 if (!obj->setProperty(cx, id, &rval))
2893 goto error;
2894 } else {
2895 slot = JSVAL_TO_INT(lval);
2896 JS_LOCK_OBJ(cx, obj);
2897 LOCKED_OBJ_WRITE_SLOT(cx, obj, slot, rval);
2898 JS_UNLOCK_OBJ(cx, obj);
2899 }
2900 END_SET_CASE(JSOP_SETGVAR)
2901
2902 BEGIN_CASE(JSOP_DEFCONST)
2903 BEGIN_CASE(JSOP_DEFVAR)
2904 index = GET_INDEX(regs.pc);
2905 atom = atoms[index];
2906
2907 /*
2908 * index is relative to atoms at this point but for global var
2909 * code below we need the absolute value.
2910 */
2911 index += atoms - script->atomMap.vector;
2912 obj = fp->varobj;
2913 JS_ASSERT(obj->map->ops->defineProperty == js_DefineProperty);
2914 attrs = JSPROP_ENUMERATE;
2915 if (!(fp->flags & JSFRAME_EVAL))
2916 attrs |= JSPROP_PERMANENT;
2917 if (op == JSOP_DEFCONST)
2918 attrs |= JSPROP_READONLY;
2919
2920 /* Lookup id in order to check for redeclaration problems. */
2921 id = ATOM_TO_JSID(atom);
2922 prop = NULL;
2923 if (!js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop))
2924 goto error;
2925
2926 /* Bind a variable only if it's not yet defined. */
2927 if (!prop) {
2928 if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID, JS_PropertyStub, JS_PropertyStub,
2929 attrs, 0, 0, &prop)) {
2930 goto error;
2931 }
2932 JS_ASSERT(prop);
2933 obj2 = obj;
2934 }
2935
2936 /*
2937 * Try to optimize a property we either just created, or found
2938 * directly in the global object, that is permanent, has a slot,
2939 * and has stub getter and setter, into a "fast global" accessed
2940 * by the JSOP_*GVAR opcodes.
2941 */
2942 if (!fp->fun &&
2943 index < GlobalVarCount(fp) &&
2944 obj2 == obj &&
2945 OBJ_IS_NATIVE(obj)) {
2946 sprop = (JSScopeProperty *) prop;
2947 if ((sprop->attrs & JSPROP_PERMANENT) &&
2948 SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) &&
2949 SPROP_HAS_STUB_GETTER(sprop) &&
2950 SPROP_HAS_STUB_SETTER(sprop)) {
2951 /*
2952 * Fast globals use frame variables to map the global
2953 * name's atom index to the permanent fp->varobj slot
2954 * number, tagged as a jsval. The atom index for the
2955 * global's name literal is identical to its variable
2956 * index.
2957 */
2958 fp->slots[index] = INT_TO_JSVAL(sprop->slot);
2959 }
2960 }
2961
2962 obj2->dropProperty(cx, prop);
2963 END_CASE(JSOP_DEFVAR)
2964
2965 BEGIN_CASE(JSOP_DEFFUN)
2966 {
2967 JSPropertyOp getter, setter;
2968 bool doSet;
2969 JSObject *pobj;
2970 JSProperty *prop;
2971 uint32 old;
2972
2973 /*
2974 * A top-level function defined in Global or Eval code (see
2975 * ECMA-262 Ed. 3), or else a SpiderMonkey extension: a named
2976 * function statement in a compound statement (not at the top
2977 * statement level of global code, or at the top level of a
2978 * function body).
2979 */
2980 LOAD_FUNCTION(0);
2981 obj = FUN_OBJECT(fun);
2982
2983 if (FUN_NULL_CLOSURE(fun)) {
2984 /*
2985 * Even a null closure needs a parent for principals finding.
2986 * FIXME: bug 476950, although debugger users may also demand
2987 * some kind of scope link for debugger-assisted eval-in-frame.
2988 */
2989 obj2 = fp->scopeChain;
2990 } else {
2991 JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
2992
2993 /*
2994 * Inline js_GetScopeChain a bit to optimize for the case of a
2995 * top-level function.
2996 */
2997 if (!fp->blockChain) {
2998 obj2 = fp->scopeChain;
2999 } else {
3000 obj2 = js_GetScopeChain(cx, fp);
3001 if (!obj2)
3002 goto error;
3003 }
3004 }
3005
3006 /*
3007 * If static link is not current scope, clone fun's object to link
3008 * to the current scope via parent. We do this to enable sharing of
3009 * compiled functions among multiple equivalent scopes, amortizing
3010 * the cost of compilation over a number of executions. Examples
3011 * include XUL scripts and event handlers shared among Firefox or
3012 * other Mozilla app chrome windows, and user-defined JS functions
3013 * precompiled and then shared among requests in server-side JS.
3014 */
3015 if (OBJ_GET_PARENT(cx, obj) != obj2) {
3016 obj = js_CloneFunctionObject(cx, fun, obj2);
3017 if (!obj)
3018 goto error;
3019 }
3020
3021 /*
3022 * Protect obj from any GC hiding below JSObject::setProperty or
3023 * JSObject::defineProperty. All paths from here must flow through
3024 * the "Restore fp->scopeChain" code below the
3025 * parent->defineProperty call.
3026 */
3027 MUST_FLOW_THROUGH("restore_scope");
3028 fp->scopeChain = obj;
3029
3030 rval = OBJECT_TO_JSVAL(obj);
3031
3032 /*
3033 * ECMA requires functions defined when entering Eval code to be
3034 * impermanent.
3035 */
3036 attrs = (fp->flags & JSFRAME_EVAL)
3037 ? JSPROP_ENUMERATE
3038 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
3039
3040 /*
3041 * Load function flags that are also property attributes. Getters
3042 * and setters do not need a slot, their value is stored elsewhere
3043 * in the property itself, not in obj slots.
3044 */
3045 setter = getter = JS_PropertyStub;
3046 flags = JSFUN_GSFLAG2ATTR(fun->flags);
3047 if (flags) {
3048 /* Function cannot be both getter a setter. */
3049 JS_ASSERT(flags == JSPROP_GETTER || flags == JSPROP_SETTER);
3050 attrs |= flags | JSPROP_SHARED;
3051 rval = JSVAL_VOID;
3052 if (flags == JSPROP_GETTER)
3053 getter = js_CastAsPropertyOp(obj);
3054 else
3055 setter = js_CastAsPropertyOp(obj);
3056 }
3057
3058 /*
3059 * We define the function as a property of the variable object and
3060 * not the current scope chain even for the case of function
3061 * expression statements and functions defined by eval inside let
3062 * or with blocks.
3063 */
3064 parent = fp->varobj;
3065 JS_ASSERT(parent);
3066
3067 /*
3068 * Check for a const property of the same name -- or any kind
3069 * of property if executing with the strict option. We check
3070 * here at runtime as well as at compile-time, to handle eval
3071 * as well as multiple HTML script tags.
3072 */
3073 id = ATOM_TO_JSID(fun->atom);
3074 prop = NULL;
3075 ok = js_CheckRedeclaration(cx, parent, id, attrs, &pobj, &prop);
3076 if (!ok)
3077 goto restore_scope;
3078
3079 /*
3080 * We deviate from 10.1.2 in ECMA 262 v3 and under eval use for
3081 * function declarations JSObject::setProperty, not
3082 * JSObject::defineProperty, to preserve the JSOP_PERMANENT
3083 * attribute of existing properties and make sure that such
3084 * properties cannot be deleted.
3085 *
3086 * We also use JSObject::setProperty for the existing properties of
3087 * Call objects with matching attributes to preserve the native
3088 * getters and setters that store the value of the property in the
3089 * interpreter frame, see bug 467495.
3090 */
3091 doSet = (attrs == JSPROP_ENUMERATE);
3092 JS_ASSERT_IF(doSet, fp->flags & JSFRAME_EVAL);
3093 if (prop) {
3094 if (parent == pobj &&
3095 OBJ_GET_CLASS(cx, parent) == &js_CallClass &&
3096 (old = ((JSScopeProperty *) prop)->attrs,
3097 !(old & (JSPROP_GETTER|JSPROP_SETTER)) &&
3098 (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs)) {
3099 /*
3100 * js_CheckRedeclaration must reject attempts to add a
3101 * getter or setter to an existing property without a
3102 * getter or setter.
3103 */
3104 JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT)));
3105 JS_ASSERT(!(old & JSPROP_READONLY));
3106 doSet = JS_TRUE;
3107 }
3108 pobj->dropProperty(cx, prop);
3109 }
3110 ok = doSet
3111 ? parent->setProperty(cx, id, &rval)
3112 : parent->defineProperty(cx, id, rval, getter, setter, attrs);
3113
3114 restore_scope:
3115 /* Restore fp->scopeChain now that obj is defined in fp->varobj. */
3116 fp->scopeChain = obj2;
3117 if (!ok)
3118 goto error;
3119 }
3120 END_CASE(JSOP_DEFFUN)
3121
3122 BEGIN_CASE(JSOP_DEFFUN_FC)
3123 BEGIN_CASE(JSOP_DEFFUN_DBGFC)
3124 LOAD_FUNCTION(0);
3125
3126 obj = (op == JSOP_DEFFUN_FC)
3127 ? js_NewFlatClosure(cx, fun)
3128 : js_NewDebuggableFlatClosure(cx, fun);
3129 if (!obj)
3130 goto error;
3131 rval = OBJECT_TO_JSVAL(obj);
3132
3133 attrs = (fp->flags & JSFRAME_EVAL)
3134 ? JSPROP_ENUMERATE
3135 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
3136
3137 flags = JSFUN_GSFLAG2ATTR(fun->flags);
3138 if (flags) {
3139 attrs |= flags | JSPROP_SHARED;
3140 rval = JSVAL_VOID;
3141 }
3142
3143 parent = fp->varobj;
3144 JS_ASSERT(parent);
3145
3146 id = ATOM_TO_JSID(fun->atom);
3147 ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL);
3148 if (ok) {
3149 if (attrs == JSPROP_ENUMERATE) {
3150 JS_ASSERT(fp->flags & JSFRAME_EVAL);
3151 ok = parent->setProperty(cx, id, &rval);
3152 } else {
3153 JS_ASSERT(attrs & JSPROP_PERMANENT);
3154
3155 ok = parent->defineProperty(cx, id, rval,
3156 (flags & JSPROP_GETTER)
3157 ? JS_EXTENSION (JSPropertyOp) obj
3158 : JS_PropertyStub,
3159 (flags & JSPROP_SETTER)
3160 ? JS_EXTENSION (JSPropertyOp) obj
3161 : JS_PropertyStub,
3162 attrs);
3163 }
3164 }
3165
3166 if (!ok)
3167 goto error;
3168 END_CASE(JSOP_DEFFUN_FC)
3169
3170 BEGIN_CASE(JSOP_DEFLOCALFUN)
3171 /*
3172 * Define a local function (i.e., one nested at the top level of
3173 * another function), parented by the current scope chain, stored
3174 * in a local variable slot that the compiler allocated. This is
3175 * an optimization over JSOP_DEFFUN that avoids requiring a call
3176 * object for the outer function's activation.
3177 */
3178 LOAD_FUNCTION(SLOTNO_LEN);
3179 JS_ASSERT(FUN_INTERPRETED(fun));
3180 JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
3181 obj = FUN_OBJECT(fun);
3182
3183 if (FUN_NULL_CLOSURE(fun)) {
3184 obj = js_CloneFunctionObject(cx, fun, fp->scopeChain);
3185 if (!obj)
3186 goto error;
3187 } else {
3188 parent = js_GetScopeChain(cx, fp);
3189 if (!parent)
3190 goto error;
3191
3192 if (OBJ_GET_PARENT(cx, obj) != parent) {
3193 #ifdef JS_TRACER
3194 if (TRACE_RECORDER(cx))
3195 js_AbortRecording(cx, "DEFLOCALFUN for closure");
3196 #endif
3197 obj = js_CloneFunctionObject(cx, fun, parent);
3198 if (!obj)
3199 goto error;
3200 }
3201 }
3202
3203 slot = GET_SLOTNO(regs.pc);
3204 TRACE_2(DefLocalFunSetSlot, slot, obj);
3205
3206 fp->slots[slot] = OBJECT_TO_JSVAL(obj);
3207 END_CASE(JSOP_DEFLOCALFUN)
3208
3209 BEGIN_CASE(JSOP_DEFLOCALFUN_FC)
3210 LOAD_FUNCTION(SLOTNO_LEN);
3211
3212 obj = js_NewFlatClosure(cx, fun);
3213 if (!obj)
3214 goto error;
3215
3216 slot = GET_SLOTNO(regs.pc);
3217 TRACE_2(DefLocalFunSetSlot, slot, obj);
3218
3219 fp->slots[slot] = OBJECT_TO_JSVAL(obj);
3220 END_CASE(JSOP_DEFLOCALFUN_FC)
3221
3222 BEGIN_CASE(JSOP_DEFLOCALFUN_DBGFC)
3223 LOAD_FUNCTION(SLOTNO_LEN);
3224
3225 obj = js_NewDebuggableFlatClosure(cx, fun);
3226 if (!obj)
3227 goto error;
3228
3229 slot = GET_SLOTNO(regs.pc);
3230 fp->slots[slot] = OBJECT_TO_JSVAL(obj);
3231 END_CASE(JSOP_DEFLOCALFUN_DBGFC)
3232
3233 BEGIN_CASE(JSOP_LAMBDA)
3234 /* Load the specified function object literal. */
3235 LOAD_FUNCTION(0);
3236 obj = FUN_OBJECT(fun);
3237
3238 if (FUN_NULL_CLOSURE(fun)) {
3239 obj = js_CloneFunctionObject(cx, fun, fp->scopeChain);
3240 if (!obj)
3241 goto error;
3242 } else {
3243 parent = js_GetScopeChain(cx, fp);
3244 if (!parent)
3245 goto error;
3246
3247 /*
3248 * FIXME: bug 471214, Cloning here even when the compiler saw
3249 * the right parent is wasteful but we don't fully support
3250 * joined function objects, yet.
3251 */
3252 obj = js_CloneFunctionObject(cx, fun, parent);
3253 if (!obj)
3254 goto error;
3255 }
3256
3257 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3258 END_CASE(JSOP_LAMBDA