1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=78:
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 /*
42 * JavaScript bytecode interpreter.
43 */
44 #include "jsstddef.h"
45 #include <stdio.h>
46 #include <string.h>
47 #include <math.h>
48 #include "jstypes.h"
49 #include "jsarena.h" /* Added by JSIFY */
50 #include "jsutil.h" /* Added by JSIFY */
51 #include "jsprf.h"
52 #include "jsapi.h"
53 #include "jsarray.h"
54 #include "jsatom.h"
55 #include "jsbool.h"
56 #include "jscntxt.h"
57 #include "jsconfig.h"
58 #include "jsdbgapi.h"
59 #include "jsfun.h"
60 #include "jsgc.h"
61 #include "jsinterp.h"
62 #include "jsiter.h"
63 #include "jslock.h"
64 #include "jsnum.h"
65 #include "jsobj.h"
66 #include "jsopcode.h"
67 #include "jsscan.h"
68 #include "jsscope.h"
69 #include "jsscript.h"
70 #include "jsstr.h"
71
72 #if JS_HAS_XML_SUPPORT
73 #include "jsxml.h"
74 #endif
75
76 #ifdef DEBUG
77 #define ASSERT_CACHE_IS_EMPTY(cache) \
78 JS_BEGIN_MACRO \
79 JSPropertyCacheEntry *end_, *pce_, entry_; \
80 JSPropertyCache *cache_ = (cache); \
81 JS_ASSERT(cache_->empty); \
82 end_ = &cache_->table[PROPERTY_CACHE_SIZE]; \
83 for (pce_ = &cache_->table[0]; pce_ < end_; pce_++) { \
84 PCE_LOAD(cache_, pce_, entry_); \
85 JS_ASSERT(!PCE_OBJECT(entry_)); \
86 JS_ASSERT(!PCE_PROPERTY(entry_)); \
87 } \
88 JS_END_MACRO
89 #else
90 #define ASSERT_CACHE_IS_EMPTY(cache) ((void)0)
91 #endif
92
93 void
js_FlushPropertyCache(JSContext * cx)94 js_FlushPropertyCache(JSContext *cx)
95 {
96 JSPropertyCache *cache;
97
98 cache = &cx->runtime->propertyCache;
99 if (cache->empty) {
100 ASSERT_CACHE_IS_EMPTY(cache);
101 return;
102 }
103 memset(cache->table, 0, sizeof cache->table);
104 cache->empty = JS_TRUE;
105 #ifdef JS_PROPERTY_CACHE_METERING
106 cache->flushes++;
107 #endif
108 }
109
110 void
js_DisablePropertyCache(JSContext * cx)111 js_DisablePropertyCache(JSContext *cx)
112 {
113 JS_ASSERT(!cx->runtime->propertyCache.disabled);
114 cx->runtime->propertyCache.disabled = JS_TRUE;
115 }
116
117 void
js_EnablePropertyCache(JSContext * cx)118 js_EnablePropertyCache(JSContext *cx)
119 {
120 JS_ASSERT(cx->runtime->propertyCache.disabled);
121 ASSERT_CACHE_IS_EMPTY(&cx->runtime->propertyCache);
122 cx->runtime->propertyCache.disabled = JS_FALSE;
123 }
124
125 /*
126 * Stack macros and functions. These all use a local variable, jsval *sp, to
127 * point to the next free stack slot. SAVE_SP must be called before any call
128 * to a function that may invoke the interpreter. RESTORE_SP must be called
129 * only after return from js_Invoke, because only js_Invoke changes fp->sp.
130 */
131 #define PUSH(v) (*sp++ = (v))
132 #define POP() (*--sp)
133 #ifdef DEBUG
134 #define SAVE_SP(fp) \
135 (JS_ASSERT((fp)->script || !(fp)->spbase || (sp) == (fp)->spbase), \
136 (fp)->sp = sp)
137 #else
138 #define SAVE_SP(fp) ((fp)->sp = sp)
139 #endif
140 #define RESTORE_SP(fp) (sp = (fp)->sp)
141
142 /*
143 * SAVE_SP_AND_PC commits deferred stores of interpreter registers to their
144 * homes in fp, when calling out of the interpreter loop or threaded code.
145 * RESTORE_SP_AND_PC copies the other way, to update registers after a call
146 * to a subroutine that interprets a piece of the current script.
147 */
148 #define SAVE_SP_AND_PC(fp) (SAVE_SP(fp), (fp)->pc = pc)
149 #define RESTORE_SP_AND_PC(fp) (RESTORE_SP(fp), pc = (fp)->pc)
150
151 /*
152 * Push the generating bytecode's pc onto the parallel pc stack that runs
153 * depth slots below the operands.
154 *
155 * NB: PUSH_OPND uses sp, depth, and pc from its lexical environment. See
156 * js_Interpret for these local variables' declarations and uses.
157 */
158 #define PUSH_OPND(v) (sp[-depth] = (jsval)pc, PUSH(v))
159 #define STORE_OPND(n,v) (sp[(n)-depth] = (jsval)pc, sp[n] = (v))
160 #define POP_OPND() POP()
161 #define FETCH_OPND(n) (sp[n])
162
163 /*
164 * Push the jsdouble d using sp, depth, and pc from the lexical environment.
165 * Try to convert d to a jsint that fits in a jsval, otherwise GC-alloc space
166 * for it and push a reference.
167 */
168 #define STORE_NUMBER(cx, n, d) \
169 JS_BEGIN_MACRO \
170 jsint i_; \
171 jsval v_; \
172 \
173 if (JSDOUBLE_IS_INT(d, i_) && INT_FITS_IN_JSVAL(i_)) { \
174 v_ = INT_TO_JSVAL(i_); \
175 } else { \
176 ok = js_NewDoubleValue(cx, d, &v_); \
177 if (!ok) \
178 goto out; \
179 } \
180 STORE_OPND(n, v_); \
181 JS_END_MACRO
182
183 #define STORE_INT(cx, n, i) \
184 JS_BEGIN_MACRO \
185 jsval v_; \
186 \
187 if (INT_FITS_IN_JSVAL(i)) { \
188 v_ = INT_TO_JSVAL(i); \
189 } else { \
190 ok = js_NewDoubleValue(cx, (jsdouble)(i), &v_); \
191 if (!ok) \
192 goto out; \
193 } \
194 STORE_OPND(n, v_); \
195 JS_END_MACRO
196
197 #define STORE_UINT(cx, n, u) \
198 JS_BEGIN_MACRO \
199 jsval v_; \
200 \
201 if ((u) <= JSVAL_INT_MAX) { \
202 v_ = INT_TO_JSVAL(u); \
203 } else { \
204 ok = js_NewDoubleValue(cx, (jsdouble)(u), &v_); \
205 if (!ok) \
206 goto out; \
207 } \
208 STORE_OPND(n, v_); \
209 JS_END_MACRO
210
211 #define FETCH_NUMBER(cx, n, d) \
212 JS_BEGIN_MACRO \
213 jsval v_; \
214 \
215 v_ = FETCH_OPND(n); \
216 VALUE_TO_NUMBER(cx, v_, d); \
217 JS_END_MACRO
218
219 #define FETCH_INT(cx, n, i) \
220 JS_BEGIN_MACRO \
221 jsval v_ = FETCH_OPND(n); \
222 if (JSVAL_IS_INT(v_)) { \
223 i = JSVAL_TO_INT(v_); \
224 } else { \
225 SAVE_SP_AND_PC(fp); \
226 ok = js_ValueToECMAInt32(cx, v_, &i); \
227 if (!ok) \
228 goto out; \
229 } \
230 JS_END_MACRO
231
232 #define FETCH_UINT(cx, n, ui) \
233 JS_BEGIN_MACRO \
234 jsval v_ = FETCH_OPND(n); \
235 jsint i_; \
236 if (JSVAL_IS_INT(v_) && (i_ = JSVAL_TO_INT(v_)) >= 0) { \
237 ui = (uint32) i_; \
238 } else { \
239 SAVE_SP_AND_PC(fp); \
240 ok = js_ValueToECMAUint32(cx, v_, &ui); \
241 if (!ok) \
242 goto out; \
243 } \
244 JS_END_MACRO
245
246 /*
247 * Optimized conversion macros that test for the desired type in v before
248 * homing sp and calling a conversion function.
249 */
250 #define VALUE_TO_NUMBER(cx, v, d) \
251 JS_BEGIN_MACRO \
252 if (JSVAL_IS_INT(v)) { \
253 d = (jsdouble)JSVAL_TO_INT(v); \
254 } else if (JSVAL_IS_DOUBLE(v)) { \
255 d = *JSVAL_TO_DOUBLE(v); \
256 } else { \
257 SAVE_SP_AND_PC(fp); \
258 ok = js_ValueToNumber(cx, v, &d); \
259 if (!ok) \
260 goto out; \
261 } \
262 JS_END_MACRO
263
264 #define POP_BOOLEAN(cx, v, b) \
265 JS_BEGIN_MACRO \
266 v = FETCH_OPND(-1); \
267 if (v == JSVAL_NULL) { \
268 b = JS_FALSE; \
269 } else if (JSVAL_IS_BOOLEAN(v)) { \
270 b = JSVAL_TO_BOOLEAN(v); \
271 } else { \
272 SAVE_SP_AND_PC(fp); \
273 ok = js_ValueToBoolean(cx, v, &b); \
274 if (!ok) \
275 goto out; \
276 } \
277 sp--; \
278 JS_END_MACRO
279
280 /*
281 * Convert a primitive string, number or boolean to a corresponding object.
282 * v must not be an object, null or undefined when using this macro.
283 */
284 #define PRIMITIVE_TO_OBJECT(cx, v, obj) \
285 JS_BEGIN_MACRO \
286 SAVE_SP(fp); \
287 if (JSVAL_IS_STRING(v)) { \
288 obj = js_StringToObject(cx, JSVAL_TO_STRING(v)); \
289 } else if (JSVAL_IS_INT(v)) { \
290 obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v)); \
291 } else if (JSVAL_IS_DOUBLE(v)) { \
292 obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v)); \
293 } else { \
294 JS_ASSERT(JSVAL_IS_BOOLEAN(v)); \
295 obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v)); \
296 } \
297 JS_END_MACRO
298
299 #define VALUE_TO_OBJECT(cx, v, obj) \
300 JS_BEGIN_MACRO \
301 if (!JSVAL_IS_PRIMITIVE(v)) { \
302 obj = JSVAL_TO_OBJECT(v); \
303 } else { \
304 SAVE_SP_AND_PC(fp); \
305 obj = js_ValueToNonNullObject(cx, v); \
306 if (!obj) { \
307 ok = JS_FALSE; \
308 goto out; \
309 } \
310 } \
311 JS_END_MACRO
312
313 #define FETCH_OBJECT(cx, n, v, obj) \
314 JS_BEGIN_MACRO \
315 v = FETCH_OPND(n); \
316 VALUE_TO_OBJECT(cx, v, obj); \
317 STORE_OPND(n, OBJECT_TO_JSVAL(obj)); \
318 JS_END_MACRO
319
320 #define VALUE_TO_PRIMITIVE(cx, v, hint, vp) \
321 JS_BEGIN_MACRO \
322 if (JSVAL_IS_PRIMITIVE(v)) { \
323 *vp = v; \
324 } else { \
325 SAVE_SP_AND_PC(fp); \
326 ok = OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, vp); \
327 if (!ok) \
328 goto out; \
329 } \
330 JS_END_MACRO
331
332 JS_FRIEND_API(jsval *)
js_AllocRawStack(JSContext * cx,uintN nslots,void ** markp)333 js_AllocRawStack(JSContext *cx, uintN nslots, void **markp)
334 {
335 jsval *sp;
336
337 if (markp)
338 *markp = JS_ARENA_MARK(&cx->stackPool);
339 JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval));
340 if (!sp) {
341 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_STACK_OVERFLOW,
342 (cx->fp && cx->fp->fun)
343 ? JS_GetFunctionName(cx->fp->fun)
344 : "script");
345 }
346 return sp;
347 }
348
349 JS_FRIEND_API(void)
js_FreeRawStack(JSContext * cx,void * mark)350 js_FreeRawStack(JSContext *cx, void *mark)
351 {
352 JS_ARENA_RELEASE(&cx->stackPool, mark);
353 }
354
355 JS_FRIEND_API(jsval *)
js_AllocStack(JSContext * cx,uintN nslots,void ** markp)356 js_AllocStack(JSContext *cx, uintN nslots, void **markp)
357 {
358 jsval *sp, *vp, *end;
359 JSArena *a;
360 JSStackHeader *sh;
361 JSStackFrame *fp;
362
363 /* Callers don't check for zero nslots: we do to avoid empty segments. */
364 if (nslots == 0) {
365 *markp = NULL;
366 return JS_ARENA_MARK(&cx->stackPool);
367 }
368
369 /* Allocate 2 extra slots for the stack segment header we'll likely need. */
370 sp = js_AllocRawStack(cx, 2 + nslots, markp);
371 if (!sp)
372 return NULL;
373
374 /* Try to avoid another header if we can piggyback on the last segment. */
375 a = cx->stackPool.current;
376 sh = cx->stackHeaders;
377 if (sh && JS_STACK_SEGMENT(sh) + sh->nslots == sp) {
378 /* Extend the last stack segment, give back the 2 header slots. */
379 sh->nslots += nslots;
380 a->avail -= 2 * sizeof(jsval);
381 } else {
382 /*
383 * Need a new stack segment, so we must initialize unused slots in the
384 * current frame. See js_GC, just before marking the "operand" jsvals,
385 * where we scan from fp->spbase to fp->sp or through fp->script->depth
386 * (whichever covers fewer slots).
387 */
388 fp = cx->fp;
389 if (fp && fp->script && fp->spbase) {
390 #ifdef DEBUG
391 jsuword depthdiff = fp->script->depth * sizeof(jsval);
392 JS_ASSERT(JS_UPTRDIFF(fp->sp, fp->spbase) <= depthdiff);
393 JS_ASSERT(JS_UPTRDIFF(*markp, fp->spbase) >= depthdiff);
394 #endif
395 end = fp->spbase + fp->script->depth;
396 for (vp = fp->sp; vp < end; vp++)
397 *vp = JSVAL_VOID;
398 }
399
400 /* Allocate and push a stack segment header from the 2 extra slots. */
401 sh = (JSStackHeader *)sp;
402 sh->nslots = nslots;
403 sh->down = cx->stackHeaders;
404 cx->stackHeaders = sh;
405 sp += 2;
406 }
407
408 /*
409 * Store JSVAL_NULL using memset, to let compilers optimize as they see
410 * fit, in case a caller allocates and pushes GC-things one by one, which
411 * could nest a last-ditch GC that will scan this segment.
412 */
413 memset(sp, 0, nslots * sizeof(jsval));
414 return sp;
415 }
416
417 JS_FRIEND_API(void)
js_FreeStack(JSContext * cx,void * mark)418 js_FreeStack(JSContext *cx, void *mark)
419 {
420 JSStackHeader *sh;
421 jsuword slotdiff;
422
423 /* Check for zero nslots allocation special case. */
424 if (!mark)
425 return;
426
427 /* We can assert because js_FreeStack always balances js_AllocStack. */
428 sh = cx->stackHeaders;
429 JS_ASSERT(sh);
430
431 /* If mark is in the current segment, reduce sh->nslots, else pop sh. */
432 slotdiff = JS_UPTRDIFF(mark, JS_STACK_SEGMENT(sh)) / sizeof(jsval);
433 if (slotdiff < (jsuword)sh->nslots)
434 sh->nslots = slotdiff;
435 else
436 cx->stackHeaders = sh->down;
437
438 /* Release the stackPool space allocated since mark was set. */
439 JS_ARENA_RELEASE(&cx->stackPool, mark);
440 }
441
442 JSBool
js_GetArgument(JSContext * cx,JSObject * obj,jsval id,jsval * vp)443 js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
444 {
445 return JS_TRUE;
446 }
447
448 JSBool
js_SetArgument(JSContext * cx,JSObject * obj,jsval id,jsval * vp)449 js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
450 {
451 return JS_TRUE;
452 }
453
454 JSBool
js_GetLocalVariable(JSContext * cx,JSObject * obj,jsval id,jsval * vp)455 js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
456 {
457 return JS_TRUE;
458 }
459
460 JSBool
js_SetLocalVariable(JSContext * cx,JSObject * obj,jsval id,jsval * vp)461 js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
462 {
463 return JS_TRUE;
464 }
465
466 JSObject *
js_GetScopeChain(JSContext * cx,JSStackFrame * fp)467 js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
468 {
469 JSObject *obj, *cursor, *clonedChild, *parent;
470 JSTempValueRooter tvr;
471
472 obj = fp->blockChain;
473 if (!obj) {
474 /*
475 * Don't force a call object for a lightweight function call, but do
476 * insist that there is a call object for a heavyweight function call.
477 */
478 JS_ASSERT(!fp->fun ||
479 !(fp->fun->flags & JSFUN_HEAVYWEIGHT) ||
480 fp->callobj);
481 JS_ASSERT(fp->scopeChain);
482 return fp->scopeChain;
483 }
484
485 /*
486 * We have one or more lexical scopes to reflect into fp->scopeChain, so
487 * make sure there's a call object at the current head of the scope chain,
488 * if this frame is a call frame.
489 */
490 if (fp->fun && !fp->callobj) {
491 JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass ||
492 JS_GetPrivate(cx, fp->scopeChain) != fp);
493 if (!js_GetCallObject(cx, fp, fp->scopeChain))
494 return NULL;
495 }
496
497 /*
498 * Clone the block chain. To avoid recursive cloning we set the parent of
499 * the cloned child after we clone the parent. In the following loop when
500 * clonedChild is null it indicates the first iteration when no special GC
501 * rooting is necessary. On the second and the following iterations we
502 * have to protect cloned so far chain against the GC during cloning of
503 * the cursor object.
504 */
505 cursor = obj;
506 clonedChild = NULL;
507 for (;;) {
508 parent = OBJ_GET_PARENT(cx, cursor);
509
510 /*
511 * We pass fp->scopeChain and not null even if we override the parent
512 * slot later as null triggers useless calculations of slot's value in
513 * js_NewObject that js_CloneBlockObject calls.
514 */
515 cursor = js_CloneBlockObject(cx, cursor, fp->scopeChain, fp);
516 if (!cursor) {
517 if (clonedChild)
518 JS_POP_TEMP_ROOT(cx, &tvr);
519 return NULL;
520 }
521 if (!clonedChild) {
522 /*
523 * The first iteration. Check if other follow and root obj if so
524 * to protect the whole cloned chain against GC.
525 */
526 obj = cursor;
527 if (!parent)
528 break;
529 JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
530 } else {
531 /*
532 * Avoid OBJ_SET_PARENT overhead as clonedChild cannot escape to
533 * other threads.
534 */
535 clonedChild->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(cursor);
536 if (!parent) {
537 JS_ASSERT(tvr.u.value == OBJECT_TO_JSVAL(obj));
538 JS_POP_TEMP_ROOT(cx, &tvr);
539 break;
540 }
541 }
542 clonedChild = cursor;
543 cursor = parent;
544 }
545 fp->flags |= JSFRAME_POP_BLOCKS;
546 fp->scopeChain = obj;
547 fp->blockChain = NULL;
548 return obj;
549 }
550
551 /*
552 * Walk the scope chain looking for block scopes whose locals need to be
553 * copied from stack slots into object slots before fp goes away.
554 */
555 static JSBool
PutBlockObjects(JSContext * cx,JSStackFrame * fp)556 PutBlockObjects(JSContext *cx, JSStackFrame *fp)
557 {
558 JSBool ok;
559 JSObject *obj;
560
561 ok = JS_TRUE;
562 for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
563 if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) {
564 if (JS_GetPrivate(cx, obj) != fp)
565 break;
566 ok &= js_PutBlockObject(cx, obj);
567 }
568 }
569 return ok;
570 }
571
572 JSObject *
js_ComputeThis(JSContext * cx,JSObject * thisp,jsval * argv)573 js_ComputeThis(JSContext *cx, JSObject *thisp, jsval *argv)
574 {
575 if (thisp && OBJ_GET_CLASS(cx, thisp) != &js_CallClass) {
576 /* Some objects (e.g., With) delegate 'this' to another object. */
577 thisp = OBJ_THIS_OBJECT(cx, thisp);
578 if (!thisp)
579 return NULL;
580 } else {
581 /*
582 * ECMA requires "the global object", but in the presence of multiple
583 * top-level objects (windows, frames, or certain layers in the client
584 * object model), we prefer fun's parent. An example that causes this
585 * code to run:
586 *
587 * // in window w1
588 * function f() { return this }
589 * function g() { return f }
590 *
591 * // in window w2
592 * var h = w1.g()
593 * alert(h() == w1)
594 *
595 * The alert should display "true".
596 */
597 if (JSVAL_IS_PRIMITIVE(argv[-2]) ||
598 !OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]))) {
599 thisp = cx->globalObject;
600 } else {
601 jsid id;
602 jsval v;
603 uintN attrs;
604
605 /* Walk up the parent chain. */
606 thisp = JSVAL_TO_OBJECT(argv[-2]);
607 id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
608 for (;;) {
609 if (!OBJ_CHECK_ACCESS(cx, thisp, id, JSACC_PARENT, &v, &attrs))
610 return NULL;
611 if (JSVAL_IS_VOID(v))
612 v = OBJ_GET_SLOT(cx, thisp, JSSLOT_PARENT);
613 if (JSVAL_IS_NULL(v))
614 break;
615 thisp = JSVAL_TO_OBJECT(v);
616 }
617 }
618 }
619 argv[-1] = OBJECT_TO_JSVAL(thisp);
620 return thisp;
621 }
622
623 #if JS_HAS_NO_SUCH_METHOD
624
625 static JSBool
NoSuchMethod(JSContext * cx,JSStackFrame * fp,jsval * vp,uint32 flags,uintN argc)626 NoSuchMethod(JSContext *cx, JSStackFrame *fp, jsval *vp, uint32 flags,
627 uintN argc)
628 {
629 JSObject *thisp, *argsobj;
630 jsval *sp, roots[3];
631 JSTempValueRooter tvr;
632 jsid id;
633 JSBool ok;
634 jsbytecode *pc;
635 jsatomid atomIndex;
636
637 /*
638 * We must call js_ComputeThis here to censor Call objects. A performance
639 * hit, since we'll call it again in the normal sequence of invoke events,
640 * but at least it's idempotent.
641 *
642 * Normally, we call ComputeThis after all frame members have been set,
643 * and in particular, after any revision of the callee value at *vp due
644 * to clasp->convert (see below). This matters because ComputeThis may
645 * access *vp via fp->argv[-2], to follow the parent chain to a global
646 * object to use as the 'this' parameter.
647 *
648 * Obviously, here in the JSVAL_IS_PRIMITIVE(v) case, there can't be any
649 * such defaulting of 'this' to callee (v, *vp) ancestor.
650 */
651 JS_ASSERT(JSVAL_IS_PRIMITIVE(vp[0]));
652 RESTORE_SP(fp);
653 if (JSVAL_IS_OBJECT(vp[1])) {
654 thisp = JSVAL_TO_OBJECT(vp[1]);
655 } else {
656 PRIMITIVE_TO_OBJECT(cx, vp[1], thisp);
657 if (!thisp)
658 return JS_FALSE;
659 vp[1] = OBJECT_TO_JSVAL(thisp);
660 }
661 thisp = js_ComputeThis(cx, thisp, vp + 2);
662 if (!thisp)
663 return JS_FALSE;
664 vp[1] = OBJECT_TO_JSVAL(thisp);
665
666 /* From here on, control must flow through label out: to return. */
667 memset(roots, 0, sizeof roots);
668 JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(roots), roots, &tvr);
669
670 id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
671 #if JS_HAS_XML_SUPPORT
672 if (OBJECT_IS_XML(cx, thisp)) {
673 JSXMLObjectOps *ops;
674
675 ops = (JSXMLObjectOps *) thisp->map->ops;
676 thisp = ops->getMethod(cx, thisp, id, &roots[2]);
677 if (!thisp) {
678 ok = JS_FALSE;
679 goto out;
680 }
681 vp[1] = OBJECT_TO_JSVAL(thisp);
682 } else
683 #endif
684 {
685 ok = OBJ_GET_PROPERTY(cx, thisp, id, &roots[2]);
686 if (!ok)
687 goto out;
688 }
689 if (JSVAL_IS_PRIMITIVE(roots[2]))
690 goto not_function;
691
692 pc = (jsbytecode *) vp[-(intN)fp->script->depth];
693 switch ((JSOp) *pc) {
694 case JSOP_NAME:
695 case JSOP_GETPROP:
696 #if JS_HAS_XML_SUPPORT
697 case JSOP_GETMETHOD:
698 #endif
699 atomIndex = GET_ATOM_INDEX(pc);
700 roots[0] = ATOM_KEY(js_GetAtom(cx, &fp->script->atomMap, atomIndex));
701 argsobj = js_NewArrayObject(cx, argc, vp + 2);
702 if (!argsobj) {
703 ok = JS_FALSE;
704 goto out;
705 }
706 roots[1] = OBJECT_TO_JSVAL(argsobj);
707 ok = js_InternalInvoke(cx, thisp, roots[2], flags | JSINVOKE_INTERNAL,
708 2, roots, &vp[0]);
709 break;
710
711 default:
712 goto not_function;
713 }
714
715 out:
716 JS_POP_TEMP_ROOT(cx, &tvr);
717 return ok;
718
719 not_function:
720 js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
721 ok = JS_FALSE;
722 goto out;
723 }
724
725 #endif /* JS_HAS_NO_SUCH_METHOD */
726
727 #ifdef DUMP_CALL_TABLE
728
729 #include "jsclist.h"
730 #include "jshash.h"
731 #include "jsdtoa.h"
732
733 typedef struct CallKey {
734 jsval callee; /* callee value */
735 const char *filename; /* function filename or null */
736 uintN lineno; /* function lineno or 0 */
737 } CallKey;
738
739 /* Compensate for typeof null == "object" brain damage. */
740 #define JSTYPE_NULL JSTYPE_LIMIT
741 #define TYPEOF(cx,v) (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v))
742 #define TYPENAME(t) (((t) == JSTYPE_NULL) ? js_null_str : js_type_str[t])
743 #define NTYPEHIST (JSTYPE_LIMIT + 1)
744
745 typedef struct CallValue {
746 uint32 total; /* total call count */
747 uint32 recycled; /* LRU-recycled calls lost */
748 uint16 minargc; /* minimum argument count */
749 uint16 maxargc; /* maximum argument count */
750 struct ArgInfo {
751 uint32 typeHist[NTYPEHIST]; /* histogram by type */
752 JSCList lruList; /* top 10 values LRU list */
753 struct ArgValCount {
754 JSCList lruLink; /* LRU list linkage */
755 jsval value; /* recently passed value */
756 uint32 count; /* number of times passed */
757 char strbuf[112]; /* string conversion buffer */
758 } topValCounts[10]; /* top 10 value storage */
759 } argInfo[8];
760 } CallValue;
761
762 typedef struct CallEntry {
763 JSHashEntry entry;
764 CallKey key;
765 CallValue value;
766 char name[32]; /* function name copy */
767 } CallEntry;
768
769 static void *
AllocCallTable(void * pool,size_t size)770 AllocCallTable(void *pool, size_t size)
771 {
772 return malloc(size);
773 }
774
775 static void
FreeCallTable(void * pool,void * item)776 FreeCallTable(void *pool, void *item)
777 {
778 free(item);
779 }
780
781 static JSHashEntry *
AllocCallEntry(void * pool,const void * key)782 AllocCallEntry(void *pool, const void *key)
783 {
784 return (JSHashEntry*) calloc(1, sizeof(CallEntry));
785 }
786
787 static void
FreeCallEntry(void * pool,JSHashEntry * he,uintN flag)788 FreeCallEntry(void *pool, JSHashEntry *he, uintN flag)
789 {
790 JS_ASSERT(flag == HT_FREE_ENTRY);
791 free(he);
792 }
793
794 static JSHashAllocOps callTableAllocOps = {
795 AllocCallTable, FreeCallTable,
796 AllocCallEntry, FreeCallEntry
797 };
798
799 JS_STATIC_DLL_CALLBACK(JSHashNumber)
js_hash_call_key(const void * key)800 js_hash_call_key(const void *key)
801 {
802 CallKey *ck = (CallKey *) key;
803 JSHashNumber hash = (jsuword)ck->callee >> 3;
804
805 if (ck->filename) {
806 hash = (hash << 4) ^ JS_HashString(ck->filename);
807 hash = (hash << 4) ^ ck->lineno;
808 }
809 return hash;
810 }
811
812 JS_STATIC_DLL_CALLBACK(intN)
js_compare_call_keys(const void * k1,const void * k2)813 js_compare_call_keys(const void *k1, const void *k2)
814 {
815 CallKey *ck1 = (CallKey *)k1, *ck2 = (CallKey *)k2;
816
817 return ck1->callee == ck2->callee &&
818 ((ck1->filename && ck2->filename)
819 ? strcmp(ck1->filename, ck2->filename) == 0
820 : ck1->filename == ck2->filename) &&
821 ck1->lineno == ck2->lineno;
822 }
823
824 JSHashTable *js_CallTable;
825 size_t js_LogCallToSourceLimit;
826
827 JS_STATIC_DLL_CALLBACK(intN)
CallTableDumper(JSHashEntry * he,intN k,void * arg)828 CallTableDumper(JSHashEntry *he, intN k, void *arg)
829 {
830 CallEntry *ce = (CallEntry *)he;
831 FILE *fp = (FILE *)arg;
832 uintN argc, i, n;
833 struct ArgInfo *ai;
834 JSType save, type;
835 JSCList *cl;
836 struct ArgValCount *avc;
837 jsval argval;
838
839 if (ce->key.filename) {
840 /* We're called at the end of the mark phase, so mark our filenames. */
841 js_MarkScriptFilename(ce->key.filename);
842 fprintf(fp, "%s:%u ", ce->key.filename, ce->key.lineno);
843 } else {
844 fprintf(fp, "@%p ", (void *) ce->key.callee);
845 }
846
847 if (ce->name[0])
848 fprintf(fp, "name %s ", ce->name);
849 fprintf(fp, "calls %lu (%lu) argc %u/%u\n",
850 (unsigned long) ce->value.total,
851 (unsigned long) ce->value.recycled,
852 ce->value.minargc, ce->value.maxargc);
853
854 argc = JS_MIN(ce->value.maxargc, 8);
855 for (i = 0; i < argc; i++) {
856 ai = &ce->value.argInfo[i];
857
858 n = 0;
859 save = -1;
860 for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) {
861 if (ai->typeHist[type]) {
862 save = type;
863 ++n;
864 }
865 }
866 if (n == 1) {
867 fprintf(fp, " arg %u type %s: %lu\n",
868 i, TYPENAME(save), (unsigned long) ai->typeHist[save]);
869 } else {
870 fprintf(fp, " arg %u type histogram:\n", i);
871 for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) {
872 fprintf(fp, " %9s: %8lu ",
873 TYPENAME(type), (unsigned long) ai->typeHist[type]);
874 for (n = (uintN) JS_HOWMANY(ai->typeHist[type], 10); n > 0; --n)
875 fputc('*', fp);
876 fputc('\n', fp);
877 }
878 }
879
880 fprintf(fp, " arg %u top 10 values:\n", i);
881 n = 1;
882 for (cl = ai->lruList.prev; cl != &ai->lruList; cl = cl->prev) {
883 avc = (struct ArgValCount *)cl;
884 if (!avc->count)
885 break;
886 argval = avc->value;
887 fprintf(fp, " %9u: %8lu %.*s (%#lx)\n",
888 n, (unsigned long) avc->count,
889 sizeof avc->strbuf, avc->strbuf, argval);
890 ++n;
891 }
892 }
893
894 return HT_ENUMERATE_NEXT;
895 }
896
897 void
js_DumpCallTable(JSContext * cx)898 js_DumpCallTable(JSContext *cx)
899 {
900 char name[24];
901 FILE *fp;
902 static uintN dumpCount;
903
904 if (!js_CallTable)
905 return;
906
907 JS_snprintf(name, sizeof name, "/tmp/calltable.dump.%u", dumpCount & 7);
908 dumpCount++;
909 fp = fopen(name, "w");
910 if (!fp)
911 return;
912
913 JS_HashTableEnumerateEntries(js_CallTable, CallTableDumper, fp);
914 fclose(fp);
915 }
916
917 static void
LogCall(JSContext * cx,jsval callee,uintN argc,jsval * argv)918 LogCall(JSContext *cx, jsval callee, uintN argc, jsval *argv)
919 {
920 CallKey key;
921 const char *name, *cstr;
922 JSFunction *fun;
923 JSHashNumber keyHash;
924 JSHashEntry **hep, *he;
925 CallEntry *ce;
926 uintN i, j;
927 jsval argval;
928 JSType type;
929 struct ArgInfo *ai;
930 struct ArgValCount *avc;
931 JSString *str;
932
933 if (!js_CallTable) {
934 js_CallTable = JS_NewHashTable(1024, js_hash_call_key,
935 js_compare_call_keys, NULL,
936 &callTableAllocOps, NULL);
937 if (!js_CallTable)
938 return;
939 }
940
941 key.callee = callee;
942 key.filename = NULL;
943 key.lineno = 0;
944 name = "";
945 if (VALUE_IS_FUNCTION(cx, callee)) {
946 fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(callee));
947 if (fun->atom)
948 name = js_AtomToPrintableString(cx, fun->atom);
949 if (FUN_INTERPRETED(fun)) {
950 key.filename = fun->u.i.script->filename;
951 key.lineno = fun->u.i.script->lineno;
952 }
953 }
954 keyHash = js_hash_call_key(&key);
955
956 hep = JS_HashTableRawLookup(js_CallTable, keyHash, &key);
957 he = *hep;
958 if (he) {
959 ce = (CallEntry *) he;
960 JS_ASSERT(strncmp(ce->name, name, sizeof ce->name) == 0);
961 } else {
962 he = JS_HashTableRawAdd(js_CallTable, hep, keyHash, &key, NULL);
963 if (!he)
964 return;
965 ce = (CallEntry *) he;
966 ce->entry.key = &ce->key;
967 ce->entry.value = &ce->value;
968 ce->key = key;
969 for (i = 0; i < 8; i++) {
970 ai = &ce->value.argInfo[i];
971 JS_INIT_CLIST(&ai->lruList);
972 for (j = 0; j < 10; j++)
973 JS_APPEND_LINK(&ai->topValCounts[j].lruLink, &ai->lruList);
974 }
975 strncpy(ce->name, name, sizeof ce->name);
976 }
977
978 ++ce->value.total;
979 if (ce->value.minargc < argc)
980 ce->value.minargc = argc;
981 if (ce->value.maxargc < argc)
982 ce->value.maxargc = argc;
983 if (argc > 8)
984 argc = 8;
985 for (i = 0; i < argc; i++) {
986 ai = &ce->value.argInfo[i];
987 argval = argv[i];
988 type = TYPEOF(cx, argval);
989 ++ai->typeHist[type];
990
991 for (j = 0; ; j++) {
992 if (j == 10) {
993 avc = (struct ArgValCount *) ai->lruList.next;
994 ce->value.recycled += avc->count;
995 avc->value = argval;
996 avc->count = 1;
997 break;
998 }
999 avc = &ai->topValCounts[j];
1000 if (avc->value == argval) {
1001 ++avc->count;
1002 break;
1003 }
1004 }
1005
1006 /* Move avc to the back of the LRU list. */
1007 JS_REMOVE_LINK(&avc->lruLink);
1008 JS_APPEND_LINK(&avc->lruLink, &ai->lruList);
1009
1010 str = NULL;
1011 cstr = "";
1012 switch (TYPEOF(cx, argval)) {
1013 case JSTYPE_VOID:
1014 cstr = js_type_str[JSTYPE_VOID];
1015 break;
1016 case JSTYPE_NULL:
1017 cstr = js_null_str;
1018 break;
1019 case JSTYPE_BOOLEAN:
1020 cstr = js_boolean_str[JSVAL_TO_BOOLEAN(argval)];
1021 break;
1022 case JSTYPE_NUMBER:
1023 if (JSVAL_IS_INT(argval)) {
1024 JS_snprintf(avc->strbuf, sizeof avc->strbuf, "%ld",
1025 JSVAL_TO_INT(argval));
1026 } else {
1027 JS_dtostr(avc->strbuf, sizeof avc->strbuf, DTOSTR_STANDARD, 0,
1028 *JSVAL_TO_DOUBLE(argval));
1029 }
1030 continue;
1031 case JSTYPE_STRING:
1032 str = js_QuoteString(cx, JSVAL_TO_STRING(argval), (jschar)'"');
1033 break;
1034 case JSTYPE_FUNCTION:
1035 if (VALUE_IS_FUNCTION(cx, argval)) {
1036 fun = (JSFunction *)JS_GetPrivate(cx, JSVAL_TO_OBJECT(argval));
1037 if (fun && fun->atom) {
1038 str = ATOM_TO_STRING(fun->atom);
1039 break;
1040 }
1041 }
1042 /* FALL THROUGH */
1043 case JSTYPE_OBJECT:
1044 js_LogCallToSourceLimit = sizeof avc->strbuf;
1045 cx->options |= JSOPTION_LOGCALL_TOSOURCE;
1046 str = js_ValueToSource(cx, argval);
1047 cx->options &= ~JSOPTION_LOGCALL_TOSOURCE;
1048 break;
1049 }
1050 if (str)
1051 cstr = JS_GetStringBytes(str);
1052 strncpy(avc->strbuf, cstr, sizeof avc->strbuf);
1053 }
1054 }
1055
1056 #endif /* DUMP_CALL_TABLE */
1057
1058 /*
1059 * Conditional assert to detect failure to clear a pending exception that is
1060 * suppressed (or unintentional suppression of a wanted exception).
1061 */
1062 #if defined DEBUG_brendan || defined DEBUG_mrbkap || defined DEBUG_shaver
1063 # define DEBUG_NOT_THROWING 1
1064 #endif
1065
1066 #ifdef DEBUG_NOT_THROWING
1067 # define ASSERT_NOT_THROWING(cx) JS_ASSERT(!(cx)->throwing)
1068 #else
1069 # define ASSERT_NOT_THROWING(cx) /* nothing */
1070 #endif
1071
1072 /*
1073 * Find a function reference and its 'this' object implicit first parameter
1074 * under argc arguments on cx's stack, and call the function. Push missing
1075 * required arguments, allocate declared local variables, and pop everything
1076 * when done. Then push the return value.
1077 */
1078 JS_FRIEND_API(JSBool)
js_Invoke(JSContext * cx,uintN argc,uintN flags)1079 js_Invoke(JSContext *cx, uintN argc, uintN flags)
1080 {
1081 void *mark;
1082 JSStackFrame *fp, frame;
1083 jsval *sp, *newsp, *limit;
1084 jsval *vp, v, thisv;
1085 JSObject *funobj, *parent, *thisp;
1086 JSBool ok;
1087 JSClass *clasp;
1088 JSObjectOps *ops;
1089 JSNative native;
1090 JSFunction *fun;
1091 JSScript *script;
1092 uintN nslots, nvars, nalloc, surplus;
1093 JSInterpreterHook hook;
1094 void *hookData;
1095
1096 /* Mark the top of stack and load frequently-used registers. */
1097 mark = JS_ARENA_MARK(&cx->stackPool);
1098 fp = cx->fp;
1099 sp = fp->sp;
1100
1101 /*
1102 * Set vp to the callee value's stack slot (it's where rval goes).
1103 * Once vp is set, control should flow through label out2: to return.
1104 * Set frame.rval early so native class and object ops can throw and
1105 * return false, causing a goto out2 with ok set to false.
1106 */
1107 vp = sp - (2 + argc);
1108 v = *vp;
1109 frame.rval = JSVAL_VOID;
1110
1111 /*
1112 * A callee must be an object reference, unless its 'this' parameter
1113 * implements the __noSuchMethod__ method, in which case that method will
1114 * be called like so:
1115 *
1116 * thisp.__noSuchMethod__(id, args)
1117 *
1118 * where id is the name of the method that this invocation attempted to
1119 * call by name, and args is an Array containing this invocation's actual
1120 * parameters.
1121 */
1122 if (JSVAL_IS_PRIMITIVE(v)) {
1123 #if JS_HAS_NO_SUCH_METHOD
1124 if (fp->script && !(flags & JSINVOKE_INTERNAL)) {
1125 ok = NoSuchMethod(cx, fp, vp, flags, argc);
1126 if (ok)
1127 frame.rval = *vp;
1128 goto out2;
1129 }
1130 #endif
1131 goto bad;
1132 }
1133
1134 /* Load thisv after potentially calling NoSuchMethod, which may set it. */
1135 thisv = vp[1];
1136
1137 funobj = JSVAL_TO_OBJECT(v);
1138 parent = OBJ_GET_PARENT(cx, funobj);
1139 clasp = OBJ_GET_CLASS(cx, funobj);
1140 if (clasp != &js_FunctionClass) {
1141 /* Function is inlined, all other classes use object ops. */
1142 ops = funobj->map->ops;
1143
1144 /*
1145 * XXX this makes no sense -- why convert to function if clasp->call?
1146 * XXX better to call that hook without converting
1147 * XXX the only thing that needs fixing is liveconnect
1148 *
1149 * Try converting to function, for closure and API compatibility.
1150 * We attempt the conversion under all circumstances for 1.2, but
1151 * only if there is a call op defined otherwise.
1152 */
1153 if ((ops == &js_ObjectOps) ? clasp->call : ops->call) {
1154 ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v);
1155 if (!ok)
1156 goto out2;
1157
1158 if (VALUE_IS_FUNCTION(cx, v)) {
1159 /* Make vp refer to funobj to keep it available as argv[-2]. */
1160 *vp = v;
1161 funobj = JSVAL_TO_OBJECT(v);
1162 parent = OBJ_GET_PARENT(cx, funobj);
1163 goto have_fun;
1164 }
1165 }
1166 fun = NULL;
1167 script = NULL;
1168 nslots = nvars = 0;
1169
1170 /* Try a call or construct native object op. */
1171 native = (flags & JSINVOKE_CONSTRUCT) ? ops->construct : ops->call;
1172 if (!native)
1173 goto bad;
1174
1175 if (JSVAL_IS_OBJECT(thisv)) {
1176 thisp = JSVAL_TO_OBJECT(thisv);
1177 } else {
1178 PRIMITIVE_TO_OBJECT(cx, thisv, thisp);
1179 if (!thisp)
1180 goto out2;
1181 vp[1] = thisv = OBJECT_TO_JSVAL(thisp);
1182 }
1183 } else {
1184 have_fun:
1185 /* Get private data and set derived locals from it. */
1186 fun = (JSFunction *) JS_GetPrivate(cx, funobj);
1187 nslots = (fun->nargs > argc) ? fun->nargs - argc : 0;
1188 if (FUN_INTERPRETED(fun)) {
1189 native = NULL;
1190 script = fun->u.i.script;
1191 nvars = fun->u.i.nvars;
1192 } else {
1193 native = fun->u.n.native;
1194 script = NULL;
1195 nvars = 0;
1196 nslots += fun->u.n.extra;
1197 }
1198
1199 if (JSFUN_BOUND_METHOD_TEST(fun->flags)) {
1200 /* Handle bound method special case. */
1201 thisp = parent;
1202 } else if (JSVAL_IS_OBJECT(thisv)) {
1203 thisp = JSVAL_TO_OBJECT(thisv);
1204 } else {
1205 uintN thispflags = JSFUN_THISP_FLAGS(fun->flags);
1206
1207 JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT));
1208 if (JSVAL_IS_STRING(thisv)) {
1209 if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_STRING)) {
1210 thisp = (JSObject *) thisv;
1211 goto init_frame;
1212 }
1213 thisp = js_StringToObject(cx, JSVAL_TO_STRING(thisv));
1214 } else if (JSVAL_IS_INT(thisv)) {
1215 if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_NUMBER)) {
1216 thisp = (JSObject *) thisv;
1217 goto init_frame;
1218 }
1219 thisp = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(thisv));
1220 } else if (JSVAL_IS_DOUBLE(thisv)) {
1221 if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_NUMBER)) {
1222 thisp = (JSObject *) thisv;
1223 goto init_frame;
1224 }
1225 thisp = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(thisv));
1226 } else {
1227 JS_ASSERT(JSVAL_IS_BOOLEAN(thisv));
1228 if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_BOOLEAN)) {
1229 thisp = (JSObject *) thisv;
1230 goto init_frame;
1231 }
1232 thisp = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(thisv));
1233 }
1234 if (!thisp) {
1235 ok = JS_FALSE;
1236 goto out2;
1237 }
1238 goto init_frame;
1239 }
1240 }
1241
1242 if (flags & JSINVOKE_CONSTRUCT) {
1243 /* Default return value for a constructor is the new object. */
1244 frame.rval = OBJECT_TO_JSVAL(thisp);
1245 } else {
1246 thisp = js_ComputeThis(cx, thisp, vp + 2);
1247 if (!thisp) {
1248 ok = JS_FALSE;
1249 goto out2;
1250 }
1251 }
1252
1253 init_frame:
1254 /* Initialize the rest of frame, except for sp (set by SAVE_SP later). */
1255 frame.thisp = thisp;
1256 frame.varobj = NULL;
1257 frame.callobj = frame.argsobj = NULL;
1258 frame.script = script;
1259 frame.fun = fun;
1260 frame.argc = argc;
1261 frame.argv = sp - argc;
1262 frame.nvars = nvars;
1263 frame.vars = sp;
1264 frame.down = fp;
1265 frame.annotation = NULL;
1266 frame.scopeChain = NULL; /* set below for real, after cx->fp is set */
1267 frame.pc = NULL;
1268 frame.spbase = NULL;
1269 frame.sharpDepth = 0;
1270 frame.sharpArray = NULL;
1271 frame.flags = flags;
1272 frame.dormantNext = NULL;
1273 frame.xmlNamespace = NULL;
1274 frame.blockChain = NULL;
1275
1276 /* From here on, control must flow through label out: to return. */
1277 cx->fp = &frame;
1278
1279 /* Init these now in case we goto out before first hook call. */
1280 hook = cx->runtime->callHook;
1281 hookData = NULL;
1282
1283 /* Check for argument slots required by the function. */
1284 if (nslots) {
1285 /* All arguments must be contiguous, so we may have to copy actuals. */
1286 nalloc = nslots;
1287 limit = (jsval *) cx->stackPool.current->limit;
1288 JS_ASSERT((jsval *) cx->stackPool.current->base <= sp && sp <= limit);
1289 if (sp + nslots > limit) {
1290 /* Hit end of arena: we have to copy argv[-2..(argc+nslots-1)]. */
1291 nalloc += 2 + argc;
1292 } else {
1293 /* Take advantage of surplus slots in the caller's frame depth. */
1294 JS_ASSERT((jsval *)mark >= sp);
1295 surplus = (jsval *)mark - sp;
1296 nalloc -= surplus;
1297 }
1298
1299 /* Check whether we have enough space in the caller's frame. */
1300 if ((intN)nalloc > 0) {
1301 /* Need space for actuals plus missing formals minus surplus. */
1302 newsp = js_AllocRawStack(cx, nalloc, NULL);
1303 if (!newsp) {
1304 ok = JS_FALSE;
1305 goto out;
1306 }
1307
1308 /* If we couldn't allocate contiguous args, copy actuals now. */
1309 if (newsp != mark) {
1310 JS_ASSERT(sp + nslots > limit);
1311 JS_ASSERT(2 + argc + nslots == nalloc);
1312 *newsp++ = vp[0];
1313 *newsp++ = vp[1];
1314 if (argc)
1315 memcpy(newsp, frame.argv, argc * sizeof(jsval));
1316 frame.argv = newsp;
1317 sp = frame.vars = newsp + argc;
1318 }
1319 }
1320
1321 /* Advance frame.vars to make room for the missing args. */
1322 frame.vars += nslots;
1323
1324 /* Push void to initialize missing args. */
1325 do {
1326 PUSH(JSVAL_VOID);
1327 } while (--nslots != 0);
1328 }
1329 JS_ASSERT(nslots == 0);
1330
1331 /* Now allocate stack space for local variables. */
1332 if (nvars) {
1333 JS_ASSERT((jsval *)cx->stackPool.current->avail >= frame.vars);
1334 surplus = (jsval *)cx->stackPool.current->avail - frame.vars;
1335 if (surplus < nvars) {
1336 newsp = js_AllocRawStack(cx, nvars, NULL);
1337 if (!newsp) {
1338 ok = JS_FALSE;
1339 goto out;
1340 }
1341 if (newsp != sp) {
1342 /* NB: Discontinuity between argv and vars. */
1343 sp = frame.vars = newsp;
1344 }
1345 }
1346
1347 /* Push void to initialize local variables. */
1348 do {
1349 PUSH(JSVAL_VOID);
1350 } while (--nvars != 0);
1351 }
1352 JS_ASSERT(nvars == 0);
1353
1354 /* Store the current sp in frame before calling fun. */
1355 SAVE_SP(&frame);
1356
1357 /* call the hook if present */
1358 if (hook && (native || script))
1359 hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->callHookData);
1360
1361 /* Call the function, either a native method or an interpreted script. */
1362 if (native) {
1363 #ifdef DEBUG_NOT_THROWING
1364 JSBool alreadyThrowing = cx->throwing;
1365 #endif
1366
1367 #if JS_HAS_LVALUE_RETURN
1368 /* Set by JS_SetCallReturnValue2, used to return reference types. */
1369 cx->rval2set = JS_FALSE;
1370 #endif
1371
1372 /* If native, use caller varobj and scopeChain for eval. */
1373 frame.varobj = fp->varobj;
1374 frame.scopeChain = fp->scopeChain;
1375 ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval);
1376 JS_RUNTIME_METER(cx->runtime, nativeCalls);
1377 #ifdef DEBUG_NOT_THROWING
1378 if (ok && !alreadyThrowing)
1379 ASSERT_NOT_THROWING(cx);
1380 #endif
1381 } else if (script) {
1382 #ifdef DUMP_CALL_TABLE
1383 LogCall(cx, *vp, argc, frame.argv);
1384 #endif
1385 /* Use parent scope so js_GetCallObject can find the right "Call". */
1386 frame.scopeChain = parent;
1387 if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) {
1388 /* Scope with a call object parented by the callee's parent. */
1389 if (!js_GetCallObject(cx, &frame, parent)) {
1390 ok = JS_FALSE;
1391 goto out;
1392 }
1393 }
1394 ok = js_Interpret(cx, script->code, &v);
1395 } else {
1396 /* fun might be onerror trying to report a syntax error in itself. */
1397 frame.scopeChain = NULL;
1398 ok = JS_TRUE;
1399 }
1400
1401 out:
1402 if (hookData) {
1403 hook = cx->runtime->callHook;
1404 if (hook)
1405 hook(cx, &frame, JS_FALSE, &ok, hookData);
1406 }
1407
1408 /* If frame has a call object, sync values and clear back-pointer. */
1409 if (frame.callobj)
1410 ok &= js_PutCallObject(cx, &frame);
1411
1412 /* If frame has an arguments object, sync values and clear back-pointer. */
1413 if (frame.argsobj)
1414 ok &= js_PutArgsObject(cx, &frame);
1415
1416 /* Restore cx->fp now that we're done releasing frame objects. */
1417 cx->fp = fp;
1418
1419 out2:
1420 /* Pop everything we may have allocated off the stack. */
1421 JS_ARENA_RELEASE(&cx->stackPool, mark);
1422
1423 /* Store the return value and restore sp just above it. */
1424 *vp = frame.rval;
1425 fp->sp = vp + 1;
1426
1427 /*
1428 * Store the location of the JSOP_CALL or JSOP_EVAL that generated the
1429 * return value, but only if this is an external (compiled from script
1430 * source) call that has stack budget for the generating pc.
1431 */
1432 if (fp->script && !(flags & JSINVOKE_INTERNAL))
1433 vp[-(intN)fp->script->depth] = (jsval)fp->pc;
1434 return ok;
1435
1436 bad:
1437 js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
1438 ok = JS_FALSE;
1439 goto out2;
1440 }
1441
1442 JSBool
js_InternalInvoke(JSContext * cx,JSObject * obj,jsval fval,uintN flags,uintN argc,jsval * argv,jsval * rval)1443 js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags,
1444 uintN argc, jsval *argv, jsval *rval)
1445 {
1446 JSStackFrame *fp, *oldfp, frame;
1447 jsval *oldsp, *sp;
1448 void *mark;
1449 uintN i;
1450 JSBool ok;
1451
1452 fp = oldfp = cx->fp;
1453 if (!fp) {
1454 memset(&frame, 0, sizeof frame);
1455 cx->fp = fp = &frame;
1456 }
1457 oldsp = fp->sp;
1458 sp = js_AllocStack(cx, 2 + argc, &mark);
1459 if (!sp) {
1460 ok = JS_FALSE;
1461 goto out;
1462 }
1463
1464 PUSH(fval);
1465 PUSH(OBJECT_TO_JSVAL(obj));
1466 for (i = 0; i < argc; i++)
1467 PUSH(argv[i]);
1468 SAVE_SP(fp);
1469 ok = js_Invoke(cx, argc, flags | JSINVOKE_INTERNAL);
1470 if (ok) {
1471 RESTORE_SP(fp);
1472
1473 /*
1474 * Store *rval in the a scoped local root if a scope is open, else in
1475 * the lastInternalResult pigeon-hole GC root, solely so users of
1476 * js_InternalInvoke and its direct and indirect (js_ValueToString for
1477 * example) callers do not need to manage roots for local, temporary
1478 * references to such results.
1479 */
1480 *rval = POP_OPND();
1481 if (JSVAL_IS_GCTHING(*rval)) {
1482 if (cx->localRootStack) {
1483 if (js_PushLocalRoot(cx, cx->localRootStack, *rval) < 0)
1484 ok = JS_FALSE;
1485 } else {
1486 cx->weakRoots.lastInternalResult = *rval;
1487 }
1488 }
1489 }
1490
1491 js_FreeStack(cx, mark);
1492 out:
1493 fp->sp = oldsp;
1494 if (oldfp != fp)
1495 cx->fp = oldfp;
1496
1497 return ok;
1498 }
1499
1500 JSBool
js_InternalGetOrSet(JSContext * cx,JSObject * obj,jsid id,jsval fval,JSAccessMode mode,uintN argc,jsval * argv,jsval * rval)1501 js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval,
1502 JSAccessMode mode, uintN argc, jsval *argv, jsval *rval)
1503 {
1504 int stackDummy;
1505
1506 /*
1507 * js_InternalInvoke could result in another try to get or set the same id
1508 * again, see bug 355497.
1509 */
1510 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
1511 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1512 JSMSG_OVER_RECURSED);
1513 return JS_FALSE;
1514 }
1515 /*
1516 * Check general (not object-ops/class-specific) access from the running
1517 * script to obj.id only if id has a scripted getter or setter that we're
1518 * about to invoke. If we don't check this case, nothing else will -- no
1519 * other native code has the chance to check.
1520 *
1521 * Contrast this non-native (scripted) case with native getter and setter
1522 * accesses, where the native itself must do an access check, if security
1523 * policies requires it. We make a checkAccess or checkObjectAccess call
1524 * back to the embedding program only in those cases where we're not going
1525 * to call an embedding-defined native function, getter, setter, or class
1526 * hook anyway. Where we do call such a native, there's no need for the
1527 * engine to impose a separate access check callback on all embeddings --
1528 * many embeddings have no security policy at all.
1529 */
1530 JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE);
1531 if (cx->runtime->checkObjectAccess &&
1532 VALUE_IS_FUNCTION(cx, fval) &&
1533 FUN_INTERPRETED((JSFunction *)
1534 JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval))) &&
1535 !cx->runtime->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode,
1536 &fval)) {
1537 return JS_FALSE;
1538 }
1539
1540 return js_InternalCall(cx, obj, fval, argc, argv, rval);
1541 }
1542
1543 JSBool
js_Execute(JSContext * cx,JSObject * chain,JSScript * script,JSStackFrame * down,uintN flags,jsval * result)1544 js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
1545 JSStackFrame *down, uintN flags, jsval *result)
1546 {
1547 JSInterpreterHook hook;
1548 void *hookData, *mark;
1549 JSStackFrame *oldfp, frame;
1550 JSObject *obj, *tmp;
1551 JSBool ok;
1552
1553 hook = cx->runtime->executeHook;
1554 hookData = mark = NULL;
1555 oldfp = cx->fp;
1556 frame.script = script;
1557 if (down) {
1558 /* Propagate arg/var state for eval and the debugger API. */
1559 frame.callobj = down->callobj;
1560 frame.argsobj = down->argsobj;
1561 frame.varobj = down->varobj;
1562 frame.fun = down->fun;
1563 frame.thisp = down->thisp;
1564 frame.argc = down->argc;
1565 frame.argv = down->argv;
1566 frame.nvars = down->nvars;
1567 frame.vars = down->vars;
1568 frame.annotation = down->annotation;
1569 frame.sharpArray = down->sharpArray;
1570 } else {
1571 frame.callobj = frame.argsobj = NULL;
1572 obj = chain;
1573 if (cx->options & JSOPTION_VAROBJFIX) {
1574 while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
1575 obj = tmp;
1576 }
1577 frame.varobj = obj;
1578 frame.fun = NULL;
1579 frame.thisp = chain;
1580 frame.argc = 0;
1581 frame.argv = NULL;
1582 frame.nvars = script->numGlobalVars;
1583 if (frame.nvars) {
1584 frame.vars = js_AllocRawStack(cx, frame.nvars, &mark);
1585 if (!frame.vars)
1586 return JS_FALSE;
1587 memset(frame.vars, 0, frame.nvars * sizeof(jsval));
1588 } else {
1589 frame.vars = NULL;
1590 }
1591 frame.annotation = NULL;
1592 frame.sharpArray = NULL;
1593 }
1594 frame.rval = JSVAL_VOID;
1595 frame.down = down;
1596 frame.scopeChain = chain;
1597 frame.pc = NULL;
1598 frame.sp = oldfp ? oldfp->sp : NULL;
1599 frame.spbase = NULL;
1600 frame.sharpDepth = 0;
1601 frame.flags = flags;
1602 frame.dormantNext = NULL;
1603 frame.xmlNamespace = NULL;
1604 frame.blockChain = NULL;
1605
1606 /*
1607 * Here we wrap the call to js_Interpret with code to (conditionally)
1608 * save and restore the old stack frame chain into a chain of 'dormant'
1609 * frame chains. Since we are replacing cx->fp, we were running into
1610 * the problem that if GC was called under this frame, some of the GC
1611 * things associated with the old frame chain (available here only in
1612 * the C variable 'oldfp') were not rooted and were being collected.
1613 *
1614 * So, now we preserve the links to these 'dormant' frame chains in cx
1615 * before calling js_Interpret and cleanup afterwards. The GC walks
1616 * these dormant chains and marks objects in the same way that it marks
1617 * objects in the primary cx->fp chain.
1618 */
1619 if (oldfp && oldfp != down) {
1620 JS_ASSERT(!oldfp->dormantNext);
1621 oldfp->dormantNext = cx->dormantFrameChain;
1622 cx->dormantFrameChain = oldfp;
1623 }
1624
1625 cx->fp = &frame;
1626 if (hook)
1627 hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->executeHookData);
1628
1629 /*
1630 * Use frame.rval, not result, so the last result stays rooted across any
1631 * GC activations nested within this js_Interpret.
1632 */
1633 ok = js_Interpret(cx, script->code, &frame.rval);
1634 *result = frame.rval;
1635
1636 if (hookData) {
1637 hook = cx->runtime->executeHook;
1638 if (hook)
1639 hook(cx, &frame, JS_FALSE, &ok, hookData);
1640 }
1641 if (mark)
1642 js_FreeRawStack(cx, mark);
1643 cx->fp = oldfp;
1644
1645 if (oldfp && oldfp != down) {
1646 JS_ASSERT(cx->dormantFrameChain == oldfp);
1647 cx->dormantFrameChain = oldfp->dormantNext;
1648 oldfp->dormantNext = NULL;
1649 }
1650
1651 return ok;
1652 }
1653
1654 #if JS_HAS_EXPORT_IMPORT
1655 /*
1656 * If id is JSVAL_VOID, import all exported properties from obj.
1657 */
1658 static JSBool
ImportProperty(JSContext * cx,JSObject * obj,jsid id)1659 ImportProperty(JSContext *cx, JSObject *obj, jsid id)
1660 {
1661 JSBool ok;
1662 JSIdArray *ida;
1663 JSProperty *prop;
1664 JSObject *obj2, *target, *funobj, *closure;
1665 JSString *str;
1666 uintN attrs;
1667 jsint i;
1668 jsval value;
1669
1670 if (JSVAL_IS_VOID(id)) {
1671 ida = JS_Enumerate(cx, obj);
1672 if (!ida)
1673 return JS_FALSE;
1674 ok = JS_TRUE;
1675 if (ida->length == 0)
1676 goto out;
1677 } else {
1678 ida = NULL;
1679 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1680 return JS_FALSE;
1681 if (!prop) {
1682 str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
1683 ID_TO_VALUE(id), NULL);
1684 if (str)
1685 js_ReportIsNotDefined(cx, JS_GetStringBytes(str));
1686 return JS_FALSE;
1687 }
1688 ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
1689 OBJ_DROP_PROPERTY(cx, obj2, prop);
1690 if (!ok)
1691 return JS_FALSE;
1692 if (!(attrs & JSPROP_EXPORTED)) {
1693 str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
1694 ID_TO_VALUE(id), NULL);
1695 if (str) {
1696 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1697 JSMSG_NOT_EXPORTED,
1698 JS_GetStringBytes(str));
1699 }
1700 return JS_FALSE;
1701 }
1702 }
1703
1704 target = cx->fp->varobj;
1705 i = 0;
1706 do {
1707 if (ida) {
1708 id = ida->vector[i];
1709 ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs);
1710 if (!ok)
1711 goto out;
1712 if (!(attrs & JSPROP_EXPORTED))
1713 continue;
1714 }
1715 ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_IMPORT, &value, &attrs);
1716 if (!ok)
1717 goto out;
1718 if (VALUE_IS_FUNCTION(cx, value)) {
1719 funobj = JSVAL_TO_OBJECT(value);
1720 closure = js_CloneFunctionObject(cx, funobj, obj);
1721 if (!closure) {
1722 ok = JS_FALSE;
1723 goto out;
1724 }
1725 value = OBJECT_TO_JSVAL(closure);
1726 }
1727
1728 /*
1729 * Handle the case of importing a property that refers to a local
1730 * variable or formal parameter of a function activation. These
1731 * properties are accessed by opcodes using stack slot numbers
1732 * generated by the compiler rather than runtime name-lookup. These
1733 * local references, therefore, bypass the normal scope chain lookup.
1734 * So, instead of defining a new property in the activation object,
1735 * modify the existing value in the stack slot.
1736 */
1737 if (OBJ_GET_CLASS(cx, target) == &js_CallClass) {
1738 ok = OBJ_LOOKUP_PROPERTY(cx, target, id, &obj2, &prop);
1739 if (!ok)
1740 goto out;
1741 } else {
1742 prop = NULL;
1743 }
1744 if (prop && target == obj2) {
1745 ok = OBJ_SET_PROPERTY(cx, target, id, &value);
1746 } else {
1747 ok = OBJ_DEFINE_PROPERTY(cx, target, id, value, NULL, NULL,
1748 attrs & ~(JSPROP_EXPORTED |
1749 JSPROP_GETTER |
1750 JSPROP_SETTER),
1751 NULL);
1752 }
1753 if (prop)
1754 OBJ_DROP_PROPERTY(cx, obj2, prop);
1755 if (!ok)
1756 goto out;
1757 } while (ida && ++i < ida->length);
1758
1759 out:
1760 if (ida)
1761 JS_DestroyIdArray(cx, ida);
1762 return ok;
1763 }
1764 #endif /* JS_HAS_EXPORT_IMPORT */
1765
1766 JSBool
js_CheckRedeclaration(JSContext * cx,JSObject * obj,jsid id,uintN attrs,JSObject ** objp,JSProperty ** propp)1767 js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
1768 JSObject **objp, JSProperty **propp)
1769 {
1770 JSObject *obj2;
1771 JSProperty *prop;
1772 uintN oldAttrs, report;
1773 JSBool isFunction;
1774 jsval value;
1775 const char *type, *name;
1776
1777 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1778 return JS_FALSE;
1779 if (propp) {
1780 *objp = obj2;
1781 *propp = prop;
1782 }
1783 if (!prop)
1784 return JS_TRUE;
1785
1786 /*
1787 * Use prop as a speedup hint to OBJ_GET_ATTRIBUTES, but drop it on error.
1788 * An assertion at label bad: will insist that it is null.
1789 */
1790 if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) {
1791 OBJ_DROP_PROPERTY(cx, obj2, prop);
1792 #ifdef DEBUG
1793 prop = NULL;
1794 #endif
1795 goto bad;
1796 }
1797
1798 /*
1799 * From here, return true, or else goto bad on failure to null out params.
1800 * If our caller doesn't want prop, drop it (we don't need it any longer).
1801 */
1802 if (!propp) {
1803 OBJ_DROP_PROPERTY(cx, obj2, prop);
1804 prop = NULL;
1805 }
1806
1807 /* If either property is readonly, we have an error. */
1808 report = ((oldAttrs | attrs) & JSPROP_READONLY)
1809 ? JSREPORT_ERROR
1810 : JSREPORT_WARNING | JSREPORT_STRICT;
1811
1812 if (report != JSREPORT_ERROR) {
1813 /*
1814 * Allow redeclaration of variables and functions, but insist that the
1815 * new value is not a getter if the old value was, ditto for setters --
1816 * unless prop is impermanent (in which case anyone could delete it and
1817 * redefine it, willy-nilly).
1818 */
1819 if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
1820 return JS_TRUE;
1821 if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0)
1822 return JS_TRUE;
1823 if (!(oldAttrs & JSPROP_PERMANENT))
1824 return JS_TRUE;
1825 report = JSREPORT_ERROR;
1826 }
1827
1828 isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
1829 if (!isFunction) {
1830 if (!OBJ_GET_PROPERTY(cx, obj, id, &value))
1831 goto bad;
1832 isFunction = VALUE_IS_FUNCTION(cx, value);
1833 }
1834 type = (oldAttrs & attrs & JSPROP_GETTER)
1835 ? js_getter_str
1836 : (oldAttrs & attrs & JSPROP_SETTER)
1837 ? js_setter_str
1838 : (oldAttrs & JSPROP_READONLY)
1839 ? js_const_str
1840 : isFunction
1841 ? js_function_str
1842 : js_var_str;
1843 name = js_AtomToPrintableString(cx, JSID_TO_ATOM(id));
1844 if (!name)
1845 goto bad;
1846 return JS_ReportErrorFlagsAndNumber(cx, report,
1847 js_GetErrorMessage, NULL,
1848 JSMSG_REDECLARED_VAR,
1849 type, name);
1850
1851 bad:
1852 if (propp) {
1853 *objp = NULL;
1854 *propp = NULL;
1855 }
1856 JS_ASSERT(!prop);
1857 return JS_FALSE;
1858 }
1859
1860 JSBool
js_StrictlyEqual(jsval lval,jsval rval)1861 js_StrictlyEqual(jsval lval, jsval rval)
1862 {
1863 jsval ltag = JSVAL_TAG(lval), rtag = JSVAL_TAG(rval);
1864 jsdouble ld, rd;
1865
1866 if (ltag == rtag) {
1867 if (ltag == JSVAL_STRING) {
1868 JSString *lstr = JSVAL_TO_STRING(lval),
1869 *rstr = JSVAL_TO_STRING(rval);
1870 return js_EqualStrings(lstr, rstr);
1871 }
1872 if (ltag == JSVAL_DOUBLE) {
1873 ld = *JSVAL_TO_DOUBLE(lval);
1874 rd = *JSVAL_TO_DOUBLE(rval);
1875 return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1876 }
1877 return lval == rval;
1878 }
1879 if (ltag == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) {
1880 ld = *JSVAL_TO_DOUBLE(lval);
1881 rd = JSVAL_TO_INT(rval);
1882 return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1883 }
1884 if (JSVAL_IS_INT(lval) && rtag == JSVAL_DOUBLE) {
1885 ld = JSVAL_TO_INT(lval);
1886 rd = *JSVAL_TO_DOUBLE(rval);
1887 return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1888 }
1889 return lval == rval;
1890 }
1891
1892 JSBool
js_InvokeConstructor(JSContext * cx,jsval * vp,uintN argc)1893 js_InvokeConstructor(JSContext *cx, jsval *vp, uintN argc)
1894 {
1895 JSFunction *fun;
1896 JSObject *obj, *obj2, *proto, *parent;
1897 jsval lval, rval;
1898 JSClass *clasp, *funclasp;
1899
1900 fun = NULL;
1901 obj2 = NULL;
1902 lval = *vp;
1903 if (!JSVAL_IS_OBJECT(lval) ||
1904 (obj2 = JSVAL_TO_OBJECT(lval)) == NULL ||
1905 /* XXX clean up to avoid special cases above ObjectOps layer */
1906 OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass ||
1907 !obj2->map->ops->construct)
1908 {
1909 fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT);
1910 if (!fun)
1911 return JS_FALSE;
1912 }
1913
1914 clasp = &js_ObjectClass;
1915 if (!obj2) {
1916 proto = parent = NULL;
1917 fun = NULL;
1918 } else {
1919 /*
1920 * Get the constructor prototype object for this function.
1921 * Use the nominal 'this' parameter slot, vp[1], as a local
1922 * root to protect this prototype, in case it has no other
1923 * strong refs.
1924 */
1925 if (!OBJ_GET_PROPERTY(cx, obj2,
1926 ATOM_TO_JSID(cx->runtime->atomState
1927 .classPrototypeAtom),
1928 &vp[1])) {
1929 return JS_FALSE;
1930 }
1931 rval = vp[1];
1932 proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL;
1933 parent = OBJ_GET_PARENT(cx, obj2);
1934
1935 if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) {
1936 funclasp = ((JSFunction *)JS_GetPrivate(cx, obj2))->clasp;
1937 if (funclasp)
1938 clasp = funclasp;
1939 }
1940 }
1941 obj = js_NewObject(cx, clasp, proto, parent);
1942 if (!obj)
1943 return JS_FALSE;
1944
1945 /* Now we have an object with a constructor method; call it. */
1946 vp[1] = OBJECT_TO_JSVAL(obj);
1947 if (!js_Invoke(cx, argc, JSINVOKE_CONSTRUCT)) {
1948 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
1949 return JS_FALSE;
1950 }
1951
1952 /* Check the return value and if it's primitive, force it to be obj. */
1953 rval = *vp;
1954 if (JSVAL_IS_PRIMITIVE(rval)) {
1955 if (!fun) {
1956 /* native [[Construct]] returning primitive is error */
1957 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1958 JSMSG_BAD_NEW_RESULT,
1959 js_ValueToPrintableString(cx, rval));
1960 return JS_FALSE;
1961 }
1962 *vp = OBJECT_TO_JSVAL(obj);
1963 }
1964
1965 JS_RUNTIME_METER(cx->runtime, constructs);
1966 return JS_TRUE;
1967 }
1968
1969 static JSBool
InternStringElementId(JSContext * cx,jsval idval,jsid * idp)1970 InternStringElementId(JSContext *cx, jsval idval, jsid *idp)
1971 {
1972 JSAtom *atom;
1973
1974 atom = js_ValueToStringAtom(cx, idval);
1975 if (!atom)
1976 return JS_FALSE;
1977 *idp = ATOM_TO_JSID(atom);
1978 return JS_TRUE;
1979 }
1980
1981 static JSBool
InternNonIntElementId(JSContext * cx,jsval idval,jsid * idp)1982 InternNonIntElementId(JSContext *cx, jsval idval, jsid *idp)
1983 {
1984 JS_ASSERT(!JSVAL_IS_INT(idval));
1985
1986 #if JS_HAS_XML_SUPPORT
1987 if (JSVAL_IS_OBJECT(idval)) {
1988 *idp = OBJECT_JSVAL_TO_JSID(idval);
1989 return JS_TRUE;
1990 }
1991 #endif
1992
1993 return InternStringElementId(cx, idval, idp);
1994 }
1995
1996 #if JS_HAS_XML_SUPPORT
1997 #define CHECK_ELEMENT_ID(obj, id) \
1998 JS_BEGIN_MACRO \
1999 if (JSID_IS_OBJECT(id) && !OBJECT_IS_XML(cx, obj)) { \
2000 SAVE_SP_AND_PC(fp); \
2001 ok = InternStringElementId(cx, OBJECT_JSID_TO_JSVAL(id), &id); \
2002 if (!ok) \
2003 goto out; \
2004 } \
2005 JS_END_MACRO
2006
2007 #else
2008 #define CHECK_ELEMENT_ID(obj, id) JS_ASSERT(!JSID_IS_OBJECT(id))
2009 #endif
2010
2011 #ifndef MAX_INTERP_LEVEL
2012 #if defined(XP_OS2)
2013 #define MAX_INTERP_LEVEL 250
2014 #else
2015 #define MAX_INTERP_LEVEL 1000
2016 #endif
2017 #endif
2018
2019 #define MAX_INLINE_CALL_COUNT 1000
2020
2021 /*
2022 * Threaded interpretation via computed goto appears to be well-supported by
2023 * GCC 3 and higher. IBM's C compiler when run with the right options (e.g.,
2024 * -qlanglvl=extended) also supports threading. Ditto the SunPro C compiler.
2025 * Currently it's broken for JS_VERSION < 160, though this isn't worth fixing.
2026 * Add your compiler support macros here.
2027 */
2028 #if JS_VERSION >= 160 && ( \
2029 __GNUC__ >= 3 || \
2030 (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \
2031 __SUNPRO_C >= 0x570)
2032 # define JS_THREADED_INTERP 1
2033 #else
2034 # undef JS_THREADED_INTERP
2035 #endif
2036
2037 JSBool
js_Interpret(JSContext * cx,jsbytecode * pc,jsval * result)2038 js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
2039 {
2040 JSRuntime *rt;
2041 JSStackFrame *fp;
2042 JSScript *script;
2043 uintN inlineCallCount;
2044 JSObject *obj, *obj2, *parent;
2045 JSVersion currentVersion, originalVersion;
2046 JSBranchCallback onbranch;
2047 JSBool ok, cond;
2048 JSTrapHandler interruptHandler;
2049 jsint depth, len;
2050 jsval *sp, *newsp;
2051 void *mark;
2052 jsbytecode *endpc, *pc2;
2053 JSOp op, op2;
2054 jsatomid atomIndex;
2055 JSAtom *atom;
2056 uintN argc, attrs, flags, slot;
2057 jsval *vp, lval, rval, ltmp, rtmp;
2058 jsid id;
2059 JSObject *withobj, *iterobj;
2060 JSProperty *prop;
2061 JSScopeProperty *sprop;
2062 JSString *str, *str2;
2063 jsint i, j;
2064 jsdouble d, d2;
2065 JSClass *clasp;
2066 JSFunction *fun;
2067 JSType type;
2068 #if !defined JS_THREADED_INTERP && defined DEBUG
2069 FILE *tracefp = NULL;
2070 #endif
2071 #if JS_HAS_EXPORT_IMPORT
2072 JSIdArray *ida;
2073 #endif
2074 jsint low, high, off, npairs;
2075 JSBool match;
2076 #if JS_HAS_GETTER_SETTER
2077 JSPropertyOp getter, setter;
2078 #endif
2079 int stackDummy;
2080
2081 #ifdef __GNUC__
2082 # define JS_EXTENSION __extension__
2083 # define JS_EXTENSION_(s) __extension__ ({ s; })
2084 #else
2085 # define JS_EXTENSION
2086 # define JS_EXTENSION_(s) s
2087 #endif
2088
2089 #ifdef JS_THREADED_INTERP
2090 static void *normalJumpTable[] = {
2091 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2092 JS_EXTENSION &&L_##op,
2093 # include "jsopcode.tbl"
2094 # undef OPDEF
2095 };
2096
2097 static void *interruptJumpTable[] = {
2098 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2099 ((op != JSOP_PUSHOBJ) \
2100 ? JS_EXTENSION &&interrupt \
2101 : JS_EXTENSION &&L_JSOP_PUSHOBJ),
2102 # include "jsopcode.tbl"
2103 # undef OPDEF
2104 };
2105
2106 register void **jumpTable = normalJumpTable;
2107
2108 # define DO_OP() JS_EXTENSION_(goto *jumpTable[op])
2109 # define DO_NEXT_OP(n) do { op = *(pc += (n)); DO_OP(); } while (0)
2110 # define BEGIN_CASE(OP) L_##OP:
2111 # define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH);
2112 # define END_VARLEN_CASE DO_NEXT_OP(len);
2113 # define EMPTY_CASE(OP) BEGIN_CASE(OP) op = *++pc; DO_OP();
2114 #else
2115 # define DO_OP() goto do_op
2116 # define DO_NEXT_OP(n) goto advance_pc
2117 # define BEGIN_CASE(OP) case OP:
2118 # define END_CASE(OP) break;
2119 # define END_VARLEN_CASE break;
2120 # define EMPTY_CASE(OP) BEGIN_CASE(OP) END_CASE(OP)
2121 #endif
2122
2123 *result = JSVAL_VOID;
2124 rt = cx->runtime;
2125
2126 /* Set registerized frame pointer and derived script pointer. */
2127 fp = cx->fp;
2128 script = fp->script;
2129 JS_ASSERT(script->length != 0);
2130
2131 /* Count of JS function calls that nest in this C js_Interpret frame. */
2132 inlineCallCount = 0;
2133
2134 /*
2135 * Optimized Get and SetVersion for proper script language versioning.
2136 *
2137 * If any native method or JSClass/JSObjectOps hook calls js_SetVersion
2138 * and changes cx->version, the effect will "stick" and we will stop
2139 * maintaining currentVersion. This is relied upon by testsuites, for
2140 * the most part -- web browsers select version before compiling and not
2141 * at run-time.
2142 */
2143 currentVersion = script->version;
2144 originalVersion = cx->version;
2145 if (currentVersion != originalVersion)
2146 js_SetVersion(cx, currentVersion);
2147
2148 #ifdef __GNUC__
2149 flags = 0; /* suppress gcc warnings */
2150 id = 0;
2151 #endif
2152
2153 /*
2154 * Prepare to call a user-supplied branch handler, and abort the script
2155 * if it returns false. We reload onbranch after calling out to native
2156 * functions (but not to getters, setters, or other native hooks).
2157 */
2158 #define LOAD_BRANCH_CALLBACK(cx) (onbranch = (cx)->branchCallback)
2159
2160 LOAD_BRANCH_CALLBACK(cx);
2161 #define CHECK_BRANCH(len) \
2162 JS_BEGIN_MACRO \
2163 if (len <= 0 && onbranch) { \
2164 SAVE_SP_AND_PC(fp); \
2165 if (!(ok = (*onbranch)(cx, script))) \
2166 goto out; \
2167 } \
2168 JS_END_MACRO
2169
2170 /*
2171 * Load the debugger's interrupt hook here and after calling out to native
2172 * functions (but not to getters, setters, or other native hooks), so we do
2173 * not have to reload it each time through the interpreter loop -- we hope
2174 * the compiler can keep it in a register when it is non-null.
2175 */
2176 #ifdef JS_THREADED_INTERP
2177 # define LOAD_JUMP_TABLE() \
2178 (jumpTable = interruptHandler ? interruptJumpTable : normalJumpTable)
2179 #else
2180 # define LOAD_JUMP_TABLE() /* nothing */
2181 #endif
2182
2183 #define LOAD_INTERRUPT_HANDLER(rt) \
2184 JS_BEGIN_MACRO \
2185 interruptHandler = (rt)->interruptHandler; \
2186 LOAD_JUMP_TABLE(); \
2187 JS_END_MACRO
2188
2189 LOAD_INTERRUPT_HANDLER(rt);
2190
2191 /* Check for too much js_Interpret nesting, or too deep a C stack. */
2192 if (++cx->interpLevel == MAX_INTERP_LEVEL ||
2193 !JS_CHECK_STACK_SIZE(cx, stackDummy)) {
2194 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
2195 ok = JS_FALSE;
2196 goto out2;
2197 }
2198
2199 /*
2200 * Allocate operand and pc stack slots for the script's worst-case depth,
2201 * unless we're called to interpret a part of an already active script, a
2202 * filtering predicate expression for example.
2203 */
2204 depth = (jsint) script->depth;
2205 if (JS_LIKELY(!fp->spbase)) {
2206 newsp = js_AllocRawStack(cx, (uintN)(2 * depth), &mark);
2207 if (!newsp) {
2208 ok = JS_FALSE;
2209 goto out2;
2210 }
2211 sp = newsp + depth;
2212 fp->spbase = sp;
2213 SAVE_SP(fp);
2214 } else {
2215 sp = fp->sp;
2216 JS_ASSERT(JS_UPTRDIFF(sp, fp->spbase) <= depth * sizeof(jsval));
2217 newsp = fp->spbase - depth;
2218 mark = NULL;
2219 }
2220
2221 /*
2222 * To support generator_throw and to catch ignored exceptions, fail right
2223 * away if cx->throwing is set. If no exception is pending, null obj in
2224 * case a callable object is being sent into a yield expression, and the
2225 * yield's result is invoked.
2226 */
2227 ok = !cx->throwing;
2228 if (!ok) {
2229 #ifdef DEBUG_NOT_THROWING
2230 printf("JS INTERPRETER CALLED WITH PENDING EXCEPTION %lx\n",
2231 (unsigned long) cx->exception);
2232 #endif
2233 goto out;
2234 }
2235 obj = NULL;
2236
2237 #ifdef JS_THREADED_INTERP
2238
2239 /*
2240 * This is a loop, but it does not look like a loop. The loop-closing
2241 * jump is distributed throughout interruptJumpTable, and comes back to
2242 * the interrupt label. The dispatch on op is through normalJumpTable.
2243 * The trick is LOAD_INTERRUPT_HANDLER setting jumpTable appropriately.
2244 *
2245 * It is important that "op" be initialized before the interrupt label
2246 * because it is possible for "op" to be specially assigned during the
2247 * normally processing of an opcode while looping (in particular, this
2248 * happens in JSOP_TRAP while debugging). We rely on DO_NEXT_OP to
2249 * correctly manage "op" in all other cases.
2250 */
2251 op = (JSOp) *pc;
2252 if (interruptHandler) {
2253 interrupt:
2254 SAVE_SP_AND_PC(fp);
2255 switch (interruptHandler(cx, script, pc, &rval,
2256 rt->interruptHandlerData)) {
2257 case JSTRAP_ERROR:
2258 ok = JS_FALSE;
2259 goto out;
2260 case JSTRAP_CONTINUE:
2261 break;
2262 case JSTRAP_RETURN:
2263 fp->rval = rval;
2264 goto out;
2265 case JSTRAP_THROW:
2266 cx->throwing = JS_TRUE;
2267 cx->exception = rval;
2268 ok = JS_FALSE;
2269 goto out;
2270 default:;
2271 }
2272 LOAD_INTERRUPT_HANDLER(rt);
2273 }
2274
2275 JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
2276 JS_EXTENSION_(goto *normalJumpTable[op]);
2277
2278 #else /* !JS_THREADED_INTERP */
2279
2280 for (;;) {
2281 op = (JSOp) *pc;
2282 do_op:
2283 len = js_CodeSpec[op].length;
2284
2285 #ifdef DEBUG
2286 tracefp = (FILE *) cx->tracefp;
2287 if (tracefp) {
2288 intN nuses, n;
2289
2290 fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, script, pc));
2291 js_Disassemble1(cx, script, pc,
2292 PTRDIFF(pc, script->code, jsbytecode), JS_FALSE,
2293 tracefp);
2294 nuses = js_CodeSpec[op].nuses;
2295 if (nuses) {
2296 SAVE_SP_AND_PC(fp);
2297 for (n = -nuses; n < 0; n++) {
2298 str = js_DecompileValueGenerator(cx, n, sp[n], NULL);
2299 if (str) {
2300 fprintf(tracefp, "%s %s",
2301 (n == -nuses) ? " inputs:" : ",",
2302 JS_GetStringBytes(str));
2303 }
2304 }
2305 fprintf(tracefp, " @ %d\n", sp - fp->spbase);
2306 }
2307 }
2308 #endif /* DEBUG */
2309
2310 if (interruptHandler && op != JSOP_PUSHOBJ) {
2311 SAVE_SP_AND_PC(fp);
2312 switch (interruptHandler(cx, script, pc, &rval,
2313 rt->interruptHandlerData)) {
2314 case JSTRAP_ERROR:
2315 ok = JS_FALSE;
2316 goto out;
2317 case JSTRAP_CONTINUE:
2318 break;
2319 case JSTRAP_RETURN:
2320 fp->rval = rval;
2321 goto out;
2322 case JSTRAP_THROW:
2323 cx->throwing = JS_TRUE;
2324 cx->exception = rval;
2325 ok = JS_FALSE;
2326 goto out;
2327 default:;
2328 }
2329 LOAD_INTERRUPT_HANDLER(rt);
2330 }
2331
2332 switch (op) {
2333
2334 #endif /* !JS_THREADED_INTERP */
2335
2336 BEGIN_CASE(JSOP_STOP)
2337 goto out;
2338
2339 EMPTY_CASE(JSOP_NOP)
2340
2341 BEGIN_CASE(JSOP_GROUP)
2342 obj = NULL;
2343 END_CASE(JSOP_GROUP)
2344
2345 BEGIN_CASE(JSOP_PUSH)
2346 PUSH_OPND(JSVAL_VOID);
2347 END_CASE(JSOP_PUSH)
2348
2349 BEGIN_CASE(JSOP_POP)
2350 sp--;
2351 END_CASE(JSOP_POP)
2352
2353 BEGIN_CASE(JSOP_POP2)
2354 sp -= 2;
2355 END_CASE(JSOP_POP2)
2356
2357 BEGIN_CASE(JSOP_SWAP)
2358 vp = sp - depth; /* swap generating pc's for the decompiler */
2359 ltmp = vp[-1];
2360 vp[-1] = vp[-2];
2361 sp[-2] = ltmp;
2362 rtmp = sp[-1];
2363 sp[-1] = sp[-2];
2364 sp[-2] = rtmp;
2365 END_CASE(JSOP_SWAP)
2366
2367 BEGIN_CASE(JSOP_POPV)
2368 *result = POP_OPND();
2369 END_CASE(JSOP_POPV)
2370
2371 BEGIN_CASE(JSOP_ENTERWITH)
2372 FETCH_OBJECT(cx, -1, rval, obj);
2373 SAVE_SP_AND_PC(fp);
2374 OBJ_TO_INNER_OBJECT(cx, obj);
2375 if (!obj || !(obj2 = js_GetScopeChain(cx, fp))) {
2376 ok = JS_FALSE;
2377 goto out;
2378 }
2379 withobj = js_NewWithObject(cx, obj, obj2, sp - fp->spbase - 1);
2380 if (!withobj) {
2381 ok = JS_FALSE;
2382 goto out;
2383 }
2384 fp->scopeChain = withobj;
2385 STORE_OPND(-1, OBJECT_TO_JSVAL(withobj));
2386 END_CASE(JSOP_ENTERWITH)
2387
2388 BEGIN_CASE(JSOP_LEAVEWITH)
2389 rval = POP_OPND();
2390 JS_ASSERT(JSVAL_IS_OBJECT(rval));
2391 withobj = JSVAL_TO_OBJECT(rval);
2392 JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass);
2393 fp->scopeChain = OBJ_GET_PARENT(cx, withobj);
2394 JS_SetPrivate(cx, withobj, NULL);
2395 END_CASE(JSOP_LEAVEWITH)
2396
2397 BEGIN_CASE(JSOP_SETRVAL)
2398 ASSERT_NOT_THROWING(cx);
2399 fp->rval = POP_OPND();
2400 END_CASE(JSOP_SETRVAL)
2401
2402 BEGIN_CASE(JSOP_RETURN)
2403 CHECK_BRANCH(-1);
2404 fp->rval = POP_OPND();
2405 /* FALL THROUGH */
2406
2407 BEGIN_CASE(JSOP_RETRVAL) /* fp->rval already set */
2408 ASSERT_NOT_THROWING(cx);
2409 if (inlineCallCount)
2410 inline_return:
2411 {
2412 JSInlineFrame *ifp = (JSInlineFrame *) fp;
2413 void *hookData = ifp->hookData;
2414
2415 /*
2416 * If fp has blocks on its scope chain, home their locals now,
2417 * before calling any debugger hook, and before freeing stack.
2418 * This matches the order of block putting and hook calling in
2419 * the "out-of-line" return code at the bottom of js_Interpret
2420 * and in js_Invoke.
2421 */
2422 if (fp->flags & JSFRAME_POP_BLOCKS) {
2423 SAVE_SP_AND_PC(fp);
2424 ok &= PutBlockObjects(cx, fp);
2425 }
2426
2427 if (hookData) {
2428 JSInterpreterHook hook = rt->callHook;
2429 if (hook) {
2430 SAVE_SP_AND_PC(fp);
2431 hook(cx, fp, JS_FALSE, &ok, hookData);
2432 LOAD_INTERRUPT_HANDLER(rt);
2433 }
2434 }
2435
2436 /*
2437 * If fp has a call object, sync values and clear the back-
2438 * pointer. This can happen for a lightweight function if it
2439 * calls eval unexpectedly (in a way that is hidden from the
2440 * compiler). See bug 325540.
2441 */
2442 if (fp->callobj) {
2443 SAVE_SP_AND_PC(fp);
2444 ok &= js_PutCallObject(cx, fp);
2445 }
2446
2447 if (fp->argsobj) {
2448 SAVE_SP_AND_PC(fp);
2449 ok &= js_PutArgsObject(cx, fp);
2450 }
2451
2452 /* Restore context version only if callee hasn't set version. */
2453 if (JS_LIKELY(cx->version == currentVersion)) {
2454 currentVersion = ifp->callerVersion;
2455 if (currentVersion != cx->version)
2456 js_SetVersion(cx, currentVersion);
2457 }
2458
2459 /* Store the return value in the caller's operand frame. */
2460 vp = ifp->rvp;
2461 *vp = fp->rval;
2462
2463 /* Restore cx->fp and release the inline frame's space. */
2464 cx->fp = fp = fp->down;
2465 JS_ARENA_RELEASE(&cx->stackPool, ifp->mark);
2466
2467 /* Restore sp to point just above the return value. */
2468 fp->sp = vp + 1;
2469 RESTORE_SP(fp);
2470
2471 /* Restore the calling script's interpreter registers. */
2472 obj = NULL;
2473 script = fp->script;
2474 depth = (jsint) script->depth;
2475 pc = fp->pc;
2476 #ifndef JS_THREADED_INTERP
2477 endpc = script->code + script->length;
2478 #endif
2479
2480 /* Store the generating pc for the return value. */
2481 vp[-depth] = (jsval)pc;
2482
2483 /* Resume execution in the calling frame. */
2484 inlineCallCount--;
2485 if (JS_LIKELY(ok)) {
2486 JS_ASSERT(js_CodeSpec[*pc].length == JSOP_CALL_LENGTH);
2487 len = JSOP_CALL_LENGTH;
2488 DO_NEXT_OP(len);
2489 }
2490 }
2491 goto out;
2492
2493 BEGIN_CASE(JSOP_DEFAULT)
2494 (void) POP();
2495 /* FALL THROUGH */
2496 BEGIN_CASE(JSOP_GOTO)
2497 len = GET_JUMP_OFFSET(pc);
2498 CHECK_BRANCH(len);
2499 END_VARLEN_CASE
2500
2501 BEGIN_CASE(JSOP_IFEQ)
2502 POP_BOOLEAN(cx, rval, cond);
2503 if (cond == JS_FALSE) {
2504 len = GET_JUMP_OFFSET(pc);
2505 CHECK_BRANCH(len);
2506 DO_NEXT_OP(len);
2507 }
2508 END_CASE(JSOP_IFEQ)
2509
2510 BEGIN_CASE(JSOP_IFNE)
2511 POP_BOOLEAN(cx, rval, cond);
2512 if (cond != JS_FALSE) {
2513 len = GET_JUMP_OFFSET(pc);
2514 CHECK_BRANCH(len);
2515 DO_NEXT_OP(len);
2516 }
2517 END_CASE(JSOP_IFNE)
2518
2519 BEGIN_CASE(JSOP_OR)
2520 POP_BOOLEAN(cx, rval, cond);
2521 if (cond == JS_TRUE) {
2522 len = GET_JUMP_OFFSET(pc);
2523 PUSH_OPND(rval);
2524 DO_NEXT_OP(len);
2525 }
2526 END_CASE(JSOP_OR)
2527
2528 BEGIN_CASE(JSOP_AND)
2529 POP_BOOLEAN(cx, rval, cond);
2530 if (cond == JS_FALSE) {
2531 len = GET_JUMP_OFFSET(pc);
2532 PUSH_OPND(rval);
2533 DO_NEXT_OP(len);
2534 }
2535 END_CASE(JSOP_AND)
2536
2537 BEGIN_CASE(JSOP_DEFAULTX)
2538 (void) POP();
2539 /* FALL THROUGH */
2540 BEGIN_CASE(JSOP_GOTOX)
2541 len = GET_JUMPX_OFFSET(pc);
2542 CHECK_BRANCH(len);
2543 END_VARLEN_CASE
2544
2545 BEGIN_CASE(JSOP_IFEQX)
2546 POP_BOOLEAN(cx, rval, cond);
2547 if (cond == JS_FALSE) {
2548 len = GET_JUMPX_OFFSET(pc);
2549 CHECK_BRANCH(len);
2550 DO_NEXT_OP(len);
2551 }
2552 END_CASE(JSOP_IFEQX)
2553
2554 BEGIN_CASE(JSOP_IFNEX)
2555 POP_BOOLEAN(cx, rval, cond);
2556 if (cond != JS_FALSE) {
2557 len = GET_JUMPX_OFFSET(pc);
2558 CHECK_BRANCH(len);
2559 DO_NEXT_OP(len);
2560 }
2561 END_CASE(JSOP_IFNEX)
2562
2563 BEGIN_CASE(JSOP_ORX)
2564 POP_BOOLEAN(cx, rval, cond);
2565 if (cond == JS_TRUE) {
2566 len = GET_JUMPX_OFFSET(pc);
2567 PUSH_OPND(rval);
2568 DO_NEXT_OP(len);
2569 }
2570 END_CASE(JSOP_ORX)
2571
2572 BEGIN_CASE(JSOP_ANDX)
2573 POP_BOOLEAN(cx, rval, cond);
2574 if (cond == JS_FALSE) {
2575 len = GET_JUMPX_OFFSET(pc);
2576 PUSH_OPND(rval);
2577 DO_NEXT_OP(len);
2578 }
2579 END_CASE(JSOP_ANDX)
2580
2581 /*
2582 * If the index value at sp[n] is not an int that fits in a jsval, it could
2583 * be an object (an XML QName, AttributeName, or AnyName), but only if we are
2584 * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a
2585 * string atom id.
2586 */
2587 #define FETCH_ELEMENT_ID(n, id) \
2588 JS_BEGIN_MACRO \
2589 jsval idval_ = FETCH_OPND(n); \
2590 if (JSVAL_IS_INT(idval_)) { \
2591 id = INT_JSVAL_TO_JSID(idval_); \
2592 } else { \
2593 SAVE_SP_AND_PC(fp); \
2594 ok = InternNonIntElementId(cx, idval_, &id); \
2595 if (!ok) \
2596 goto out; \
2597 } \
2598 JS_END_MACRO
2599
2600 BEGIN_CASE(JSOP_IN)
2601 SAVE_SP_AND_PC(fp);
2602 rval = FETCH_OPND(-1);
2603 if (JSVAL_IS_PRIMITIVE(rval)) {
2604 str = js_DecompileValueGenerator(cx, -1, rval, NULL);
2605 if (str) {
2606 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2607 JSMSG_IN_NOT_OBJECT,
2608 JS_GetStringBytes(str));
2609 }
2610 ok = JS_FALSE;
2611 goto out;
2612 }
2613 obj = JSVAL_TO_OBJECT(rval);
2614 FETCH_ELEMENT_ID(-2, id);
2615 CHECK_ELEMENT_ID(obj, id);
2616 ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
2617 if (!ok)
2618 goto out;
2619 sp--;
2620 STORE_OPND(-1, BOOLEAN_TO_JSVAL(prop != NULL));
2621 if (prop)
2622 OBJ_DROP_PROPERTY(cx, obj2, prop);
2623 END_CASE(JSOP_IN)
2624
2625 BEGIN_CASE(JSOP_FOREACH)
2626 flags = JSITER_ENUMERATE | JSITER_FOREACH;
2627 goto value_to_iter;
2628
2629 #if JS_HAS_DESTRUCTURING
2630 BEGIN_CASE(JSOP_FOREACHKEYVAL)
2631 flags = JSITER_ENUMERATE | JSITER_FOREACH | JSITER_KEYVALUE;
2632 goto value_to_iter;
2633 #endif
2634
2635 BEGIN_CASE(JSOP_FORIN)
2636 /*
2637 * Set JSITER_ENUMERATE to indicate that for-in loop should use
2638 * the enumeration protocol's iterator for compatibility if an
2639 * explicit iterator is not given via the optional __iterator__
2640 * method.
2641 */
2642 flags = JSITER_ENUMERATE;
2643
2644 value_to_iter:
2645 JS_ASSERT(sp > fp->spbase);
2646 SAVE_SP_AND_PC(fp);
2647 ok = js_ValueToIterator(cx, flags, &sp[-1]);
2648 if (!ok)
2649 goto out;
2650 JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-1]));
2651 JS_ASSERT(JSOP_FORIN_LENGTH == js_CodeSpec[op].length);
2652 END_CASE(JSOP_FORIN)
2653
2654 BEGIN_CASE(JSOP_FORPROP)
2655 /*
2656 * Handle JSOP_FORPROP first, so the cost of the goto do_forinloop
2657 * is not paid for the more common cases.
2658 */
2659 lval = FETCH_OPND(-1);
2660 atom = GET_ATOM(cx, script, pc);
2661 id = ATOM_TO_JSID(atom);
2662 i = -2;
2663 goto do_forinloop;
2664
2665 BEGIN_CASE(JSOP_FORNAME)
2666 atom = GET_ATOM(cx, script, pc);
2667 id = ATOM_TO_JSID(atom);
2668
2669 /*
2670 * ECMA 12.6.3 says to eval the LHS after looking for properties
2671 * to enumerate, and bail without LHS eval if there are no props.
2672 * We do Find here to share the most code at label do_forinloop.
2673 * If looking for enumerable properties could have side effects,
2674 * then we'd have to move this into the common code and condition
2675 * it on op == JSOP_FORNAME.
2676 */
2677 SAVE_SP_AND_PC(fp);
2678 ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
2679 if (!ok)
2680 goto out;
2681 if (prop)
2682 OBJ_DROP_PROPERTY(cx, obj2, prop);
2683 lval = OBJECT_TO_JSVAL(obj);
2684 /* FALL THROUGH */
2685
2686 BEGIN_CASE(JSOP_FORARG)
2687 BEGIN_CASE(JSOP_FORVAR)
2688 BEGIN_CASE(JSOP_FORLOCAL)
2689 /*
2690 * JSOP_FORARG and JSOP_FORVAR don't require any lval computation
2691 * here, because they address slots on the stack (in fp->args and
2692 * fp->vars, respectively). Same applies to JSOP_FORLOCAL, which
2693 * addresses fp->spbase.
2694 */
2695 /* FALL THROUGH */
2696
2697 BEGIN_CASE(JSOP_FORELEM)
2698 /*
2699 * JSOP_FORELEM simply initializes or updates the iteration state
2700 * and leaves the index expression evaluation and assignment to the
2701 * enumerator until after the next property has been acquired, via
2702 * a JSOP_ENUMELEM bytecode.
2703 */
2704 i = -1;
2705
2706 do_forinloop:
2707 /*
2708 * Reach under the top of stack to find our property iterator, a
2709 * JSObject that contains the iteration state.
2710 */
2711 JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[i]));
2712 iterobj = JSVAL_TO_OBJECT(sp[i]);
2713
2714 SAVE_SP_AND_PC(fp);
2715 ok = js_CallIteratorNext(cx, iterobj, &rval);
2716 if (!ok)
2717 goto out;
2718 if (rval == JSVAL_HOLE) {
2719 rval = JSVAL_FALSE;
2720 goto end_forinloop;
2721 }
2722
2723 switch (op) {
2724 case JSOP_FORARG:
2725 slot = GET_ARGNO(pc);
2726 JS_ASSERT(slot < fp->fun->nargs);
2727 fp->argv[slot] = rval;
2728 break;
2729
2730 case JSOP_FORVAR:
2731 slot = GET_VARNO(pc);
2732 JS_ASSERT(slot < fp->fun->u.i.nvars);
2733 fp->vars[slot] = rval;
2734 break;
2735
2736 case JSOP_FORLOCAL:
2737 slot = GET_UINT16(pc);
2738 JS_ASSERT(slot < (uintN)depth);
2739 vp = &fp->spbase[slot];
2740 GC_POKE(cx, *vp);
2741 *vp = rval;
2742 break;
2743
2744 case JSOP_FORELEM:
2745 /* FORELEM is not a SET operation, it's more like BINDNAME. */
2746 PUSH_OPND(rval);
2747 break;
2748
2749 default:
2750 JS_ASSERT(op == JSOP_FORPROP || op == JSOP_FORNAME);
2751
2752 /* Convert lval to a non-null object containing id. */
2753 VALUE_TO_OBJECT(cx, lval, obj);
2754 if (op == JSOP_FORPROP)
2755 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
2756
2757 /* Set the variable obj[id] to refer to rval. */
2758 fp->flags |= JSFRAME_ASSIGNING;
2759 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
2760 fp->flags &= ~JSFRAME_ASSIGNING;
2761 if (!ok)
2762 goto out;
2763 break;
2764 }
2765
2766 /* Push true to keep looping through properties. */
2767 rval = JSVAL_TRUE;
2768
2769 end_forinloop:
2770 sp += i + 1;
2771 PUSH_OPND(rval);
2772 len = js_CodeSpec[op].length;
2773 DO_NEXT_OP(len);
2774
2775 BEGIN_CASE(JSOP_DUP)
2776 JS_ASSERT(sp > fp->spbase);
2777 vp = sp - 1; /* address top of stack */
2778 rval = *vp;
2779 vp -= depth; /* address generating pc */
2780 vp[1] = *vp;
2781 PUSH(rval);
2782 END_CASE(JSOP_DUP)
2783
2784 BEGIN_CASE(JSOP_DUP2)
2785 JS_ASSERT(sp - 2 >= fp->spbase);
2786 vp = sp - 1; /* address top of stack */
2787 lval = vp[-1];
2788 rval = *vp;
2789 vp -= depth; /* address generating pc */
2790 vp[1] = vp[2] = *vp;
2791 PUSH(lval);
2792 PUSH(rval);
2793 END_CASE(JSOP_DUP2)
2794
2795 #define PROPERTY_OP(n, call) \
2796 JS_BEGIN_MACRO \
2797 /* Fetch the left part and resolve it to a non-null object. */ \
2798 FETCH_OBJECT(cx, n, lval, obj); \
2799 \
2800 /* Get or set the property, set ok false if error, true if success. */\
2801 SAVE_SP_AND_PC(fp); \
2802 call; \
2803 if (!ok) \
2804 goto out; \
2805 JS_END_MACRO
2806
2807 #define ELEMENT_OP(n, call) \
2808 JS_BEGIN_MACRO \
2809 /* Fetch the right part and resolve it to an internal id. */ \
2810 FETCH_ELEMENT_ID(n, id); \
2811 \
2812 /* Fetch the left part and resolve it to a non-null object. */ \
2813 FETCH_OBJECT(cx, n - 1, lval, obj); \
2814 \
2815 /* Ensure that id has a type suitable for use with obj. */ \
2816 CHECK_ELEMENT_ID(obj, id); \
2817 \
2818 /* Get or set the element, set ok false if error, true if success. */ \
2819 SAVE_SP_AND_PC(fp); \
2820 call; \
2821 if (!ok) \
2822 goto out; \
2823 JS_END_MACRO
2824
2825 #define NATIVE_GET(cx,obj,pobj,sprop,vp) \
2826 JS_BEGIN_MACRO \
2827 if (SPROP_HAS_STUB_GETTER(sprop)) { \
2828 /* Fast path for Object instance properties. */ \
2829 JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT || \
2830 !SPROP_HAS_STUB_SETTER(sprop)); \
2831 *vp = ((sprop)->slot != SPROP_INVALID_SLOT) \
2832 ? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot) \
2833 : JSVAL_VOID; \
2834 } else { \
2835 SAVE_SP_AND_PC(fp); \
2836 ok = js_NativeGet(cx, obj, pobj, sprop, vp); \
2837 if (!ok) \
2838 goto out; \
2839 } \
2840 JS_END_MACRO
2841
2842 #define NATIVE_SET(cx,obj,sprop,vp) \
2843 JS_BEGIN_MACRO \
2844 if (SPROP_HAS_STUB_SETTER(sprop) && \
2845 (sprop)->slot != SPROP_INVALID_SLOT) { \
2846 /* Fast path for Object instance properties. */ \
2847 LOCKED_OBJ_SET_SLOT(obj, (sprop)->slot, *vp); \
2848 } else { \
2849 SAVE_SP_AND_PC(fp); \
2850 ok = js_NativeSet(cx, obj, sprop, vp); \
2851 if (!ok) \
2852 goto out; \
2853 } \
2854 JS_END_MACRO
2855
2856 /*
2857 * CACHED_GET and CACHED_SET use cx, obj, id, and rval from their callers'
2858 * environments.
2859 */
2860 #define CACHED_GET(call) \
2861 JS_BEGIN_MACRO \
2862 if (!OBJ_IS_NATIVE(obj)) { \
2863 ok = call; \
2864 } else { \
2865 JS_LOCK_OBJ(cx, obj); \
2866 PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); \
2867 if (sprop) { \
2868 NATIVE_GET(cx, obj, obj, sprop, &rval); \
2869 JS_UNLOCK_OBJ(cx, obj); \
2870 } else { \
2871 JS_UNLOCK_OBJ(cx, obj); \
2872 ok = call; \
2873 /* No fill here: js_GetProperty fills the cache. */ \
2874 } \
2875 } \
2876 JS_END_MACRO
2877
2878 #define CACHED_SET(call) \
2879 JS_BEGIN_MACRO \
2880 if (!OBJ_IS_NATIVE(obj)) { \
2881 ok = call; \
2882 } else { \
2883 JSScope *scope_; \
2884 JS_LOCK_OBJ(cx, obj); \
2885 PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); \
2886 if (sprop && \
2887 !(sprop->attrs & JSPROP_READONLY) && \
2888 (scope_ = OBJ_SCOPE(obj), !SCOPE_IS_SEALED(scope_))) { \
2889 NATIVE_SET(cx, obj, sprop, &rval); \
2890 JS_UNLOCK_SCOPE(cx, scope_); \
2891 } else { \
2892 JS_UNLOCK_OBJ(cx, obj); \
2893 ok = call; \
2894 /* No fill here: js_SetProperty writes through the cache. */ \
2895 } \
2896 } \
2897 JS_END_MACRO
2898
2899 #define BEGIN_LITOPX_CASE(OP,PCOFF) \
2900 BEGIN_CASE(OP) \
2901 pc2 = pc; \
2902 atomIndex = GET_ATOM_INDEX(pc + PCOFF); \
2903 do_##OP: \
2904 atom = js_GetAtom(cx, &script->atomMap, atomIndex);
2905
2906 #define END_LITOPX_CASE(OP) \
2907 END_CASE(OP)
2908
2909 BEGIN_LITOPX_CASE(JSOP_SETCONST, 0)
2910 obj = fp->varobj;
2911 rval = FETCH_OPND(-1);
2912 SAVE_SP_AND_PC(fp);
2913 ok = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval,
2914 NULL, NULL,
2915 JSPROP_ENUMERATE | JSPROP_PERMANENT |
2916 JSPROP_READONLY,
2917 NULL);
2918 if (!ok)
2919 goto out;
2920 STORE_OPND(-1, rval);
2921 END_LITOPX_CASE(JSOP_SETCONST)
2922
2923 #if JS_HAS_DESTRUCTURING
2924 BEGIN_CASE(JSOP_ENUMCONSTELEM)
2925 FETCH_ELEMENT_ID(-1, id);
2926 FETCH_OBJECT(cx, -2, lval, obj);
2927 CHECK_ELEMENT_ID(obj, id);
2928 rval = FETCH_OPND(-3);
2929 SAVE_SP_AND_PC(fp);
2930 ok = OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL,
2931 JSPROP_ENUMERATE | JSPROP_PERMANENT |
2932 JSPROP_READONLY,
2933 NULL);
2934 if (!ok)
2935 goto out;
2936 sp -= 3;
2937 END_CASE(JSOP_ENUMCONSTELEM)
2938 #endif
2939
2940 BEGIN_LITOPX_CASE(JSOP_BINDNAME, 0)
2941 SAVE_SP_AND_PC(fp);
2942 obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom));
2943 if (!obj) {
2944 ok = JS_FALSE;
2945 goto out;
2946 }
2947 PUSH_OPND(OBJECT_TO_JSVAL(obj));
2948 END_LITOPX_CASE(JSOP_BINDNAME)
2949
2950 BEGIN_CASE(JSOP_SETNAME)
2951 atom = GET_ATOM(cx, script, pc);
2952 id = ATOM_TO_JSID(atom);
2953 rval = FETCH_OPND(-1);
2954 lval = FETCH_OPND(-2);
2955 JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval));
2956 obj = JSVAL_TO_OBJECT(lval);
2957 SAVE_SP_AND_PC(fp);
2958 CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval));
2959 if (!ok)
2960 goto out;
2961 sp--;
2962 STORE_OPND(-1, rval);
2963 obj = NULL;
2964 END_CASE(JSOP_SETNAME)
2965
2966 #define INTEGER_OP(OP, EXTRA_CODE) \
2967 JS_BEGIN_MACRO \
2968 FETCH_INT(cx, -1, j); \
2969 FETCH_INT(cx, -2, i); \
2970 EXTRA_CODE \
2971 i = i OP j; \
2972 sp--; \
2973 STORE_INT(cx, -1, i); \
2974 JS_END_MACRO
2975
2976 #define BITWISE_OP(OP) INTEGER_OP(OP, (void) 0;)
2977 #define SIGNED_SHIFT_OP(OP) INTEGER_OP(OP, j &= 31;)
2978
2979 BEGIN_CASE(JSOP_BITOR)
2980 BITWISE_OP(|);
2981 END_CASE(JSOP_BITOR)
2982
2983 BEGIN_CASE(JSOP_BITXOR)
2984 BITWISE_OP(^);
2985 END_CASE(JSOP_BITXOR)
2986
2987 BEGIN_CASE(JSOP_BITAND)
2988 BITWISE_OP(&);
2989 END_CASE(JSOP_BITAND)
2990
2991 #define RELATIONAL_OP(OP) \
2992 JS_BEGIN_MACRO \
2993 rval = FETCH_OPND(-1); \
2994 lval = FETCH_OPND(-2); \
2995 /* Optimize for two int-tagged operands (typical loop control). */ \
2996 if ((lval & rval) & JSVAL_INT) { \
2997 ltmp = lval ^ JSVAL_VOID; \
2998 rtmp = rval ^ JSVAL_VOID; \
2999 if (ltmp && rtmp) { \
3000 cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval); \
3001 } else { \
3002 d = ltmp ? JSVAL_TO_INT(lval) : *rt->jsNaN; \
3003 d2 = rtmp ? JSVAL_TO_INT(rval) : *rt->jsNaN; \
3004 cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \
3005 } \
3006 } else { \
3007 VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_NUMBER, &lval); \
3008 sp[-2] = lval; \
3009 VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_NUMBER, &rval); \
3010 if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { \
3011 str = JSVAL_TO_STRING(lval); \
3012 str2 = JSVAL_TO_STRING(rval); \
3013 cond = js_CompareStrings(str, str2) OP 0; \
3014 } else { \
3015 VALUE_TO_NUMBER(cx, lval, d); \
3016 VALUE_TO_NUMBER(cx, rval, d2); \
3017 cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \
3018 } \
3019 } \
3020 sp--; \
3021 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
3022 JS_END_MACRO
3023
3024 /*
3025 * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies
3026 * because they begin if/else chains, so callers must not put semicolons after
3027 * the call expressions!
3028 */
3029 #if JS_HAS_XML_SUPPORT
3030 #define XML_EQUALITY_OP(OP) \
3031 if ((ltmp == JSVAL_OBJECT && \
3032 (obj2 = JSVAL_TO_OBJECT(lval)) && \
3033 OBJECT_IS_XML(cx, obj2)) || \
3034 (rtmp == JSVAL_OBJECT && \
3035 (obj2 = JSVAL_TO_OBJECT(rval)) && \
3036 OBJECT_IS_XML(cx, obj2))) { \
3037 JSXMLObjectOps *ops; \
3038 \
3039 ops = (JSXMLObjectOps *) obj2->map->ops; \
3040 if (obj2 == JSVAL_TO_OBJECT(rval)) \
3041 rval = lval; \
3042 SAVE_SP_AND_PC(fp); \
3043 ok = ops->equality(cx, obj2, rval, &cond); \
3044 if (!ok) \
3045 goto out; \
3046 cond = cond OP JS_TRUE; \
3047 } else
3048
3049 #define EXTENDED_EQUALITY_OP(OP) \
3050 if (ltmp == JSVAL_OBJECT && \
3051 (obj2 = JSVAL_TO_OBJECT(lval)) && \
3052 ((clasp = OBJ_GET_CLASS(cx, obj2))->flags & JSCLASS_IS_EXTENDED)) { \
3053 JSExtendedClass *xclasp; \
3054 \
3055 xclasp = (JSExtendedClass *) clasp; \
3056 SAVE_SP_AND_PC(fp); \
3057 ok = xclasp->equality(cx, obj2, rval, &cond); \
3058 if (!ok) \
3059 goto out; \
3060 cond = cond OP JS_TRUE; \
3061 } else
3062 #else
3063 #define XML_EQUALITY_OP(OP) /* nothing */
3064 #define EXTENDED_EQUALITY_OP(OP) /* nothing */
3065 #endif
3066
3067 #define EQUALITY_OP(OP, IFNAN) \
3068 JS_BEGIN_MACRO \
3069 rval = FETCH_OPND(-1); \
3070 lval = FETCH_OPND(-2); \
3071 ltmp = JSVAL_TAG(lval); \
3072 rtmp = JSVAL_TAG(rval); \
3073 XML_EQUALITY_OP(OP) \
3074 if (ltmp == rtmp) { \
3075 if (ltmp == JSVAL_STRING) { \
3076 str = JSVAL_TO_STRING(lval); \
3077 str2 = JSVAL_TO_STRING(rval); \
3078 cond = js_EqualStrings(str, str2) OP JS_TRUE; \
3079 } else if (ltmp == JSVAL_DOUBLE) { \
3080 d = *JSVAL_TO_DOUBLE(lval); \
3081 d2 = *JSVAL_TO_DOUBLE(rval); \
3082 cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \
3083 } else { \
3084 EXTENDED_EQUALITY_OP(OP) \
3085 /* Handle all undefined (=>NaN) and int combinations. */ \
3086 cond = lval OP rval; \
3087 } \
3088 } else { \
3089 if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) { \
3090 cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) OP 1; \
3091 } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) { \
3092 cond = 1 OP 0; \
3093 } else { \
3094 if (ltmp == JSVAL_OBJECT) { \
3095 VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &sp[-2]); \
3096 lval = sp[-2]; \
3097 ltmp = JSVAL_TAG(lval); \
3098 } else if (rtmp == JSVAL_OBJECT) { \
3099 VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &sp[-1]); \
3100 rval = sp[-1]; \
3101 rtmp = JSVAL_TAG(rval); \
3102 } \
3103 if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) { \
3104 str = JSVAL_TO_STRING(lval); \
3105 str2 = JSVAL_TO_STRING(rval); \
3106 cond = js_EqualStrings(str, str2) OP JS_TRUE; \
3107 } else { \
3108 VALUE_TO_NUMBER(cx, lval, d); \
3109 VALUE_TO_NUMBER(cx, rval, d2); \
3110 cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \
3111 } \
3112 } \
3113 } \
3114 sp--; \
3115 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
3116 JS_END_MACRO
3117
3118 BEGIN_CASE(JSOP_EQ)
3119 EQUALITY_OP(==, JS_FALSE);
3120 END_CASE(JSOP_EQ)
3121
3122 BEGIN_CASE(JSOP_NE)
3123 EQUALITY_OP(!=, JS_TRUE);
3124 END_CASE(JSOP_NE)
3125
3126 #define NEW_EQUALITY_OP(OP) \
3127 JS_BEGIN_MACRO \
3128 rval = FETCH_OPND(-1); \
3129 lval = FETCH_OPND(-2); \
3130 cond = js_StrictlyEqual(lval, rval) OP JS_TRUE; \
3131 sp--; \
3132 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
3133 JS_END_MACRO
3134
3135 BEGIN_CASE(JSOP_NEW_EQ)
3136 NEW_EQUALITY_OP(==);
3137 END_CASE(JSOP_NEW_EQ)
3138
3139 BEGIN_CASE(JSOP_NEW_NE)
3140 NEW_EQUALITY_OP(!=);
3141 END_CASE(JSOP_NEW_NE)
3142
3143 BEGIN_CASE(JSOP_CASE)
3144 pc2 = (jsbytecode *) sp[-2-depth];
3145 NEW_EQUALITY_OP(==);
3146 (void) POP();
3147 if (cond) {
3148 len = GET_JUMP_OFFSET(pc);
3149 CHECK_BRANCH(len);
3150 DO_NEXT_OP(len);
3151 }
3152 sp[-depth] = (jsval)pc2;
3153 PUSH(lval);
3154 END_CASE(JSOP_CASE)
3155
3156 BEGIN_CASE(JSOP_CASEX)
3157 pc2 = (jsbytecode *) sp[-2-depth];
3158 NEW_EQUALITY_OP(==);
3159 (void) POP();
3160 if (cond) {
3161 len = GET_JUMPX_OFFSET(pc);
3162 CHECK_BRANCH(len);
3163 DO_NEXT_OP(len);
3164 }
3165 sp[-depth] = (jsval)pc2;
3166 PUSH(lval);
3167 END_CASE(JSOP_CASEX)
3168
3169 BEGIN_CASE(JSOP_LT)
3170 RELATIONAL_OP(<);
3171 END_CASE(JSOP_LT)
3172
3173 BEGIN_CASE(JSOP_LE)
3174 RELATIONAL_OP(<=);
3175 END_CASE(JSOP_LE)
3176
3177 BEGIN_CASE(JSOP_GT)
3178 RELATIONAL_OP(>);
3179 END_CASE(JSOP_GT)
3180
3181 BEGIN_CASE(JSOP_GE)
3182 RELATIONAL_OP(>=);
3183 END_CASE(JSOP_GE)
3184
3185 #undef EQUALITY_OP
3186 #undef RELATIONAL_OP
3187
3188 BEGIN_CASE(JSOP_LSH)
3189 SIGNED_SHIFT_OP(<<);
3190 END_CASE(JSOP_LSH)
3191
3192 BEGIN_CASE(JSOP_RSH)
3193 SIGNED_SHIFT_OP(>>);
3194 END_CASE(JSOP_RSH)
3195
3196 BEGIN_CASE(JSOP_URSH)
3197 {
3198 uint32 u;
3199
3200 FETCH_INT(cx, -1, j);
3201 FETCH_UINT(cx, -2, u);
3202 u >>= j & 31;
3203 sp--;
3204 STORE_UINT(cx, -1, u);
3205 }
3206 END_CASE(JSOP_URSH)
3207
3208 #undef INTEGER_OP
3209 #undef BITWISE_OP
3210 #undef SIGNED_SHIFT_OP
3211
3212 BEGIN_CASE(JSOP_ADD)
3213 rval = FETCH_OPND(-1);
3214 lval = FETCH_OPND(-2);
3215 #if JS_HAS_XML_SUPPORT
3216 if (!JSVAL_IS_PRIMITIVE(lval) &&
3217 (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) &&
3218 VALUE_IS_XML(cx, rval)) {
3219 JSXMLObjectOps *ops;
3220
3221 ops = (JSXMLObjectOps *) obj2->map->ops;
3222 SAVE_SP_AND_PC(fp);
3223 ok = ops->concatenate(cx, obj2, rval, &rval);
3224 if (!ok)
3225 goto out;
3226 sp--;
3227 STORE_OPND(-1, rval);
3228 } else
3229 #endif
3230 {
3231 VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &sp[-2]);
3232 lval = sp[-2];
3233 VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &sp[-1]);
3234 rval = sp[-1];
3235 if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) {
3236 SAVE_SP_AND_PC(fp);
3237 if (cond) {
3238 str = JSVAL_TO_STRING(lval);
3239 ok = (str2 = js_ValueToString(cx, rval)) != NULL;
3240 if (!ok)
3241 goto out;
3242 sp[-1] = STRING_TO_JSVAL(str2);
3243 } else {
3244 str2 = JSVAL_TO_STRING(rval);
3245 ok = (str = js_ValueToString(cx, lval)) != NULL;
3246 if (!ok)
3247 goto out;
3248 sp[-2] = STRING_TO_JSVAL(str);
3249 }
3250 str = js_ConcatStrings(cx, str, str2);
3251 if (!str) {
3252 ok = JS_FALSE;
3253 goto out;
3254 }
3255 sp--;
3256 STORE_OPND(-1, STRING_TO_JSVAL(str));
3257 } else {
3258 VALUE_TO_NUMBER(cx, lval, d);
3259 VALUE_TO_NUMBER(cx, rval, d2);
3260 d += d2;
3261 sp--;
3262 STORE_NUMBER(cx, -1, d);
3263 }
3264 }
3265 END_CASE(JSOP_ADD)
3266
3267 #define BINARY_OP(OP) \
3268 JS_BEGIN_MACRO \
3269 FETCH_NUMBER(cx, -1, d2); \
3270 FETCH_NUMBER(cx, -2, d); \
3271 d = d OP d2; \
3272 sp--; \
3273 STORE_NUMBER(cx, -1, d); \
3274 JS_END_MACRO
3275
3276 BEGIN_CASE(JSOP_SUB)
3277 BINARY_OP(-);
3278 END_CASE(JSOP_SUB)
3279
3280 BEGIN_CASE(JSOP_MUL)
3281 BINARY_OP(*);
3282 END_CASE(JSOP_MUL)
3283
3284 BEGIN_CASE(JSOP_DIV)
3285 FETCH_NUMBER(cx, -1, d2);
3286 FETCH_NUMBER(cx, -2, d);
3287 sp--;
3288 if (d2 == 0) {
3289 #ifdef XP_WIN
3290 /* XXX MSVC miscompiles such that (NaN == 0) */
3291 if (JSDOUBLE_IS_NaN(d2))
3292 rval = DOUBLE_TO_JSVAL(rt->jsNaN);
3293 else
3294 #endif
3295 if (d == 0 || JSDOUBLE_IS_NaN(d))
3296 rval = DOUBLE_TO_JSVAL(rt->jsNaN);
3297 else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
3298 rval = DOUBLE_TO_JSVAL(rt->jsNegativeInfinity);
3299 else
3300 rval = DOUBLE_TO_JSVAL(rt->jsPositiveInfinity);
3301 STORE_OPND(-1, rval);
3302 } else {
3303 d /= d2;
3304 STORE_NUMBER(cx, -1, d);
3305 }
3306 END_CASE(JSOP_DIV)
3307
3308 BEGIN_CASE(JSOP_MOD)
3309 FETCH_NUMBER(cx, -1, d2);
3310 FETCH_NUMBER(cx, -2, d);
3311 sp--;
3312 if (d2 == 0) {
3313 STORE_OPND(-1, DOUBLE_TO_JSVAL(rt->jsNaN));
3314 } else {
3315 #ifdef XP_WIN
3316 /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
3317 if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))
3318 #endif
3319 d = fmod(d, d2);
3320 STORE_NUMBER(cx, -1, d);
3321 }
3322 END_CASE(JSOP_MOD)
3323
3324 BEGIN_CASE(JSOP_NOT)
3325 POP_BOOLEAN(cx, rval, cond);
3326 PUSH_OPND(BOOLEAN_TO_JSVAL(!cond));
3327 END_CASE(JSOP_NOT)
3328
3329 BEGIN_CASE(JSOP_BITNOT)
3330 FETCH_INT(cx, -1, i);
3331 i = ~i;
3332 STORE_INT(cx, -1, i);
3333 END_CASE(JSOP_BITNOT)
3334
3335 BEGIN_CASE(JSOP_NEG)
3336 /*
3337 * Optimize the case of an int-tagged operand by noting that
3338 * INT_FITS_IN_JSVAL(i) => INT_FITS_IN_JSVAL(-i) unless i is 0
3339 * when -i is the negative zero which is jsdouble.
3340 */
3341 rval = FETCH_OPND(-1);
3342 if (JSVAL_IS_INT(rval) && (i = JSVAL_TO_INT(rval)) != 0) {
3343 i = -i;
3344 JS_ASSERT(INT_FITS_IN_JSVAL(i));
3345 rval = INT_TO_JSVAL(i);
3346 } else {
3347 if (JSVAL_IS_DOUBLE(rval)) {
3348 d = *JSVAL_TO_DOUBLE(rval);
3349 } else {
3350 SAVE_SP_AND_PC(fp);
3351 ok = js_ValueToNumber(cx, rval, &d);
3352 if (!ok)
3353 goto out;
3354 }
3355 #ifdef HPUX
3356 /*
3357 * Negation of a zero doesn't produce a negative
3358 * zero on HPUX. Perform the operation by bit
3359 * twiddling.
3360 */
3361 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
3362 #else
3363 d = -d;
3364 #endif
3365 ok = js_NewNumberValue(cx, d, &rval);
3366 if (!ok)
3367 goto out;
3368 }
3369 STORE_OPND(-1, rval);
3370 END_CASE(JSOP_NEG)
3371
3372 BEGIN_CASE(JSOP_POS)
3373 rval = FETCH_OPND(-1);
3374 if (!JSVAL_IS_NUMBER(rval)) {
3375 SAVE_SP_AND_PC(fp);
3376 ok = js_ValueToNumber(cx, rval, &d);
3377 if (!ok)
3378 goto out;
3379 ok = js_NewNumberValue(cx, d, &rval);
3380 if (!ok)
3381 goto out;
3382 sp[-1] = rval;
3383 }
3384 sp[-1-depth] = (jsval)pc;
3385 END_CASE(JSOP_POS)
3386
3387 BEGIN_CASE(JSOP_NEW)
3388 /* Get immediate argc and find the constructor function. */
3389 argc = GET_ARGC(pc);
3390
3391 do_new:
3392 SAVE_SP_AND_PC(fp);
3393 vp = sp - (2 + argc);
3394 JS_ASSERT(vp >= fp->spbase);
3395
3396 ok = js_InvokeConstructor(cx, vp, argc);
3397 if (!ok)
3398 goto out;
3399 RESTORE_SP(fp);
3400 LOAD_BRANCH_CALLBACK(cx);
3401 LOAD_INTERRUPT_HANDLER(rt);
3402 obj = JSVAL_TO_OBJECT(*vp);
3403 len = js_CodeSpec[op].length;
3404 DO_NEXT_OP(len);
3405
3406 BEGIN_CASE(JSOP_DELNAME)
3407 atom = GET_ATOM(cx, script, pc);
3408 id = ATOM_TO_JSID(atom);
3409
3410 SAVE_SP_AND_PC(fp);
3411 ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
3412 if (!ok)
3413 goto out;
3414
3415 /* ECMA says to return true if name is undefined or inherited. */
3416 rval = JSVAL_TRUE;
3417 if (prop) {
3418 OBJ_DROP_PROPERTY(cx, obj2, prop);
3419 ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval);
3420 if (!ok)
3421 goto out;
3422 }
3423 PUSH_OPND(rval);
3424 END_CASE(JSOP_DELNAME)
3425
3426 BEGIN_CASE(JSOP_DELPROP)
3427 atom = GET_ATOM(cx, script, pc);
3428 id = ATOM_TO_JSID(atom);
3429 PROPERTY_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval));
3430 STORE_OPND(-1, rval);
3431 END_CASE(JSOP_DELPROP)
3432
3433 BEGIN_CASE(JSOP_DELELEM)
3434 ELEMENT_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval));
3435 sp--;
3436 STORE_OPND(-1, rval);
3437 END_CASE(JSOP_DELELEM)
3438
3439 BEGIN_CASE(JSOP_TYPEOFEXPR)
3440 BEGIN_CASE(JSOP_TYPEOF)
3441 rval = FETCH_OPND(-1);
3442 SAVE_SP_AND_PC(fp);
3443 type = JS_TypeOfValue(cx, rval);
3444 atom = rt->atomState.typeAtoms[type];
3445 STORE_OPND(-1, ATOM_KEY(atom));
3446 END_CASE(JSOP_TYPEOF)
3447
3448 BEGIN_CASE(JSOP_VOID)
3449 (void) POP_OPND();
3450 PUSH_OPND(JSVAL_VOID);
3451 END_CASE(JSOP_VOID)
3452
3453 BEGIN_CASE(JSOP_INCNAME)
3454 BEGIN_CASE(JSOP_DECNAME)
3455 BEGIN_CASE(JSOP_NAMEINC)
3456 BEGIN_CASE(JSOP_NAMEDEC)
3457 atom = GET_ATOM(cx, script, pc);
3458 id = ATOM_TO_JSID(atom);
3459
3460 SAVE_SP_AND_PC(fp);
3461 ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
3462 if (!ok)
3463 goto out;
3464 if (!prop)
3465 goto atom_not_defined;
3466
3467 OBJ_DROP_PROPERTY(cx, obj2, prop);
3468 lval = OBJECT_TO_JSVAL(obj);
3469 i = 0;
3470 goto do_incop;
3471
3472 BEGIN_CASE(JSOP_INCPROP)
3473 BEGIN_CASE(JSOP_DECPROP)
3474 BEGIN_CASE(JSOP_PROPINC)
3475 BEGIN_CASE(JSOP_PROPDEC)
3476 atom = GET_ATOM(cx, script, pc);
3477 id = ATOM_TO_JSID(atom);
3478 lval = FETCH_OPND(-1);
3479 i = -1;
3480 goto do_incop;
3481
3482 BEGIN_CASE(JSOP_INCELEM)
3483 BEGIN_CASE(JSOP_DECELEM)
3484 BEGIN_CASE(JSOP_ELEMINC)
3485 BEGIN_CASE(JSOP_ELEMDEC)
3486 FETCH_ELEMENT_ID(-1, id);
3487 lval = FETCH_OPND(-2);
3488 i = -2;
3489
3490 do_incop:
3491 {
3492 const JSCodeSpec *cs;
3493
3494 VALUE_TO_OBJECT(cx, lval, obj);
3495 if (i < 0)
3496 STORE_OPND(i, OBJECT_TO_JSVAL(obj));
3497 CHECK_ELEMENT_ID(obj, id);
3498
3499 /* The operand must contain a number. */
3500 SAVE_SP_AND_PC(fp);
3501 CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval));
3502 if (!ok)
3503 goto out;
3504
3505 /* Preload for use in the if/else immediately below. */
3506 cs = &js_CodeSpec[op];
3507
3508 /* The expression result goes in rtmp, the updated value in rval. */
3509 if (JSVAL_IS_INT(rval) &&
3510 rval != INT_TO_JSVAL(JSVAL_INT_MIN) &&
3511 rval != INT_TO_JSVAL(JSVAL_INT_MAX)) {
3512 if (cs->format & JOF_POST) {
3513 rtmp = rval;
3514 (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2);
3515 } else {
3516 (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2);
3517 rtmp = rval;
3518 }
3519 } else {
3520
3521 /*
3522 * Initially, rval contains the value to increment or decrement, which is not
3523 * yet converted. As above, the expression result goes in rtmp, the updated
3524 * value goes in rval. Our caller must set vp to point at a GC-rooted jsval
3525 * in which we home rtmp, to protect it from GC in case the unconverted rval
3526 * is not a number.
3527 */
3528 #define NONINT_INCREMENT_OP_MIDDLE() \
3529 JS_BEGIN_MACRO \
3530 VALUE_TO_NUMBER(cx, rval, d); \
3531 if (cs->format & JOF_POST) { \
3532 rtmp = rval; \
3533 if (!JSVAL_IS_NUMBER(rtmp)) { \
3534 ok = js_NewNumberValue(cx, d, &rtmp); \
3535 if (!ok) \
3536 goto out; \
3537 } \
3538 *vp = rtmp; \
3539 (cs->format & JOF_INC) ? d++ : d--; \
3540 ok = js_NewNumberValue(cx, d, &rval); \
3541 } else { \
3542 (cs->format & JOF_INC) ? ++d : --d; \
3543 ok = js_NewNumberValue(cx, d, &rval); \
3544 rtmp = rval; \
3545 } \
3546 if (!ok) \
3547 goto out; \
3548 JS_END_MACRO
3549
3550 if (cs->format & JOF_POST) {
3551 /*
3552 * We must push early to protect the postfix increment
3553 * or decrement result, if converted to a jsdouble from
3554 * a non-number value, from GC nesting in the setter.
3555 */
3556 vp = sp;
3557 PUSH(JSVAL_VOID);
3558 SAVE_SP(fp);
3559 --i;
3560 }
3561 #ifdef __GNUC__
3562 else vp = NULL; /* suppress bogus gcc warnings */
3563 #endif
3564
3565 NONINT_INCREMENT_OP_MIDDLE();
3566 }
3567
3568 fp->flags |= JSFRAME_ASSIGNING;
3569 CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval));
3570 fp->flags &= ~JSFRAME_ASSIGNING;
3571 if (!ok)
3572 goto out;
3573 sp += i;
3574 PUSH_OPND(rtmp);
3575 len = js_CodeSpec[op].length;
3576 DO_NEXT_OP(len);
3577 }
3578
3579 /* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */
3580 #define FAST_INCREMENT_OP(SLOT,COUNT,BASE,PRE,OPEQ,MINMAX) \
3581 slot = SLOT; \
3582 JS_ASSERT(slot < fp->fun->COUNT); \
3583 vp = fp->BASE + slot; \
3584 rval = *vp; \
3585 if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \
3586 goto do_nonint_fast_incop; \
3587 PRE = rval; \
3588 rval OPEQ 2; \
3589 *vp = rval; \
3590 PUSH_OPND(PRE); \
3591 goto end_nonint_fast_incop
3592
3593 BEGIN_CASE(JSOP_INCARG)
3594 FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, +=, MAX);
3595 BEGIN_CASE(JSOP_DECARG)
3596 FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, -=, MIN);
3597 BEGIN_CASE(JSOP_ARGINC)
3598 FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, +=, MAX);
3599 BEGIN_CASE(JSOP_ARGDEC)
3600 FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, -=, MIN);
3601
3602 BEGIN_CASE(JSOP_INCVAR)
3603 FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rval, +=, MAX);
3604 BEGIN_CASE(JSOP_DECVAR)
3605 FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rval, -=, MIN);
3606 BEGIN_CASE(JSOP_VARINC)
3607 FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rtmp, +=, MAX);
3608 BEGIN_CASE(JSOP_VARDEC)
3609 FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rtmp, -=, MIN);
3610
3611 end_nonint_fast_incop:
3612 len = JSOP_INCARG_LENGTH; /* all fast incops are same length */
3613 DO_NEXT_OP(len);
3614
3615 #undef FAST_INCREMENT_OP
3616
3617 do_nonint_fast_incop:
3618 {
3619 const JSCodeSpec *cs = &js_CodeSpec[op];
3620
3621 NONINT_INCREMENT_OP_MIDDLE();
3622 *vp = rval;
3623 PUSH_OPND(rtmp);
3624 len = cs->length;
3625 DO_NEXT_OP(len);
3626 }
3627
3628 /* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */
3629 #define FAST_GLOBAL_INCREMENT_OP(SLOWOP,PRE,OPEQ,MINMAX) \
3630 slot = GET_VARNO(pc); \
3631 JS_ASSERT(slot < fp->nvars); \
3632 lval = fp->vars[slot]; \
3633 if (JSVAL_IS_NULL(lval)) { \
3634 op = SLOWOP; \
3635 DO_OP(); \
3636 } \
3637 slot = JSVAL_TO_INT(lval); \
3638 obj = fp->varobj; \
3639 rval = OBJ_GET_SLOT(cx, obj, slot); \
3640 if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \
3641 goto do_nonint_fast_global_incop; \
3642 PRE = rval; \
3643 rval OPEQ 2; \
3644 OBJ_SET_SLOT(cx, obj, slot, rval); \
3645 PUSH_OPND(PRE); \
3646 goto end_nonint_fast_global_incop
3647
3648 BEGIN_CASE(JSOP_INCGVAR)
3649 FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, rval, +=, MAX);
3650 BEGIN_CASE(JSOP_DECGVAR)
3651 FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, rval, -=, MIN);
3652 BEGIN_CASE(JSOP_GVARINC)
3653 FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, rtmp, +=, MAX);
3654 BEGIN_CASE(JSOP_GVARDEC)
3655 FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, rtmp, -=, MIN);
3656
3657 end_nonint_fast_global_incop:
3658 len = JSOP_INCGVAR_LENGTH; /* all gvar incops are same length */
3659 JS_ASSERT(len == js_CodeSpec[op].length);
3660 DO_NEXT_OP(len);
3661
3662 #undef FAST_GLOBAL_INCREMENT_OP
3663
3664 do_nonint_fast_global_incop:
3665 {
3666 const JSCodeSpec *cs = &js_CodeSpec[op];
3667
3668 vp = sp++;
3669 SAVE_SP(fp);
3670 NONINT_INCREMENT_OP_MIDDLE();
3671 OBJ_SET_SLOT(cx, obj, slot, rval);
3672 STORE_OPND(-1, rtmp);
3673 len = cs->length;
3674 DO_NEXT_OP(len);
3675 }
3676
3677 BEGIN_CASE(JSOP_GETPROP)
3678 BEGIN_CASE(JSOP_GETXPROP)
3679 /* Get an immediate atom naming the property. */
3680 atom = GET_ATOM(cx, script, pc);
3681 lval = FETCH_OPND(-1);
3682 if (JSVAL_IS_STRING(lval) &&
3683 atom == cx->runtime->atomState.lengthAtom) {
3684 rval = INT_TO_JSVAL(JSSTRING_LENGTH(JSVAL_TO_STRING(lval)));
3685 obj = NULL;
3686 } else {
3687 id = ATOM_TO_JSID(atom);
3688 VALUE_TO_OBJECT(cx, lval, obj);
3689 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
3690 SAVE_SP_AND_PC(fp);
3691 CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval));
3692 if (!ok)
3693 goto out;
3694 }
3695 STORE_OPND(-1, rval);
3696 END_CASE(JSOP_GETPROP)
3697
3698 BEGIN_CASE(JSOP_SETPROP)
3699 /* Pop the right-hand side into rval for OBJ_SET_PROPERTY. */
3700 rval = FETCH_OPND(-1);
3701
3702 /* Get an immediate atom naming the property. */
3703 atom = GET_ATOM(cx, script, pc);
3704 id = ATOM_TO_JSID(atom);
3705 PROPERTY_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)));
3706 sp--;
3707 STORE_OPND(-1, rval);
3708 obj = NULL;
3709 END_CASE(JSOP_SETPROP)
3710
3711 BEGIN_CASE(JSOP_GETELEM)
3712 BEGIN_CASE(JSOP_GETXELEM)
3713 ELEMENT_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)));
3714 sp--;
3715 STORE_OPND(-1, rval);
3716 END_CASE(JSOP_GETELEM)
3717
3718 BEGIN_CASE(JSOP_SETELEM)
3719 rval = FETCH_OPND(-1);
3720 ELEMENT_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)));
3721 sp -= 2;
3722 STORE_OPND(-1, rval);
3723 obj = NULL;
3724 END_CASE(JSOP_SETELEM)
3725
3726 BEGIN_CASE(JSOP_ENUMELEM)
3727 /* Funky: the value to set is under the [obj, id] pair. */
3728 FETCH_ELEMENT_ID(-1, id);
3729 FETCH_OBJECT(cx, -2, lval, obj);
3730 CHECK_ELEMENT_ID(obj, id);
3731 rval = FETCH_OPND(-3);
3732 SAVE_SP_AND_PC(fp);
3733 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
3734 if (!ok)
3735 goto out;
3736 sp -= 3;
3737 END_CASE(JSOP_ENUMELEM)
3738
3739 /*
3740 * LAZY_ARGS_THISP allows the JSOP_ARGSUB bytecode to defer creation of the
3741 * arguments object until it is truly needed. JSOP_ARGSUB optimizes away
3742 * arguments objects when the only uses of the 'arguments' parameter are to
3743 * fetch individual actual parameters. But if such a use were then invoked,
3744 * e.g., arguments[i](), the 'this' parameter would and must bind to the
3745 * caller's arguments object. So JSOP_ARGSUB sets obj to LAZY_ARGS_THISP.
3746 */
3747 #define LAZY_ARGS_THISP ((JSObject *) JSVAL_VOID)
3748
3749 BEGIN_CASE(JSOP_PUSHOBJ)
3750 if (obj == LAZY_ARGS_THISP && !(obj = js_GetArgsObject(cx, fp))) {
3751 ok = JS_FALSE;
3752 goto out;
3753 }
3754 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3755 END_CASE(JSOP_PUSHOBJ)
3756
3757 BEGIN_CASE(JSOP_CALL)
3758 BEGIN_CASE(JSOP_EVAL)
3759 argc = GET_ARGC(pc);
3760 vp = sp - (argc + 2);
3761 lval = *vp;
3762 SAVE_SP_AND_PC(fp);
3763 if (VALUE_IS_FUNCTION(cx, lval) &&
3764 (obj = JSVAL_TO_OBJECT(lval),
3765 fun = (JSFunction *) JS_GetPrivate(cx, obj),
3766 FUN_INTERPRETED(fun)))
3767 /* inline_call: */
3768 {
3769 uintN nframeslots, nvars, nslots, missing;
3770 JSArena *a;
3771 jsuword avail, nbytes;
3772 JSBool overflow;
3773 void *newmark;
3774 jsval *rvp;
3775 JSInlineFrame *newifp;
3776 JSInterpreterHook hook;
3777
3778 /* Restrict recursion of lightweight functions. */
3779 if (inlineCallCount == MAX_INLINE_CALL_COUNT) {
3780 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3781 JSMSG_OVER_RECURSED);
3782 ok = JS_FALSE;
3783 goto out;
3784 }
3785
3786 /* Compute the total number of stack slots needed for fun. */
3787 nframeslots = JS_HOWMANY(sizeof(JSInlineFrame), sizeof(jsval));
3788 nvars = fun->u.i.nvars;
3789 script = fun->u.i.script;
3790 depth = (jsint) script->depth;
3791 nslots = nframeslots + nvars + 2 * depth;
3792
3793 /* Allocate missing expected args adjacent to actual args. */
3794 missing = (fun->nargs > argc) ? fun->nargs - argc : 0;
3795 a = cx->stackPool.current;
3796 avail = a->avail;
3797 newmark = (void *) avail;
3798 if (missing) {
3799 newsp = sp + missing;
3800 overflow = (jsuword) newsp > a->limit;
3801 if (overflow)
3802 nslots += 2 + argc + missing;
3803 else if ((jsuword) newsp > avail)
3804 avail = a->avail = (jsuword) newsp;
3805 }
3806 #ifdef __GNUC__
3807 else overflow = JS_FALSE; /* suppress bogus gcc warnings */
3808 #endif
3809
3810 /* Allocate the inline frame with its vars and operand slots. */
3811 newsp = (jsval *) avail;
3812 nbytes = nslots * sizeof(jsval);
3813 avail += nbytes;
3814 if (avail <= a->limit) {
3815 a->avail = avail;
3816 } else {
3817 JS_ARENA_ALLOCATE_CAST(newsp, jsval *, &cx->stackPool,
3818 nbytes);
3819 if (!newsp) {
3820 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3821 JSMSG_STACK_OVERFLOW,
3822 (fp && fp->fun)
3823 ? JS_GetFunctionName(fp->fun)
3824 : "script");
3825 goto bad_inline_call;
3826 }
3827 }
3828
3829 /* Move args if missing overflow arena a, push missing args. */
3830 rvp = vp;
3831 if (missing) {
3832 if (overflow) {
3833 memcpy(newsp, vp, (2 + argc) * sizeof(jsval));
3834 vp = newsp;
3835 sp = vp + 2 + argc;
3836 newsp = sp + missing;
3837 }
3838 do {
3839 PUSH(JSVAL_VOID);
3840 } while (--missing != 0);
3841 }
3842
3843 /* Claim space for the stack frame and initialize it. */
3844 newifp = (JSInlineFrame *) newsp;
3845 newsp += nframeslots;
3846 newifp->frame.callobj = NULL;
3847 newifp->frame.argsobj = NULL;
3848 newifp->frame.varobj = NULL;
3849 newifp->frame.script = script;
3850 newifp->frame.fun = fun;
3851 newifp->frame.argc = argc;
3852 newifp->frame.argv = vp + 2;
3853 newifp->frame.rval = JSVAL_VOID;
3854 newifp->frame.nvars = nvars;
3855 newifp->frame.vars = newsp;
3856 newifp->frame.down = fp;
3857 newifp->frame.annotation = NULL;
3858 newifp->frame.scopeChain = parent = OBJ_GET_PARENT(cx, obj);
3859 newifp->frame.sharpDepth = 0;
3860 newifp->frame.sharpArray = NULL;
3861 newifp->frame.flags = 0;
3862 newifp->frame.dormantNext = NULL;
3863 newifp->frame.xmlNamespace = NULL;
3864 newifp->frame.blockChain = NULL;
3865 newifp->rvp = rvp;
3866 newifp->mark = newmark;
3867
3868 /* Compute the 'this' parameter now that argv is set. */
3869 if (!JSVAL_IS_OBJECT(vp[1])) {
3870 PRIMITIVE_TO_OBJECT(cx, vp[1], obj2);
3871 if (!obj2)
3872 goto bad_inline_call;
3873 vp[1] = OBJECT_TO_JSVAL(obj2);
3874 }
3875 newifp->frame.thisp =
3876 js_ComputeThis(cx,
3877 JSFUN_BOUND_METHOD_TEST(fun->flags)
3878 ? parent
3879 : JSVAL_TO_OBJECT(vp[1]),
3880 newifp->frame.argv);
3881 if (!newifp->frame.thisp)
3882 goto bad_inline_call;
3883 #ifdef DUMP_CALL_TABLE
3884 LogCall(cx, *vp, argc, vp + 2);
3885 #endif
3886
3887 /* Push void to initialize local variables. */
3888 sp = newsp;
3889 while (nvars--)
3890 PUSH(JSVAL_VOID);
3891 sp += depth;
3892 newifp->frame.spbase = sp;
3893 SAVE_SP(&newifp->frame);
3894
3895 /* Call the debugger hook if present. */
3896 hook = rt->callHook;
3897 if (hook) {
3898 newifp->frame.pc = NULL;
3899 newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0,
3900 rt->callHookData);
3901 LOAD_INTERRUPT_HANDLER(rt);
3902 } else {
3903 newifp->hookData = NULL;
3904 }
3905
3906 /* Scope with a call object parented by the callee's parent. */
3907 if (JSFUN_HEAVYWEIGHT_TEST(fun->flags) &&
3908 !js_GetCallObject(cx, &newifp->frame, parent)) {
3909 goto bad_inline_call;
3910 }
3911
3912 /* Switch to new version if currentVersion wasn't overridden. */
3913 newifp->callerVersion = cx->version;
3914 if (JS_LIKELY(cx->version == currentVersion)) {
3915 currentVersion = script->version;
3916 if (currentVersion != cx->version)
3917 js_SetVersion(cx, currentVersion);
3918 }
3919
3920 /* Push the frame and set interpreter registers. */
3921 cx->fp = fp = &newifp->frame;
3922 pc = script->code;
3923 #ifndef JS_THREADED_INTERP
3924 endpc = pc + script->length;
3925 #endif
3926 obj = NULL;
3927 inlineCallCount++;
3928 JS_RUNTIME_METER(rt, inlineCalls);
3929
3930 /* Load first opcode and dispatch it (safe since JSOP_STOP). */
3931 op = *pc;
3932 DO_OP();
3933
3934 bad_inline_call:
3935 RESTORE_SP(fp);
3936 JS_ASSERT(fp->pc == pc);
3937 script = fp->script;
3938 depth = (jsint) script->depth;
3939 js_FreeRawStack(cx, newmark);
3940 ok = JS_FALSE;
3941 goto out;
3942 }
3943
3944 ok = js_Invoke(cx, argc, 0);
3945 RESTORE_SP(fp);
3946 LOAD_BRANCH_CALLBACK(cx);
3947 LOAD_INTERRUPT_HANDLER(rt);
3948 if (!ok)
3949 goto out;
3950 JS_RUNTIME_METER(rt, nonInlineCalls);
3951 #if JS_HAS_LVALUE_RETURN
3952 if (cx->rval2set) {
3953 /*
3954 * Use the stack depth we didn't claim in our budget, but that
3955 * we know is there on account of [fun, this] already having
3956 * been pushed, at a minimum (if no args). Those two slots
3957 * have been popped and [rval] has been pushed, which leaves
3958 * one more slot for rval2 before we might overflow.
3959 *
3960 * NB: rval2 must be the property identifier, and rval the
3961 * object from which to get the property. The pair form an
3962 * ECMA "reference type", which can be used on the right- or
3963 * left-hand side of assignment ops. Note well: only native
3964 * methods can return reference types. See JSOP_SETCALL just
3965 * below for the left-hand-side case.
3966 */
3967 PUSH_OPND(cx->rval2);
3968 ELEMENT_OP(-1, ok = OBJ_GET_PROPERTY(cx, obj, id, &rval));
3969
3970 sp--;
3971 STORE_OPND(-1, rval);
3972 cx->rval2set = JS_FALSE;
3973 }
3974 #endif /* JS_HAS_LVALUE_RETURN */
3975 obj = NULL;
3976 END_CASE(JSOP_CALL)
3977
3978 #if JS_HAS_LVALUE_RETURN
3979 BEGIN_CASE(JSOP_SETCALL)
3980 argc = GET_ARGC(pc);
3981 SAVE_SP_AND_PC(fp);
3982 ok = js_Invoke(cx, argc, 0);
3983 RESTORE_SP(fp);
3984 LOAD_BRANCH_CALLBACK(cx);
3985 LOAD_INTERRUPT_HANDLER(rt);
3986 if (!ok)
3987 goto out;
3988 if (!cx->rval2set) {
3989 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3990 JSMSG_BAD_LEFTSIDE_OF_ASS);
3991 ok = JS_FALSE;
3992 goto out;
3993 }
3994 PUSH_OPND(cx->rval2);
3995 cx->rval2set = JS_FALSE;
3996 obj = NULL;
3997 END_CASE(JSOP_SETCALL)
3998 #endif
3999
4000 BEGIN_CASE(JSOP_NAME)
4001 atom = GET_ATOM(cx, script, pc);
4002 id = ATOM_TO_JSID(atom);
4003
4004 SAVE_SP_AND_PC(fp);
4005 ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
4006 if (!ok)
4007 goto out;
4008 if (!prop) {
4009 /* Kludge to allow (typeof foo == "undefined") tests. */
4010 len = JSOP_NAME_LENGTH;
4011 endpc = script->code + script->length;
4012 for (pc2 = pc + len; pc2 < endpc; pc2++) {
4013 op2 = (JSOp)*pc2;
4014 if (op2 == JSOP_TYPEOF) {
4015 PUSH_OPND(JSVAL_VOID);
4016 DO_NEXT_OP(len);
4017 }
4018 if (op2 != JSOP_GROUP)
4019 break;
4020 }
4021 goto atom_not_defined;
4022 }
4023
4024 /* Take the slow path if prop was not found in a native object. */
4025 if (!OBJ_IS_NATIVE(obj) || !OBJ_IS_NATIVE(obj2)) {
4026 OBJ_DROP_PROPERTY(cx, obj2, prop);
4027 ok = OBJ_GET_PROPERTY(cx, obj, id, &rval);
4028 if (!ok)
4029 goto out;
4030 } else {
4031 sprop = (JSScopeProperty *)prop;
4032 NATIVE_GET(cx, obj, obj2, sprop, &rval);
4033 OBJ_DROP_PROPERTY(cx, obj2, prop);
4034 }
4035 PUSH_OPND(rval);
4036 END_CASE(JSOP_NAME)
4037
4038 BEGIN_CASE(JSOP_UINT16)
4039 i = (jsint) GET_ATOM_INDEX(pc);
4040 rval = INT_TO_JSVAL(i);
4041 PUSH_OPND(rval);
4042 obj = NULL;
4043 END_CASE(JSOP_UINT16)
4044
4045 BEGIN_CASE(JSOP_UINT24)
4046 i = (jsint) GET_LITERAL_INDEX(pc);
4047 rval = INT_TO_JSVAL(i);
4048 PUSH_OPND(rval);
4049 END_CASE(JSOP_UINT24)
4050
4051 BEGIN_CASE(JSOP_LITERAL)
4052 atomIndex = GET_LITERAL_INDEX(pc);
4053 atom = js_GetAtom(cx, &script->atomMap, atomIndex);
4054 PUSH_OPND(ATOM_KEY(atom));
4055 obj = NULL;
4056 END_CASE(JSOP_LITERAL)
4057
4058 BEGIN_CASE(JSOP_FINDNAME)
4059 atomIndex = GET_LITERAL_INDEX(pc);
4060 atom = js_GetAtom(cx, &script->atomMap, atomIndex);
4061 SAVE_SP_AND_PC(fp);
4062 obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom));
4063 if (!obj) {
4064 ok = JS_FALSE;
4065 goto out;
4066 }
4067 PUSH_OPND(OBJECT_TO_JSVAL(obj));
4068 PUSH_OPND(ATOM_KEY(atom));
4069 END_CASE(JSOP_FINDNAME)
4070
4071 BEGIN_CASE(JSOP_LITOPX)
4072 /*
4073 * Load atomIndex, which is used by code at each do_JSOP_* label.
4074 *
4075 * Also set pc2 to point at the bytecode extended by this prefix
4076 * to have a leading 24 bit atomIndex, instead of the unextended
4077 * 16-bit atomIndex that normally comes after op. This enables
4078 * JOF_INDEXCONST format ops (which have multiple immediates) to
4079 * collect their other immediate via GET_VARNO(pc2) or similar.
4080 *
4081 * Finally, load op and, if threading, adjust pc so that it will
4082 * be advanced properly at the end of op's case by DO_NEXT_OP.
4083 */
4084 atomIndex = GET_LITERAL_INDEX(pc);
4085 pc2 = pc + 1 + LITERAL_INDEX_LEN;
4086 op = *pc2;
4087 pc += JSOP_LITOPX_LENGTH - (1 + ATOM_INDEX_LEN);
4088 #ifndef JS_THREADED_INTERP
4089 len = js_CodeSpec[op].length;
4090 #endif
4091 switch (op) {
4092 case JSOP_ANONFUNOBJ: goto do_JSOP_ANONFUNOBJ;
4093 case JSOP_BINDNAME: goto do_JSOP_BINDNAME;
4094 case JSOP_CLOSURE: goto do_JSOP_CLOSURE;
4095 case JSOP_DEFCONST: goto do_JSOP_DEFCONST;
4096 case JSOP_DEFFUN: goto do_JSOP_DEFFUN;
4097 case JSOP_DEFLOCALFUN: goto do_JSOP_DEFLOCALFUN;
4098 case JSOP_DEFVAR: goto do_JSOP_DEFVAR;
4099 #if JS_HAS_EXPORT_IMPORT
4100 case JSOP_EXPORTNAME: goto do_JSOP_EXPORTNAME;
4101 #endif
4102 #if JS_HAS_XML_SUPPORT
4103 case JSOP_GETMETHOD: goto do_JSOP_GETMETHOD;
4104 case JSOP_SETMETHOD: goto do_JSOP_SETMETHOD;
4105 #endif
4106 case JSOP_NAMEDFUNOBJ: goto do_JSOP_NAMEDFUNOBJ;
4107 case JSOP_NUMBER: goto do_JSOP_NUMBER;
4108 case JSOP_OBJECT: goto do_JSOP_OBJECT;
4109 #if JS_HAS_XML_SUPPORT
4110 case JSOP_QNAMECONST: goto do_JSOP_QNAMECONST;
4111 case JSOP_QNAMEPART: goto do_JSOP_QNAMEPART;
4112 #endif
4113 case JSOP_REGEXP: goto do_JSOP_REGEXP;
4114 case JSOP_SETCONST: goto do_JSOP_SETCONST;
4115 case JSOP_STRING: goto do_JSOP_STRING;
4116 #if JS_HAS_XML_SUPPORT
4117 case JSOP_XMLCDATA: goto do_JSOP_XMLCDATA;
4118 case JSOP_XMLCOMMENT: goto do_JSOP_XMLCOMMENT;
4119 case JSOP_XMLOBJECT: goto do_JSOP_XMLOBJECT;
4120 case JSOP_XMLPI: goto do_JSOP_XMLPI;
4121 #endif
4122 case JSOP_ENTERBLOCK: goto do_JSOP_ENTERBLOCK;
4123 default: JS_ASSERT(0);
4124 }
4125 /* NOTREACHED */
4126
4127 BEGIN_CASE(JSOP_NUMBER)
4128 BEGIN_CASE(JSOP_STRING)
4129 BEGIN_CASE(JSOP_OBJECT)
4130 atomIndex = GET_ATOM_INDEX(pc);
4131
4132 do_JSOP_NUMBER:
4133 do_JSOP_STRING:
4134 do_JSOP_OBJECT:
4135 atom = js_GetAtom(cx, &script->atomMap, atomIndex);
4136 PUSH_OPND(ATOM_KEY(atom));
4137 obj = NULL;
4138 END_CASE(JSOP_NUMBER)
4139
4140 BEGIN_LITOPX_CASE(JSOP_REGEXP, 0)
4141 {
4142 JSRegExp *re;
4143 JSObject *funobj;
4144
4145 /*
4146 * Push a regexp object for the atom mapped by the bytecode at pc,
4147 * cloning the literal's regexp object if necessary, to simulate in
4148 * the pre-compile/execute-later case what ECMA specifies for the
4149 * compile-and-go case: that scanning each regexp literal creates
4150 * a single corresponding RegExp object.
4151 *
4152 * To support pre-compilation transparently, we must handle the
4153 * case where a regexp object literal is used in a different global
4154 * at execution time from the global with which it was scanned at
4155 * compile time. We do this by re-wrapping the JSRegExp private
4156 * data struct with a cloned object having the right prototype and
4157 * parent, and having its own lastIndex property value storage.
4158 *
4159 * Unlike JSOP_DEFFUN and other prolog bytecodes that may clone
4160 * literal objects, we don't want to pay a script prolog execution
4161 * price for all regexp literals in a script (many may not be used
4162 * by a particular execution of that script, depending on control
4163 * flow), so we initialize lazily here.
4164 *
4165 * XXX This code is specific to regular expression objects. If we
4166 * need a similar op for other kinds of object literals, we should
4167 * push cloning down under JSObjectOps and reuse code here.
4168 */
4169 JS_ASSERT(ATOM_IS_OBJECT(atom));
4170 obj = ATOM_TO_OBJECT(atom);
4171 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass);
4172
4173 re = (JSRegExp *) JS_GetPrivate(cx, obj);
4174 slot = re->cloneIndex;
4175 if (fp->fun) {
4176 /*
4177 * We're in function code, not global or eval code (in eval
4178 * code, JSOP_REGEXP is never emitted). The code generator
4179 * recorded in fp->fun->nregexps the number of re->cloneIndex
4180 * slots that it reserved in the cloned funobj.
4181 */
4182 funobj = JSVAL_TO_OBJECT(fp->argv[-2]);
4183 slot += JSCLASS_RESERVED_SLOTS(&js_FunctionClass);
4184 if (!JS_GetReservedSlot(cx, funobj, slot, &rval))
4185 return JS_FALSE;
4186 if (JSVAL_IS_VOID(rval))
4187 rval = JSVAL_NULL;
4188 } else {
4189 /*
4190 * We're in global code. The code generator already arranged
4191 * via script->numGlobalVars to reserve a global variable slot
4192 * at cloneIndex. All global variable slots are initialized
4193 * to null, not void, for faster testing in JSOP_*GVAR cases.
4194 */
4195 rval = fp->vars[slot];
4196 #ifdef __GNUC__
4197 funobj = NULL; /* suppress bogus gcc warnings */
4198 #endif
4199 }
4200
4201 if (JSVAL_IS_NULL(rval)) {
4202 /* Compute the current global object in obj2. */
4203 obj2 = fp->scopeChain;
4204 while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL)
4205 obj2 = parent;
4206
4207 /*
4208 * We must home sp here, because either js_CloneRegExpObject
4209 * or JS_SetReservedSlot could nest a last-ditch GC. We home
4210 * pc as well, in case js_CloneRegExpObject has to lookup the
4211 * "RegExp" class in the global object, which could entail a
4212 * JSNewResolveOp call.
4213 */
4214 SAVE_SP_AND_PC(fp);
4215
4216 /*
4217 * If obj's parent is not obj2, we must clone obj so that it
4218 * has the right parent, and therefore, the right prototype.
4219 *
4220 * Yes, this means we assume that the correct RegExp.prototype
4221 * to which regexp instances (including literals) delegate can
4222 * be distinguished solely by the instance's parent, which was
4223 * set to the parent of the RegExp constructor function object
4224 * when the instance was created. In other words,
4225 *
4226 * (/x/.__parent__ == RegExp.__parent__) implies
4227 * (/x/.__proto__ == RegExp.prototype)
4228 *
4229 * (unless you assign a different object to RegExp.prototype
4230 * at runtime, in which case, ECMA doesn't specify operation,
4231 * and you get what you deserve).
4232 *
4233 * This same coupling between instance parent and constructor
4234 * parent turns up everywhere (see jsobj.c's FindClassObject,
4235 * js_ConstructObject, and js_NewObject). It's fundamental to
4236 * the design of the language when you consider multiple global
4237 * objects and separate compilation and execution, even though
4238 * it is not specified fully in ECMA.
4239 */
4240 if (OBJ_GET_PARENT(cx, obj) != obj2) {
4241 obj = js_CloneRegExpObject(cx, obj, obj2);
4242 if (!obj) {
4243 ok = JS_FALSE;
4244 goto out;
4245 }
4246 }
4247 rval = OBJECT_TO_JSVAL(obj);
4248
4249 /* Store the regexp object value in its cloneIndex slot. */
4250 if (fp->fun) {
4251 if (!JS_SetReservedSlot(cx, funobj, slot, rval))
4252 return JS_FALSE;
4253 } else {
4254 fp->vars[slot] = rval;
4255 }
4256 }
4257
4258 PUSH_OPND(rval);
4259 obj = NULL;
4260 }
4261 END_LITOPX_CASE(JSOP_REGEXP)
4262
4263 BEGIN_CASE(JSOP_ZERO)
4264 PUSH_OPND(JSVAL_ZERO);
4265 obj = NULL;
4266 END_CASE(JSOP_ZERO)
4267
4268 BEGIN_CASE(JSOP_ONE)
4269 PUSH_OPND(JSVAL_ONE);
4270 obj = NULL;
4271 END_CASE(JSOP_ONE)
4272
4273 BEGIN_CASE(JSOP_NULL)
4274 PUSH_OPND(JSVAL_NULL);
4275 obj = NULL;
4276 END_CASE(JSOP_NULL)
4277
4278 BEGIN_CASE(JSOP_THIS)
4279 obj = fp->thisp;
4280 clasp = OBJ_GET_CLASS(cx, obj);
4281 if (clasp->flags & JSCLASS_IS_EXTENDED) {
4282 JSExtendedClass *xclasp;
4283
4284 xclasp = (JSExtendedClass *) clasp;
4285 if (xclasp->outerObject) {
4286 obj = xclasp->outerObject(cx, obj);
4287 if (!obj) {
4288 ok = JS_FALSE;
4289 goto out;
4290 }
4291 }
4292 }
4293
4294 PUSH_OPND(OBJECT_TO_JSVAL(obj));
4295 obj = NULL;
4296 END_CASE(JSOP_THIS)
4297
4298 BEGIN_CASE(JSOP_FALSE)
4299 PUSH_OPND(JSVAL_FALSE);
4300 obj = NULL;
4301 END_CASE(JSOP_FALSE)
4302
4303 BEGIN_CASE(JSOP_TRUE)
4304 PUSH_OPND(JSVAL_TRUE);
4305 obj = NULL;
4306 END_CASE(JSOP_TRUE)
4307
4308 BEGIN_CASE(JSOP_TABLESWITCH)
4309 pc2 = pc;
4310 len = GET_JUMP_OFFSET(pc2);
4311
4312 /*
4313 * ECMAv2+ forbids conversion of discriminant, so we will skip to
4314 * the default case if the discriminant isn't already an int jsval.
4315 * (This opcode is emitted only for dense jsint-domain switches.)
4316 */
4317 rval = POP_OPND();
4318 if (!JSVAL_IS_INT(rval))
4319 DO_NEXT_OP(len);
4320 i = JSVAL_TO_INT(rval);
4321
4322 pc2 += JUMP_OFFSET_LEN;
4323 low = GET_JUMP_OFFSET(pc2);
4324 pc2 += JUMP_OFFSET_LEN;
4325 high = GET_JUMP_OFFSET(pc2);
4326
4327 i -= low;
4328 if ((jsuint)i < (jsuint)(high - low + 1)) {
4329 pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i;
4330 off = (jsint) GET_JUMP_OFFSET(pc2);
4331 if (off)
4332 len = off;
4333 }
4334 END_VARLEN_CASE
4335
4336 BEGIN_CASE(JSOP_LOOKUPSWITCH)
4337 lval = POP_OPND();
4338 pc2 = pc;
4339 len = GET_JUMP_OFFSET(pc2);
4340
4341 if (!JSVAL_IS_NUMBER(lval) &&
4342 !JSVAL_IS_STRING(lval) &&
4343 !JSVAL_IS_BOOLEAN(lval)) {
4344 DO_NEXT_OP(len);
4345 }
4346
4347 pc2 += JUMP_OFFSET_LEN;
4348 npairs = (jsint) GET_ATOM_INDEX(pc2);
4349 pc2 += ATOM_INDEX_LEN;
4350
4351 #define SEARCH_PAIRS(MATCH_CODE) \
4352 while (npairs) { \
4353 atom = GET_ATOM(cx, script, pc2); \
4354 rval = ATOM_KEY(atom); \
4355 MATCH_CODE \
4356 if (match) { \
4357 pc2 += ATOM_INDEX_LEN; \
4358 len = GET_JUMP_OFFSET(pc2); \
4359 DO_NEXT_OP(len); \
4360 } \
4361 pc2 += ATOM_INDEX_LEN + JUMP_OFFSET_LEN; \
4362 npairs--; \
4363 }
4364 if (JSVAL_IS_STRING(lval)) {
4365 str = JSVAL_TO_STRING(lval);
4366 SEARCH_PAIRS(
4367 match = (JSVAL_IS_STRING(rval) &&
4368 ((str2 = JSVAL_TO_STRING(rval)) == str ||
4369 js_EqualStrings(str2, str)));
4370 )
4371 } else if (JSVAL_IS_DOUBLE(lval)) {
4372 d = *JSVAL_TO_DOUBLE(lval);
4373 SEARCH_PAIRS(
4374 match = (JSVAL_IS_DOUBLE(rval) &&
4375 *JSVAL_TO_DOUBLE(rval) == d);
4376 )
4377 } else {
4378 SEARCH_PAIRS(
4379 match = (lval == rval);
4380 )
4381 }
4382 #undef SEARCH_PAIRS
4383 END_VARLEN_CASE
4384
4385 BEGIN_CASE(JSOP_TABLESWITCHX)
4386 pc2 = pc;
4387 len = GET_JUMPX_OFFSET(pc2);
4388
4389 /*
4390 * ECMAv2+ forbids conversion of discriminant, so we will skip to
4391 * the default case if the discriminant isn't already an int jsval.
4392 * (This opcode is emitted only for dense jsint-domain switches.)
4393 */
4394 rval = POP_OPND();
4395 if (!JSVAL_IS_INT(rval))
4396 DO_NEXT_OP(len);
4397 i = JSVAL_TO_INT(rval);
4398
4399 pc2 += JUMPX_OFFSET_LEN;
4400 low = GET_JUMP_OFFSET(pc2);
4401 pc2 += JUMP_OFFSET_LEN;
4402 high = GET_JUMP_OFFSET(pc2);
4403
4404 i -= low;
4405 if ((jsuint)i < (jsuint)(high - low + 1)) {
4406 pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i;
4407 off = (jsint) GET_JUMPX_OFFSET(pc2);
4408 if (off)
4409 len = off;
4410 }
4411 END_VARLEN_CASE
4412
4413 BEGIN_CASE(JSOP_LOOKUPSWITCHX)
4414 lval = POP_OPND();
4415 pc2 = pc;
4416 len = GET_JUMPX_OFFSET(pc2);
4417
4418 if (!JSVAL_IS_NUMBER(lval) &&
4419 !JSVAL_IS_STRING(lval) &&
4420 !JSVAL_IS_BOOLEAN(lval)) {
4421 DO_NEXT_OP(len);
4422 }
4423
4424 pc2 += JUMPX_OFFSET_LEN;
4425 npairs = (jsint) GET_ATOM_INDEX(pc2);
4426 pc2 += ATOM_INDEX_LEN;
4427
4428 #define SEARCH_EXTENDED_PAIRS(MATCH_CODE) \
4429 while (npairs) { \
4430 atom = GET_ATOM(cx, script, pc2); \
4431 rval = ATOM_KEY(atom); \
4432 MATCH_CODE \
4433 if (match) { \
4434 pc2 += ATOM_INDEX_LEN; \
4435 len = GET_JUMPX_OFFSET(pc2); \
4436 DO_NEXT_OP(len); \
4437 } \
4438 pc2 += ATOM_INDEX_LEN + JUMPX_OFFSET_LEN; \
4439 npairs--; \
4440 }
4441 if (JSVAL_IS_STRING(lval)) {
4442 str = JSVAL_TO_STRING(lval);
4443 SEARCH_EXTENDED_PAIRS(
4444 match = (JSVAL_IS_STRING(rval) &&
4445 ((str2 = JSVAL_TO_STRING(rval)) == str ||
4446 js_EqualStrings(str2, str)));
4447 )
4448 } else if (JSVAL_IS_DOUBLE(lval)) {
4449 d = *JSVAL_TO_DOUBLE(lval);
4450 SEARCH_EXTENDED_PAIRS(
4451 match = (JSVAL_IS_DOUBLE(rval) &&
4452 *JSVAL_TO_DOUBLE(rval) == d);
4453 )
4454 } else {
4455 SEARCH_EXTENDED_PAIRS(
4456 match = (lval == rval);
4457 )
4458 }
4459 #undef SEARCH_EXTENDED_PAIRS
4460 END_VARLEN_CASE
4461
4462 EMPTY_CASE(JSOP_CONDSWITCH)
4463
4464 #if JS_HAS_EXPORT_IMPORT
4465 BEGIN_CASE(JSOP_EXPORTALL)
4466 obj = fp->varobj;
4467 SAVE_SP_AND_PC(fp);
4468 ida = JS_Enumerate(cx, obj);
4469 if (!ida) {
4470 ok = JS_FALSE;
4471 } else {
4472 for (i = 0, j = ida->length; i < j; i++) {
4473 id = ida->vector[i];
4474 ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
4475 if (!ok)
4476 break;
4477 if (!prop)
4478 continue;
4479 ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
4480 if (ok) {
4481 attrs |= JSPROP_EXPORTED;
4482 ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs);
4483 }
4484 OBJ_DROP_PROPERTY(cx, obj2, prop);
4485 if (!ok)
4486 break;
4487 }
4488 JS_DestroyIdArray(cx, ida);
4489 }
4490 END_CASE(JSOP_EXPORTALL)
4491
4492 BEGIN_LITOPX_CASE(JSOP_EXPORTNAME, 0)
4493 id = ATOM_TO_JSID(atom);
4494 obj = fp->varobj;
4495 SAVE_SP_AND_PC(fp);
4496 ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
4497 if (!ok)
4498 goto out;
4499 if (!prop) {
4500 ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
4501 JSPROP_EXPORTED, NULL);
4502 } else {
4503 ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
4504 if (ok) {
4505 attrs |= JSPROP_EXPORTED;
4506 ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs);
4507 }
4508 OBJ_DROP_PROPERTY(cx, obj2, prop);
4509 }
4510 if (!ok)
4511 goto out;
4512 END_LITOPX_CASE(JSOP_EXPORTNAME)
4513
4514 BEGIN_CASE(JSOP_IMPORTALL)
4515 id = (jsid) JSVAL_VOID;
4516 PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id));
4517 sp--;
4518 END_CASE(JSOP_IMPORTALL)
4519
4520 BEGIN_CASE(JSOP_IMPORTPROP)
4521 /* Get an immediate atom naming the property. */
4522 atom = GET_ATOM(cx, script, pc);
4523 id = ATOM_TO_JSID(atom);
4524 PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id));
4525 sp--;
4526 END_CASE(JSOP_IMPORTPROP)
4527
4528 BEGIN_CASE(JSOP_IMPORTELEM)
4529 ELEMENT_OP(-1, ok = ImportProperty(cx, obj, id));
4530 sp -= 2;
4531 END_CASE(JSOP_IMPORTELEM)
4532 #endif /* JS_HAS_EXPORT_IMPORT */
4533
4534 BEGIN_CASE(JSOP_TRAP)
4535 SAVE_SP_AND_PC(fp);
4536 switch (JS_HandleTrap(cx, script, pc, &rval)) {
4537 case JSTRAP_ERROR:
4538 ok = JS_FALSE;
4539 goto out;
4540 case JSTRAP_CONTINUE:
4541 JS_ASSERT(JSVAL_IS_INT(rval));
4542 op = (JSOp) JSVAL_TO_INT(rval);
4543 JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
4544 LOAD_INTERRUPT_HANDLER(rt);
4545 DO_OP();
4546 case JSTRAP_RETURN:
4547 fp->rval = rval;
4548 goto out;
4549 case JSTRAP_THROW:
4550 cx->throwing = JS_TRUE;
4551 cx->exception = rval;
4552 ok = JS_FALSE;
4553 goto out;
4554 default:;
4555 }
4556 LOAD_INTERRUPT_HANDLER(rt);
4557 END_CASE(JSOP_TRAP)
4558
4559 BEGIN_CASE(JSOP_ARGUMENTS)
4560 SAVE_SP_AND_PC(fp);
4561 ok = js_GetArgsValue(cx, fp, &rval);
4562 if (!ok)
4563 goto out;
4564 PUSH_OPND(rval);
4565 obj = NULL;
4566 END_CASE(JSOP_ARGUMENTS)
4567
4568 BEGIN_CASE(JSOP_ARGSUB)
4569 id = INT_TO_JSID(GET_ARGNO(pc));
4570 SAVE_SP_AND_PC(fp);
4571 ok = js_GetArgsProperty(cx, fp, id, &obj, &rval);
4572 if (!ok)
4573 goto out;
4574 if (!obj) {
4575 /*
4576 * If arguments was not overridden by eval('arguments = ...'),
4577 * set obj to the magic cookie respected by JSOP_PUSHOBJ, just
4578 * in case this bytecode is part of an 'arguments[i](j, k)' or
4579 * similar such invocation sequence, where the function that
4580 * is invoked expects its 'this' parameter to be the caller's
4581 * arguments object.
4582 */
4583 obj = LAZY_ARGS_THISP;
4584 }
4585 PUSH_OPND(rval);
4586 END_CASE(JSOP_ARGSUB)
4587
4588 #undef LAZY_ARGS_THISP
4589
4590 BEGIN_CASE(JSOP_ARGCNT)
4591 id = ATOM_TO_JSID(rt->atomState.lengthAtom);
4592 SAVE_SP_AND_PC(fp);
4593 ok = js_GetArgsProperty(cx, fp, id, &obj, &rval);
4594 if (!ok)
4595 goto out;
4596 PUSH_OPND(rval);
4597 END_CASE(JSOP_ARGCNT)
4598
4599 BEGIN_CASE(JSOP_GETARG)
4600 slot = GET_ARGNO(pc);
4601 JS_ASSERT(slot < fp->fun->nargs);
4602 PUSH_OPND(fp->argv[slot]);
4603 obj = NULL;
4604 END_CASE(JSOP_GETARG)
4605
4606 BEGIN_CASE(JSOP_SETARG)
4607 slot = GET_ARGNO(pc);
4608 JS_ASSERT(slot < fp->fun->nargs);
4609 vp = &fp->argv[slot];
4610 GC_POKE(cx, *vp);
4611 *vp = FETCH_OPND(-1);
4612 obj = NULL;
4613 END_CASE(JSOP_SETARG)
4614
4615 BEGIN_CASE(JSOP_GETVAR)
4616 slot = GET_VARNO(pc);
4617 JS_ASSERT(slot < fp->fun->u.i.nvars);
4618 PUSH_OPND(fp->vars[slot]);
4619 obj = NULL;
4620 END_CASE(JSOP_GETVAR)
4621
4622 BEGIN_CASE(JSOP_SETVAR)
4623 slot = GET_VARNO(pc);
4624 JS_ASSERT(slot < fp->fun->u.i.nvars);
4625 vp = &fp->vars[slot];
4626 GC_POKE(cx, *vp);
4627 *vp = FETCH_OPND(-1);
4628 obj = NULL;
4629 END_CASE(JSOP_SETVAR)
4630
4631 BEGIN_CASE(JSOP_GETGVAR)
4632 slot = GET_VARNO(pc);
4633 JS_ASSERT(slot < fp->nvars);
4634 lval = fp->vars[slot];
4635 if (JSVAL_IS_NULL(lval)) {
4636 op = JSOP_NAME;
4637 DO_OP();
4638 }
4639 slot = JSVAL_TO_INT(lval);
4640 obj = fp->varobj;
4641 rval = OBJ_GET_SLOT(cx, obj, slot);
4642 PUSH_OPND(rval);
4643 END_CASE(JSOP_GETGVAR)
4644
4645 BEGIN_CASE(JSOP_SETGVAR)
4646 slot = GET_VARNO(pc);
4647 JS_ASSERT(slot < fp->nvars);
4648 rval = FETCH_OPND(-1);
4649 lval = fp->vars[slot];
4650 obj = fp->varobj;
4651 if (JSVAL_IS_NULL(lval)) {
4652 /*
4653 * Inline-clone and specialize JSOP_SETNAME code here because
4654 * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval]
4655 * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME.
4656 */
4657 atom = GET_ATOM(cx, script, pc);
4658 id = ATOM_TO_JSID(atom);
4659 SAVE_SP_AND_PC(fp);
4660 CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval));
4661 if (!ok)
4662 goto out;
4663 STORE_OPND(-1, rval);
4664 } else {
4665 slot = JSVAL_TO_INT(lval);
4666 GC_POKE(cx, obj->slots[slot]);
4667 OBJ_SET_SLOT(cx, obj, slot, rval);
4668 }
4669 obj = NULL;
4670 END_CASE(JSOP_SETGVAR)
4671
4672 BEGIN_CASE(JSOP_DEFCONST)
4673 BEGIN_CASE(JSOP_DEFVAR)
4674 atomIndex = GET_ATOM_INDEX(pc);
4675
4676 do_JSOP_DEFCONST:
4677 do_JSOP_DEFVAR:
4678 atom = js_GetAtom(cx, &script->atomMap, atomIndex);
4679 obj = fp->varobj;
4680 attrs = JSPROP_ENUMERATE;
4681 if (!(fp->flags & JSFRAME_EVAL))
4682 attrs |= JSPROP_PERMANENT;
4683 if (op == JSOP_DEFCONST)
4684 attrs |= JSPROP_READONLY;
4685
4686 /* Lookup id in order to check for redeclaration problems. */
4687 id = ATOM_TO_JSID(atom);
4688 SAVE_SP_AND_PC(fp);
4689 ok = js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop);
4690 if (!ok)
4691 goto out;
4692
4693 /* Bind a variable only if it's not yet defined. */
4694 if (!prop) {
4695 ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
4696 attrs, &prop);
4697 if (!ok)
4698 goto out;
4699 JS_ASSERT(prop);
4700 obj2 = obj;
4701 }
4702
4703 /*
4704 * Try to optimize a property we either just created, or found
4705 * directly in the global object, that is permanent, has a slot,
4706 * and has stub getter and setter, into a "fast global" accessed
4707 * by the JSOP_*GVAR opcodes.
4708 */
4709 if (atomIndex < script->numGlobalVars &&
4710 (attrs & JSPROP_PERMANENT) &&
4711 obj2 == obj &&
4712 OBJ_IS_NATIVE(obj)) {
4713 sprop = (JSScopeProperty *) prop;
4714 if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) &&
4715 SPROP_HAS_STUB_GETTER(sprop) &&
4716 SPROP_HAS_STUB_SETTER(sprop)) {
4717 /*
4718 * Fast globals use fp->vars to map the global name's
4719 * atomIndex to the permanent fp->varobj slot number,
4720 * tagged as a jsval. The atomIndex for the global's
4721 * name literal is identical to its fp->vars index.
4722 */
4723 fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot);
4724 }
4725 }
4726
4727 OBJ_DROP_PROPERTY(cx, obj2, prop);
4728 END_CASE(JSOP_DEFVAR)
4729
4730 BEGIN_LITOPX_CASE(JSOP_DEFFUN, 0)
4731 obj = ATOM_TO_OBJECT(atom);
4732 fun = (JSFunction *) JS_GetPrivate(cx, obj);
4733 id = ATOM_TO_JSID(fun->atom);
4734
4735 /*
4736 * We must be at top-level (either outermost block that forms a
4737 * function's body, or a global) scope, not inside an expression
4738 * (JSOP_{ANON,NAMED}FUNOBJ) or compound statement (JSOP_CLOSURE)
4739 * in the same compilation unit (ECMA Program).
4740 *
4741 * However, we could be in a Program being eval'd from inside a
4742 * with statement, so we need to distinguish scope chain head from
4743 * variables object. Hence the obj2 vs. parent distinction below.
4744 * First we make sure the function object we're defining has the
4745 * right scope chain. Then we define its name in fp->varobj.
4746 *
4747 * If static link is not current scope, clone fun's object to link
4748 * to the current scope via parent. This clause exists to enable
4749 * sharing of compiled functions among multiple equivalent scopes,
4750 * splitting the cost of compilation evenly among the scopes and
4751 * amortizing it over a number of executions. Examples include XUL
4752 * scripts and event handlers shared among Mozilla chrome windows,
4753 * and server-side JS user-defined functions shared among requests.
4754 *
4755 * NB: The Script object exposes compile and exec in the language,
4756 * such that this clause introduces an incompatible change from old
4757 * JS versions that supported Script. Such a JS version supported
4758 * executing a script that defined and called functions scoped by
4759 * the compile-time static link, not by the exec-time scope chain.
4760 *
4761 * We sacrifice compatibility, breaking such scripts, in order to
4762 * promote compile-cost sharing and amortizing, and because Script
4763 * is not and will not be standardized.
4764 */
4765 JS_ASSERT(!fp->blockChain);
4766 obj2 = fp->scopeChain;
4767 if (OBJ_GET_PARENT(cx, obj) != obj2) {
4768 obj = js_CloneFunctionObject(cx, obj, obj2);
4769 if (!obj) {
4770 ok = JS_FALSE;
4771 goto out;
4772 }
4773 }
4774
4775 /*
4776 * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All
4777 * paths from here must flow through the "Restore fp->scopeChain"
4778 * code below the OBJ_DEFINE_PROPERTY call.
4779 */
4780 fp->scopeChain = obj;
4781 rval = OBJECT_TO_JSVAL(obj);
4782
4783 /*
4784 * ECMA requires functions defined when entering Global code to be
4785 * permanent, and functions defined when entering Eval code to be
4786 * impermanent.
4787 */
4788 attrs = JSPROP_ENUMERATE;
4789 if (!(fp->flags & JSFRAME_EVAL))
4790 attrs |= JSPROP_PERMANENT;
4791
4792 /*
4793 * Load function flags that are also property attributes. Getters
4794 * and setters do not need a slot, their value is stored elsewhere
4795 * in the property itself, not in obj->slots.
4796 */
4797 flags = JSFUN_GSFLAG2ATTR(fun->flags);
4798 if (flags) {
4799 attrs |= flags | JSPROP_SHARED;
4800 rval = JSVAL_VOID;
4801 }
4802
4803 /*
4804 * Check for a const property of the same name -- or any kind
4805 * of property if executing with the strict option. We check
4806 * here at runtime as well as at compile-time, to handle eval
4807 * as well as multiple HTML script tags.
4808 */
4809 parent = fp->varobj;
4810 SAVE_SP_AND_PC(fp);
4811 ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL);
4812 if (ok) {
4813 ok = OBJ_DEFINE_PROPERTY(cx, parent, id, rval,
4814 (flags & JSPROP_GETTER)
4815 ? JS_EXTENSION (JSPropertyOp) obj
4816 : NULL,
4817 (flags & JSPROP_SETTER)
4818 ? JS_EXTENSION (JSPropertyOp) obj
4819 : NULL,
4820 attrs,
4821 &prop);
4822 }
4823
4824 /* Restore fp->scopeChain now that obj is defined in fp->varobj. */
4825 fp->scopeChain = obj2;
4826 if (!ok)
4827 goto out;
4828
4829 #if 0
4830 if (attrs == (JSPROP_ENUMERATE | JSPROP_PERMANENT) &&
4831 script->numGlobalVars) {
4832 /*
4833 * As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals
4834 * use fp->vars to map the global function name's atomIndex to
4835 * its permanent fp->varobj slot number, tagged as a jsval.
4836 */
4837 sprop = (JSScopeProperty *) prop;
4838 fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot);
4839 }
4840 #endif
4841 OBJ_DROP_PROPERTY(cx, parent, prop);
4842 END_LITOPX_CASE(JSOP_DEFFUN)
4843
4844 BEGIN_LITOPX_CASE(JSOP_DEFLOCALFUN, VARNO_LEN)
4845 /*
4846 * Define a local function (i.e., one nested at the top level of
4847 * another function), parented by the current scope chain, and
4848 * stored in a local variable slot that the compiler allocated.
4849 * This is an optimization over JSOP_DEFFUN that avoids requiring
4850 * a call object for the outer function's activation.
4851 */
4852 slot = GET_VARNO(pc2);
4853 obj = ATOM_TO_OBJECT(atom);
4854
4855 JS_ASSERT(!fp->blockChain);
4856 if (!(fp->flags & JSFRAME_POP_BLOCKS)) {
4857 /*
4858 * If the compiler-created function object (obj) is scoped by a
4859 * let-induced body block, temporarily update fp->blockChain so
4860 * that js_GetScopeChain will clone the block into the runtime
4861 * scope needed to parent the function object's clone.
4862 */
4863 parent = OBJ_GET_PARENT(cx, obj);
4864 if (OBJ_GET_CLASS(cx, parent) == &js_BlockClass)
4865 fp->blockChain = parent;
4866 parent = js_GetScopeChain(cx, fp);
4867 } else {
4868 /*
4869 * We have already emulated JSOP_ENTERBLOCK for the enclosing
4870 * body block, for a prior JSOP_DEFLOCALFUN in the prolog, so
4871 * we just load fp->scopeChain into parent.
4872 *
4873 * In typical execution scenarios, the prolog bytecodes that
4874 * include this JSOP_DEFLOCALFUN run, then come main bytecodes
4875 * including JSOP_ENTERBLOCK for the outermost (body) block.
4876 * JSOP_ENTERBLOCK will detect that it need not do anything if
4877 * the body block was entered above due to a local function.
4878 * Finally the matching JSOP_LEAVEBLOCK runs.
4879 *
4880 * If the matching JSOP_LEAVEBLOCK for the body block does not
4881 * run for some reason, the body block will be properly "put"
4882 * (via js_PutBlockObject) by the PutBlockObjects call at the
4883 * bottom of js_Interpret.
4884 */
4885 parent = fp->scopeChain;
4886 JS_ASSERT(OBJ_GET_CLASS(cx, parent) == &js_BlockClass);
4887 JS_ASSERT(OBJ_GET_PROTO(cx, parent) == OBJ_GET_PARENT(cx, obj));
4888 JS_ASSERT(OBJ_GET_CLASS(cx, OBJ_GET_PARENT(cx, parent))
4889 == &js_CallClass);
4890 }
4891
4892 /* If re-parenting, store a clone of the function object. */
4893 if (OBJ_GET_PARENT(cx, obj) != parent) {
4894 SAVE_SP_AND_PC(fp);
4895 obj = js_CloneFunctionObject(cx, obj, parent);
4896 if (!obj) {
4897 ok = JS_FALSE;
4898 goto out;
4899 }
4900 }
4901 fp->vars[slot] = OBJECT_TO_JSVAL(obj);
4902 END_LITOPX_CASE(JSOP_DEFLOCALFUN)
4903
4904 BEGIN_LITOPX_CASE(JSOP_ANONFUNOBJ, 0)
4905 /* Push the specified function object literal. */
4906 obj = ATOM_TO_OBJECT(atom);
4907
4908 /* If re-parenting, push a clone of the function object. */
4909 SAVE_SP_AND_PC(fp);
4910 parent = js_GetScopeChain(cx, fp);
4911 if (!parent) {
4912 ok = JS_FALSE;
4913 goto out;
4914 }
4915 if (OBJ_GET_PARENT(cx, obj) != parent) {
4916 obj = js_CloneFunctionObject(cx, obj, parent);
4917 if (!obj) {
4918 ok = JS_FALSE;
4919 goto out;
4920 }
4921 }
4922 PUSH_OPND(OBJECT_TO_JSVAL(obj));
4923 obj = NULL;
4924 END_LITOPX_CASE(JSOP_ANONFUNOBJ)
4925
4926 BEGIN_LITOPX_CASE(JSOP_NAMEDFUNOBJ, 0)
4927 /* ECMA ed. 3 FunctionExpression: function Identifier [etc.]. */
4928 rval = ATOM_KEY(atom);
4929 JS_ASSERT(VALUE_IS_FUNCTION(cx, rval));
4930
4931 /*
4932 * 1. Create a new object as if by the expression new Object().
4933 * 2. Add Result(1) to the front of the scope chain.
4934 *
4935 * Step 2 is achieved by making the new object's parent be the
4936 * current scope chain, and then making the new object the parent
4937 * of the Function object clone.
4938 */
4939 SAVE_SP_AND_PC(fp);
4940 obj2 = js_GetScopeChain(cx, fp);
4941 if (!obj2) {
4942 ok = JS_FALSE;
4943 goto out;
4944 }
4945 parent = js_NewObject(cx, &js_ObjectClass, NULL, obj2);
4946 if (!parent) {
4947 ok = JS_FALSE;
4948 goto out;
4949 }
4950
4951 /*
4952 * 3. Create a new Function object as specified in section 13.2
4953 * with [parameters and body specified by the function expression
4954 * that was parsed by the compiler into a Function object, and
4955 * saved in the script's atom map].
4956 *
4957 * Protect parent from GC after js_CloneFunctionObject calls into
4958 * js_NewObject, which displaces the newborn object root in cx by
4959 * allocating the clone, then runs a last-ditch GC while trying
4960 * to allocate the clone's slots vector. Another, multi-threaded
4961 * path: js_CloneFunctionObject => js_NewObject => OBJ_GET_CLASS
4962 * which may suspend the current request in ClaimScope, with the
4963 * newborn displaced as in the first scenario.
4964 */
4965 fp->scopeChain = parent;
4966 obj = js_CloneFunctionObject(cx, JSVAL_TO_OBJECT(rval), parent);
4967 if (!obj) {
4968 ok = JS_FALSE;
4969 goto out;
4970 }
4971
4972 /*
4973 * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All
4974 * paths from here must flow through the "Restore fp->scopeChain"
4975 * code below the OBJ_DEFINE_PROPERTY call.
4976 */
4977 fp->scopeChain = obj;
4978 rval = OBJECT_TO_JSVAL(obj);
4979
4980 /*
4981 * 4. Create a property in the object Result(1). The property's
4982 * name is [fun->atom, the identifier parsed by the compiler],
4983 * value is Result(3), and attributes are { DontDelete, ReadOnly }.
4984 */
4985 fun = (JSFunction *) JS_GetPrivate(cx, obj);
4986 attrs = JSFUN_GSFLAG2ATTR(fun->flags);
4987 if (attrs) {
4988 attrs |= JSPROP_SHARED;
4989 rval = JSVAL_VOID;
4990 }
4991 ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval,
4992 (attrs & JSPROP_GETTER)
4993 ? JS_EXTENSION (JSPropertyOp) obj
4994 : NULL,
4995 (attrs & JSPROP_SETTER)
4996 ? JS_EXTENSION (JSPropertyOp) obj
4997 : NULL,
4998 attrs |
4999 JSPROP_ENUMERATE | JSPROP_PERMANENT |
5000 JSPROP_READONLY,
5001 NULL);
5002
5003 /* Restore fp->scopeChain now that obj is defined in parent. */
5004 fp->scopeChain = obj2;
5005 if (!ok) {
5006 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
5007 goto out;
5008 }
5009
5010 /*
5011 * 5. Remove Result(1) from the front of the scope chain [no-op].
5012 * 6. Return Result(3).
5013 */
5014 PUSH_OPND(OBJECT_TO_JSVAL(obj));
5015 obj = NULL;
5016 END_LITOPX_CASE(JSOP_NAMEDFUNOBJ)
5017
5018 BEGIN_LITOPX_CASE(JSOP_CLOSURE, 0)
5019 /*
5020 * ECMA ed. 3 extension: a named function expression in a compound
5021 * statement (not at the top statement level of global code, or at
5022 * the top level of a function body).
5023 *
5024 * Get immediate operand atom, which is a function object literal.
5025 * From it, get the function to close.
5026 */
5027 JS_ASSERT(VALUE_IS_FUNCTION(cx, ATOM_KEY(atom)));
5028 obj = ATOM_TO_OBJECT(atom);
5029
5030 /*
5031 * Clone the function object with the current scope chain as the
5032 * clone's parent. The original function object is the prototype
5033 * of the clone. Do this only if re-parenting; the compiler may
5034 * have seen the right parent already and created a sufficiently
5035 * well-scoped function object.
5036 */
5037 SAVE_SP_AND_PC(fp);
5038 obj2 = js_GetScopeChain(cx, fp);
5039 if (!obj2) {
5040 ok = JS_FALSE;
5041 goto out;
5042 }
5043 if (OBJ_GET_PARENT(cx, obj) != obj2) {
5044 obj = js_CloneFunctionObject(cx, obj, obj2);
5045 if (!obj) {
5046 ok = JS_FALSE;
5047 goto out;
5048 }
5049 }
5050
5051 /*
5052 * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All
5053 * paths from here must flow through the "Restore fp->scopeChain"
5054 * code below the OBJ_DEFINE_PROPERTY call.
5055 */
5056 fp->scopeChain = obj;
5057 rval = OBJECT_TO_JSVAL(obj);
5058
5059 /*
5060 * Make a property in fp->varobj with id fun->atom and value obj,
5061 * unless fun is a getter or setter (in which case, obj is cast to
5062 * a JSPropertyOp and passed accordingly).
5063 */
5064 fun = (JSFunction *) JS_GetPrivate(cx, obj);
5065 attrs = JSFUN_GSFLAG2ATTR(fun->flags);
5066 if (attrs) {
5067 attrs |= JSPROP_SHARED;
5068 rval = JSVAL_VOID;
5069 }
5070 parent = fp->varobj;
5071 ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval,
5072 (attrs & JSPROP_GETTER)
5073 ? JS_EXTENSION (JSPropertyOp) obj
5074 : NULL,
5075 (attrs & JSPROP_SETTER)
5076 ? JS_EXTENSION (JSPropertyOp) obj
5077 : NULL,
5078 attrs | JSPROP_ENUMERATE
5079 | JSPROP_PERMANENT,
5080 &prop);
5081
5082 /* Restore fp->scopeChain now that obj is defined in fp->varobj. */
5083 fp->scopeChain = obj2;
5084 if (!ok) {
5085 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
5086 goto out;
5087 }
5088
5089 #if 0
5090 if (attrs == 0 && script->numGlobalVars) {
5091 /*
5092 * As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals
5093 * use fp->vars to map the global function name's atomIndex to
5094 * its permanent fp->varobj slot number, tagged as a jsval.
5095 */
5096 sprop = (JSScopeProperty *) prop;
5097 fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot);
5098 }
5099 #endif
5100 OBJ_DROP_PROPERTY(cx, parent, prop);
5101 END_LITOPX_CASE(JSOP_CLOSURE)
5102
5103 #if JS_HAS_GETTER_SETTER
5104 BEGIN_CASE(JSOP_GETTER)
5105 BEGIN_CASE(JSOP_SETTER)
5106 op2 = (JSOp) *++pc;
5107 switch (op2) {
5108 case JSOP_SETNAME:
5109 case JSOP_SETPROP:
5110 atom = GET_ATOM(cx, script, pc);
5111 id = ATOM_TO_JSID(atom);
5112 rval = FETCH_OPND(-1);
5113 i = -1;
5114 goto gs_pop_lval;
5115
5116 case JSOP_SETELEM:
5117 rval = FETCH_OPND(-1);
5118 FETCH_ELEMENT_ID(-2, id);
5119 i = -2;
5120 gs_pop_lval:
5121 FETCH_OBJECT(cx, i - 1, lval, obj);
5122 break;
5123
5124 case JSOP_INITPROP:
5125 JS_ASSERT(sp - fp->spbase >= 2);
5126 rval = FETCH_OPND(-1);
5127 i = -1;
5128 atom = GET_ATOM(cx, script, pc);
5129 id = ATOM_TO_JSID(atom);
5130 goto gs_get_lval;
5131
5132 case JSOP_INITELEM:
5133 JS_ASSERT(sp - fp->spbase >= 3);
5134 rval = FETCH_OPND(-1);
5135 FETCH_ELEMENT_ID(-2, id);
5136 i = -2;
5137 gs_get_lval:
5138 lval = FETCH_OPND(i-1);
5139 JS_ASSERT(JSVAL_IS_OBJECT(lval));
5140 obj = JSVAL_TO_OBJECT(lval);
5141 break;
5142
5143 default:
5144 JS_ASSERT(0);
5145 }
5146
5147 /* Ensure that id has a type suitable for use with obj. */
5148 CHECK_ELEMENT_ID(obj, id);
5149
5150 SAVE_SP_AND_PC(fp);
5151 if (JS_TypeOfValue(cx, rval) != JSTYPE_FUNCTION) {
5152 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5153 JSMSG_BAD_GETTER_OR_SETTER,
5154 (op == JSOP_GETTER)
5155 ? js_getter_str
5156 : js_setter_str);
5157 ok = JS_FALSE;
5158 goto out;
5159 }
5160
5161 /*
5162 * Getters and setters are just like watchpoints from an access
5163 * control point of view.
5164 */
5165 ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &rtmp, &attrs);
5166 if (!ok)
5167 goto out;
5168
5169 if (op == JSOP_GETTER) {
5170 getter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval);
5171 setter = NULL;
5172 attrs = JSPROP_GETTER;
5173 } else {
5174 getter = NULL;
5175 setter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval);
5176 attrs = JSPROP_SETTER;
5177 }
5178 attrs |= JSPROP_ENUMERATE | JSPROP_SHARED;
5179
5180 /* Check for a readonly or permanent property of the same name. */
5181 ok = js_CheckRedeclaration(cx, obj, id, attrs, NULL, NULL);
5182 if (!ok)
5183 goto out;
5184
5185 ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, getter, setter,
5186 attrs, NULL);
5187 if (!ok)
5188 goto out;
5189
5190 obj = NULL;
5191 sp += i;
5192 if (js_CodeSpec[op2].ndefs)
5193 STORE_OPND(-1, rval);
5194 len = js_CodeSpec[op2].length;
5195 DO_NEXT_OP(len);
5196 #endif /* JS_HAS_GETTER_SETTER */
5197
5198 BEGIN_CASE(JSOP_NEWINIT)
5199 argc = 0;
5200 fp->sharpDepth++;
5201 goto do_new;
5202
5203 BEGIN_CASE(JSOP_ENDINIT)
5204 if (--fp->sharpDepth == 0)
5205 fp->sharpArray = NULL;
5206
5207 /* Re-set the newborn root to the top of this object tree. */
5208 JS_ASSERT(sp - fp->spbase >= 1);
5209 lval = FETCH_OPND(-1);
5210 JS_ASSERT(JSVAL_IS_OBJECT(lval));
5211 cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(lval);
5212 END_CASE(JSOP_ENDINIT)
5213
5214 BEGIN_CASE(JSOP_INITPROP)
5215 /* Pop the property's value into rval. */
5216 JS_ASSERT(sp - fp->spbase >= 2);
5217 rval = FETCH_OPND(-1);
5218
5219 /* Get the immediate property name into id. */
5220 atom = GET_ATOM(cx, script, pc);
5221 id = ATOM_TO_JSID(atom);
5222 i = -1;
5223 goto do_init;
5224
5225 BEGIN_CASE(JSOP_INITELEM)
5226 /* Pop the element's value into rval. */
5227 JS_ASSERT(sp - fp->spbase >= 3);
5228 rval = FETCH_OPND(-1);
5229
5230 /* Pop and conditionally atomize the element id. */
5231 FETCH_ELEMENT_ID(-2, id);
5232 i = -2;
5233
5234 do_init:
5235 /* Find the object being initialized at top of stack. */
5236 lval = FETCH_OPND(i-1);
5237 JS_ASSERT(JSVAL_IS_OBJECT(lval));
5238 obj = JSVAL_TO_OBJECT(lval);
5239
5240 /* Ensure that id has a type suitable for use with obj. */
5241 CHECK_ELEMENT_ID(obj, id);
5242
5243 /* Set the property named by obj[id] to rval. */
5244 SAVE_SP_AND_PC(fp);
5245 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
5246 if (!ok)
5247 goto out;
5248 sp += i;
5249 len = js_CodeSpec[op].length;
5250 DO_NEXT_OP(len);
5251
5252 #if JS_HAS_SHARP_VARS
5253 BEGIN_CASE(JSOP_DEFSHARP)
5254 SAVE_SP_AND_PC(fp);
5255 obj = fp->sharpArray;
5256 if (!obj) {
5257 obj = js_NewArrayObject(cx, 0, NULL);
5258 if (!obj) {
5259 ok = JS_FALSE;
5260 goto out;
5261 }
5262 fp->sharpArray = obj;
5263 }
5264 i = (jsint) GET_ATOM_INDEX(pc);
5265 id = INT_TO_JSID(i);
5266 rval = FETCH_OPND(-1);
5267 if (JSVAL_IS_PRIMITIVE(rval)) {
5268 char numBuf[12];
5269 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
5270 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5271 JSMSG_BAD_SHARP_DEF, numBuf);
5272 ok = JS_FALSE;
5273 goto out;
5274 }
5275 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
5276 if (!ok)
5277 goto out;
5278 END_CASE(JSOP_DEFSHARP)
5279
5280 BEGIN_CASE(JSOP_USESHARP)
5281 i = (jsint) GET_ATOM_INDEX(pc);
5282 id = INT_TO_JSID(i);
5283 obj = fp->sharpArray;
5284 if (!obj) {
5285 rval = JSVAL_VOID;
5286 } else {
5287 SAVE_SP_AND_PC(fp);
5288 ok = OBJ_GET_PROPERTY(cx, obj, id, &rval);
5289 if (!ok)
5290 goto out;
5291 }
5292 if (!JSVAL_IS_OBJECT(rval)) {
5293 char numBuf[12];
5294 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
5295
5296 SAVE_SP_AND_PC(fp);
5297 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5298 JSMSG_BAD_SHARP_USE, numBuf);
5299 ok = JS_FALSE;
5300 goto out;
5301 }
5302 PUSH_OPND(rval);
5303 END_CASE(JSOP_USESHARP)
5304 #endif /* JS_HAS_SHARP_VARS */
5305
5306 /* No-ops for ease of decompilation and jit'ing. */
5307 EMPTY_CASE(JSOP_TRY)
5308 EMPTY_CASE(JSOP_FINALLY)
5309
5310 /* Reset the stack to the given depth. */
5311 BEGIN_CASE(JSOP_SETSP)
5312 i = (jsint) GET_ATOM_INDEX(pc);
5313 JS_ASSERT(i >= 0);
5314
5315 for (obj = fp->blockChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
5316 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
5317 if (OBJ_BLOCK_DEPTH(cx, obj) + (jsint)OBJ_BLOCK_COUNT(cx, obj) <= i) {
5318 JS_ASSERT(OBJ_BLOCK_DEPTH(cx, obj) < i || OBJ_BLOCK_COUNT(cx, obj) == 0);
5319 break;
5320 }
5321 }
5322 fp->blockChain = obj;
5323
5324 JS_ASSERT(ok);
5325 for (obj = fp->scopeChain;
5326 (clasp = OBJ_GET_CLASS(cx, obj)) == &js_WithClass ||
5327 clasp == &js_BlockClass;
5328 obj = OBJ_GET_PARENT(cx, obj)) {
5329 if (JS_GetPrivate(cx, obj) != fp ||
5330 OBJ_BLOCK_DEPTH(cx, obj) < i) {
5331 break;
5332 }
5333 if (clasp == &js_BlockClass)
5334 ok &= js_PutBlockObject(cx, obj);
5335 else
5336 JS_SetPrivate(cx, obj, NULL);
5337 }
5338
5339 fp->scopeChain = obj;
5340
5341 /* Set sp after js_PutBlockObject to avoid potential GC hazards. */
5342 sp = fp->spbase + i;
5343
5344 /* Don't fail until after we've updated all stacks. */
5345 if (!ok)
5346 goto out;
5347 END_CASE(JSOP_SETSP)
5348
5349 BEGIN_CASE(JSOP_GOSUB)
5350 JS_ASSERT(cx->exception != JSVAL_HOLE);
5351 if (!cx->throwing) {
5352 lval = JSVAL_HOLE;
5353 } else {
5354 lval = cx->exception;
5355 cx->throwing = JS_FALSE;
5356 }
5357 PUSH(lval);
5358 i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUB_LENGTH;
5359 len = GET_JUMP_OFFSET(pc);
5360 PUSH(INT_TO_JSVAL(i));
5361 END_VARLEN_CASE
5362
5363 BEGIN_CASE(JSOP_GOSUBX)
5364 JS_ASSERT(cx->exception != JSVAL_HOLE);
5365 if (!cx->throwing) {
5366 lval = JSVAL_HOLE;
5367 } else {
5368 lval = cx->exception;
5369 cx->throwing = JS_FALSE;
5370 }
5371 PUSH(lval);
5372 i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUBX_LENGTH;
5373 len = GET_JUMPX_OFFSET(pc);
5374 PUSH(INT_TO_JSVAL(i));
5375 END_VARLEN_CASE
5376
5377 BEGIN_CASE(JSOP_RETSUB)
5378 rval = POP();
5379 JS_ASSERT(JSVAL_IS_INT(rval));
5380 lval = POP();
5381 if (lval != JSVAL_HOLE) {
5382 /*
5383 * Exception was pending during finally, throw it *before* we
5384 * adjust pc, because pc indexes into script->trynotes. This
5385 * turns out not to be necessary, but it seems clearer. And
5386 * it points out a FIXME: 350509, due to Igor Bukanov.
5387 */
5388 cx->throwing = JS_TRUE;
5389 cx->exception = lval;
5390 ok = JS_FALSE;
5391 goto out;
5392 }
5393 len = JSVAL_TO_INT(rval);
5394 pc = script->main;
5395 END_VARLEN_CASE
5396
5397 BEGIN_CASE(JSOP_EXCEPTION)
5398 JS_ASSERT(cx->throwing);
5399 PUSH(cx->exception);
5400 cx->throwing = JS_FALSE;
5401 END_CASE(JSOP_EXCEPTION)
5402
5403 BEGIN_CASE(JSOP_THROWING)
5404 JS_ASSERT(!cx->throwing);
5405 cx->throwing = JS_TRUE;
5406 cx->exception = POP_OPND();
5407 END_CASE(JSOP_THROWING)
5408
5409 BEGIN_CASE(JSOP_THROW)
5410 JS_ASSERT(!cx->throwing);
5411 cx->throwing = JS_TRUE;
5412 cx->exception = POP_OPND();
5413 ok = JS_FALSE;
5414 /* let the code at out try to catch the exception. */
5415 goto out;
5416
5417 BEGIN_CASE(JSOP_SETLOCALPOP)
5418 /*
5419 * The stack must have a block with at least one local slot below
5420 * the exception object.
5421 */
5422 JS_ASSERT(sp - fp->spbase >= 2);
5423 slot = GET_UINT16(pc);
5424 JS_ASSERT(slot + 1 < (uintN)depth);
5425 fp->spbase[slot] = POP_OPND();
5426 END_CASE(JSOP_SETLOCALPOP)
5427
5428 BEGIN_CASE(JSOP_INSTANCEOF)
5429 SAVE_SP_AND_PC(fp);
5430 rval = FETCH_OPND(-1);
5431 if (JSVAL_IS_PRIMITIVE(rval) ||
5432 !(obj = JSVAL_TO_OBJECT(rval))->map->ops->hasInstance) {
5433 str = js_DecompileValueGenerator(cx, -1, rval, NULL);
5434 if (str) {
5435 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5436 JSMSG_BAD_INSTANCEOF_RHS,
5437 JS_GetStringBytes(str));
5438 }
5439 ok = JS_FALSE;
5440 goto out;
5441 }
5442 lval = FETCH_OPND(-2);
5443 cond = JS_FALSE;
5444 ok = obj->map->ops->hasInstance(cx, obj, lval, &cond);
5445 if (!ok)
5446 goto out;
5447 sp--;
5448 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));
5449 END_CASE(JSOP_INSTANCEOF)
5450
5451 #if JS_HAS_DEBUGGER_KEYWORD
5452 BEGIN_CASE(JSOP_DEBUGGER)
5453 {
5454 JSTrapHandler handler = rt->debuggerHandler;
5455 if (handler) {
5456 SAVE_SP_AND_PC(fp);
5457 switch (handler(cx, script, pc, &rval,
5458 rt->debuggerHandlerData)) {
5459 case JSTRAP_ERROR:
5460 ok = JS_FALSE;
5461 goto out;
5462 case JSTRAP_CONTINUE:
5463 break;
5464 case JSTRAP_RETURN:
5465 fp->rval = rval;
5466 goto out;
5467 case JSTRAP_THROW:
5468 cx->throwing = JS_TRUE;
5469 cx->exception = rval;
5470 ok = JS_FALSE;
5471 goto out;
5472 default:;
5473 }
5474 LOAD_INTERRUPT_HANDLER(rt);
5475 }
5476 }
5477 END_CASE(JSOP_DEBUGGER)
5478 #endif /* JS_HAS_DEBUGGER_KEYWORD */
5479
5480 #if JS_HAS_XML_SUPPORT
5481 BEGIN_CASE(JSOP_DEFXMLNS)
5482 rval = POP();
5483 SAVE_SP_AND_PC(fp);
5484 ok = js_SetDefaultXMLNamespace(cx, rval);
5485 if (!ok)
5486 goto out;
5487 END_CASE(JSOP_DEFXMLNS)
5488
5489 BEGIN_CASE(JSOP_ANYNAME)
5490 SAVE_SP_AND_PC(fp);
5491 ok = js_GetAnyName(cx, &rval);
5492 if (!ok)
5493 goto out;
5494 PUSH_OPND(rval);
5495 END_CASE(JSOP_ANYNAME)
5496
5497 BEGIN_LITOPX_CASE(JSOP_QNAMEPART, 0)
5498 PUSH_OPND(ATOM_KEY(atom));
5499 END_LITOPX_CASE(JSOP_QNAMEPART)
5500
5501 BEGIN_LITOPX_CASE(JSOP_QNAMECONST, 0)
5502 rval = ATOM_KEY(atom);
5503 lval = FETCH_OPND(-1);
5504 SAVE_SP_AND_PC(fp);
5505 obj = js_ConstructXMLQNameObject(cx, lval, rval);
5506 if (!obj) {
5507 ok = JS_FALSE;
5508 goto out;
5509 }
5510 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5511 END_LITOPX_CASE(JSOP_QNAMECONST)
5512
5513 BEGIN_CASE(JSOP_QNAME)
5514 rval = FETCH_OPND(-1);
5515 lval = FETCH_OPND(-2);
5516 SAVE_SP_AND_PC(fp);
5517 obj = js_ConstructXMLQNameObject(cx, lval, rval);
5518 if (!obj) {
5519 ok = JS_FALSE;
5520 goto out;
5521 }
5522 sp--;
5523 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5524 END_CASE(JSOP_QNAME)
5525
5526 BEGIN_CASE(JSOP_TOATTRNAME)
5527 rval = FETCH_OPND(-1);
5528 SAVE_SP_AND_PC(fp);
5529 ok = js_ToAttributeName(cx, &rval);
5530 if (!ok)
5531 goto out;
5532 STORE_OPND(-1, rval);
5533 END_CASE(JSOP_TOATTRNAME)
5534
5535 BEGIN_CASE(JSOP_TOATTRVAL)
5536 rval = FETCH_OPND(-1);
5537 JS_ASSERT(JSVAL_IS_STRING(rval));
5538 SAVE_SP_AND_PC(fp);
5539 str = js_EscapeAttributeValue(cx, JSVAL_TO_STRING(rval));
5540 if (!str) {
5541 ok = JS_FALSE;
5542 goto out;
5543 }
5544 STORE_OPND(-1, STRING_TO_JSVAL(str));
5545 END_CASE(JSOP_TOATTRVAL)
5546
5547 BEGIN_CASE(JSOP_ADDATTRNAME)
5548 BEGIN_CASE(JSOP_ADDATTRVAL)
5549 rval = FETCH_OPND(-1);
5550 lval = FETCH_OPND(-2);
5551 str = JSVAL_TO_STRING(lval);
5552 str2 = JSVAL_TO_STRING(rval);
5553 SAVE_SP_AND_PC(fp);
5554 str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2);
5555 if (!str) {
5556 ok = JS_FALSE;
5557 goto out;
5558 }
5559 sp--;
5560 STORE_OPND(-1, STRING_TO_JSVAL(str));
5561 END_CASE(JSOP_ADDATTRNAME)
5562
5563 BEGIN_CASE(JSOP_BINDXMLNAME)
5564 lval = FETCH_OPND(-1);
5565 SAVE_SP_AND_PC(fp);
5566 ok = js_FindXMLProperty(cx, lval, &obj, &rval);
5567 if (!ok)
5568 goto out;
5569 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5570 PUSH_OPND(rval);
5571 END_CASE(JSOP_BINDXMLNAME)
5572
5573 BEGIN_CASE(JSOP_SETXMLNAME)
5574 obj = JSVAL_TO_OBJECT(FETCH_OPND(-3));
5575 lval = FETCH_OPND(-2);
5576 rval = FETCH_OPND(-1);
5577 SAVE_SP_AND_PC(fp);
5578 ok = js_SetXMLProperty(cx, obj, lval, &rval);
5579 if (!ok)
5580 goto out;
5581 sp -= 2;
5582 STORE_OPND(-1, rval);
5583 obj = NULL;
5584 END_CASE(JSOP_SETXMLNAME)
5585
5586 BEGIN_CASE(JSOP_XMLNAME)
5587 lval = FETCH_OPND(-1);
5588 SAVE_SP_AND_PC(fp);
5589 ok = js_FindXMLProperty(cx, lval, &obj, &rval);
5590 if (!ok)
5591 goto out;
5592 ok = js_GetXMLProperty(cx, obj, rval, &rval);
5593 if (!ok)
5594 goto out;
5595 STORE_OPND(-1, rval);
5596 END_CASE(JSOP_XMLNAME)
5597
5598 BEGIN_CASE(JSOP_DESCENDANTS)
5599 BEGIN_CASE(JSOP_DELDESC)
5600 FETCH_OBJECT(cx, -2, lval, obj);
5601 rval = FETCH_OPND(-1);
5602 SAVE_SP_AND_PC(fp);
5603 ok = js_GetXMLDescendants(cx, obj, rval, &rval);
5604 if (!ok)
5605 goto out;
5606
5607 if (op == JSOP_DELDESC) {
5608 sp[-1] = rval; /* set local root */
5609 ok = js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval));
5610 if (!ok)
5611 goto out;
5612 rval = JSVAL_TRUE; /* always succeed */
5613 }
5614
5615 sp--;
5616 STORE_OPND(-1, rval);
5617 END_CASE(JSOP_DESCENDANTS)
5618
5619 BEGIN_CASE(JSOP_FILTER)
5620 FETCH_OBJECT(cx, -1, lval, obj);
5621 len = GET_JUMP_OFFSET(pc);
5622 SAVE_SP_AND_PC(fp);
5623 ok = js_FilterXMLList(cx, obj, pc + js_CodeSpec[op].length, &rval);
5624 if (!ok)
5625 goto out;
5626 JS_ASSERT(fp->sp == sp);
5627 STORE_OPND(-1, rval);
5628 END_VARLEN_CASE
5629
5630 BEGIN_CASE(JSOP_ENDFILTER)
5631 *result = POP_OPND();
5632 goto out;
5633
5634 EMPTY_CASE(JSOP_STARTXML)
5635 EMPTY_CASE(JSOP_STARTXMLEXPR)
5636
5637 BEGIN_CASE(JSOP_TOXML)
5638 rval = FETCH_OPND(-1);
5639 SAVE_SP_AND_PC(fp);
5640 obj = js_ValueToXMLObject(cx, rval);
5641 if (!obj) {
5642 ok = JS_FALSE;
5643 goto out;
5644 }
5645 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5646 END_CASE(JSOP_TOXML)
5647
5648 BEGIN_CASE(JSOP_TOXMLLIST)
5649 rval = FETCH_OPND(-1);
5650 SAVE_SP_AND_PC(fp);
5651 obj = js_ValueToXMLListObject(cx, rval);
5652 if (!obj) {
5653 ok = JS_FALSE;
5654 goto out;
5655 }
5656 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5657 END_CASE(JSOP_TOXMLLIST)
5658
5659 BEGIN_CASE(JSOP_XMLTAGEXPR)
5660 rval = FETCH_OPND(-1);
5661 SAVE_SP_AND_PC(fp);
5662 str = js_ValueToString(cx, rval);
5663 if (!str) {
5664 ok = JS_FALSE;
5665 goto out;
5666 }
5667 STORE_OPND(-1, STRING_TO_JSVAL(str));
5668 END_CASE(JSOP_XMLTAGEXPR)
5669
5670 BEGIN_CASE(JSOP_XMLELTEXPR)
5671 rval = FETCH_OPND(-1);
5672 SAVE_SP_AND_PC(fp);
5673 if (VALUE_IS_XML(cx, rval)) {
5674 str = js_ValueToXMLString(cx, rval);
5675 } else {
5676 str = js_ValueToString(cx, rval);
5677 if (str)
5678 str = js_EscapeElementValue(cx, str);
5679 }
5680 if (!str) {
5681 ok = JS_FALSE;
5682 goto out;
5683 }
5684 STORE_OPND(-1, STRING_TO_JSVAL(str));
5685 END_CASE(JSOP_XMLELTEXPR)
5686
5687 BEGIN_LITOPX_CASE(JSOP_XMLOBJECT, 0)
5688 SAVE_SP_AND_PC(fp);
5689 obj = js_CloneXMLObject(cx, ATOM_TO_OBJECT(atom));
5690 if (!obj) {
5691 ok = JS_FALSE;
5692 goto out;
5693 }
5694 PUSH_OPND(OBJECT_TO_JSVAL(obj));
5695 obj = NULL;
5696 END_LITOPX_CASE(JSOP_XMLOBJECT)
5697
5698 BEGIN_LITOPX_CASE(JSOP_XMLCDATA, 0)
5699 str = ATOM_TO_STRING(atom);
5700 obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_TEXT, NULL, str);
5701 if (!obj) {
5702 ok = JS_FALSE;
5703 goto out;
5704 }
5705 PUSH_OPND(OBJECT_TO_JSVAL(obj));
5706 END_LITOPX_CASE(JSOP_XMLCDATA)
5707
5708 BEGIN_LITOPX_CASE(JSOP_XMLCOMMENT, 0)
5709 str = ATOM_TO_STRING(atom);
5710 obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_COMMENT, NULL, str);
5711 if (!obj) {
5712 ok = JS_FALSE;
5713 goto out;
5714 }
5715 PUSH_OPND(OBJECT_TO_JSVAL(obj));
5716 END_LITOPX_CASE(JSOP_XMLCOMMENT)
5717
5718 BEGIN_LITOPX_CASE(JSOP_XMLPI, 0)
5719 str = ATOM_TO_STRING(atom);
5720 rval = FETCH_OPND(-1);
5721 str2 = JSVAL_TO_STRING(rval);
5722 SAVE_SP_AND_PC(fp);
5723 obj = js_NewXMLSpecialObject(cx,
5724 JSXML_CLASS_PROCESSING_INSTRUCTION,
5725 str, str2);
5726 if (!obj) {
5727 ok = JS_FALSE;
5728 goto out;
5729 }
5730 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5731 END_LITOPX_CASE(JSOP_XMLPI)
5732
5733 BEGIN_LITOPX_CASE(JSOP_GETMETHOD, 0)
5734 /* Get an immediate atom naming the property. */
5735 id = ATOM_TO_JSID(atom);
5736 lval = FETCH_OPND(-1);
5737 SAVE_SP_AND_PC(fp);
5738 if (!JSVAL_IS_PRIMITIVE(lval)) {
5739 STORE_OPND(-1, lval);
5740 obj = JSVAL_TO_OBJECT(lval);
5741
5742 /* Special-case XML object method lookup, per ECMA-357. */
5743 if (OBJECT_IS_XML(cx, obj)) {
5744 JSXMLObjectOps *ops;
5745
5746 ops = (JSXMLObjectOps *) obj->map->ops;
5747 obj = ops->getMethod(cx, obj, id, &rval);
5748 if (!obj)
5749 ok = JS_FALSE;
5750 } else {
5751 CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval));
5752 }
5753 } else {
5754 if (JSVAL_IS_STRING(lval)) {
5755 i = JSProto_String;
5756 } else if (JSVAL_IS_NUMBER(lval)) {
5757 i = JSProto_Number;
5758 } else if (JSVAL_IS_BOOLEAN(lval)) {
5759 i = JSProto_Boolean;
5760 } else {
5761 JS_ASSERT(JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval));
5762 str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
5763 lval, NULL);
5764 if (str) {
5765 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5766 JSMSG_NO_PROPERTIES,
5767 JS_GetStringBytes(str));
5768 }
5769 ok = JS_FALSE;
5770 goto out;
5771 }
5772 ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj);
5773 if (!ok)
5774 goto out;
5775 JS_ASSERT(obj);
5776 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5777 CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval));
5778 obj = (JSObject *) lval; /* keep tagged as non-object */
5779 }
5780 if (!ok)
5781 goto out;
5782 STORE_OPND(-1, rval);
5783 END_LITOPX_CASE(JSOP_GETMETHOD)
5784
5785 BEGIN_LITOPX_CASE(JSOP_SETMETHOD, 0)
5786 /* Get an immediate atom naming the property. */
5787 id = ATOM_TO_JSID(atom);
5788 rval = FETCH_OPND(-1);
5789 FETCH_OBJECT(cx, -2, lval, obj);
5790 SAVE_SP_AND_PC(fp);
5791
5792 /* Special-case XML object method lookup, per ECMA-357. */
5793 if (OBJECT_IS_XML(cx, obj)) {
5794 JSXMLObjectOps *ops;
5795
5796 ops = (JSXMLObjectOps *) obj->map->ops;
5797 ok = ops->setMethod(cx, obj, id, &rval);
5798 } else {
5799 CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval));
5800 }
5801 if (!ok)
5802 goto out;
5803 --sp;
5804 STORE_OPND(-1, rval);
5805 obj = NULL;
5806 END_LITOPX_CASE(JSOP_SETMETHOD)
5807
5808 BEGIN_CASE(JSOP_GETFUNNS)
5809 SAVE_SP_AND_PC(fp);
5810 ok = js_GetFunctionNamespace(cx, &rval);
5811 if (!ok)
5812 goto out;
5813 PUSH_OPND(rval);
5814 END_CASE(JSOP_GETFUNNS)
5815 #endif /* JS_HAS_XML_SUPPORT */
5816
5817 BEGIN_LITOPX_CASE(JSOP_ENTERBLOCK, 0)
5818 obj = ATOM_TO_OBJECT(atom);
5819 JS_ASSERT(fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp);
5820 vp = sp + OBJ_BLOCK_COUNT(cx, obj);
5821 JS_ASSERT(vp <= fp->spbase + depth);
5822 while (sp < vp) {
5823 STORE_OPND(0, JSVAL_VOID);
5824 sp++;
5825 }
5826
5827 /*
5828 * If this frame had to reflect the compile-time block chain into
5829 * the runtime scope chain, we can't optimize block scopes out of
5830 * runtime any longer, because an outer block that parents obj has
5831 * been cloned onto the scope chain. To avoid re-cloning such a
5832 * parent and accumulating redundant clones via js_GetScopeChain,
5833 * we must clone each block eagerly on entry, and push it on the
5834 * scope chain, until this frame pops.
5835 */
5836 if (fp->flags & JSFRAME_POP_BLOCKS) {
5837 JS_ASSERT(!fp->blockChain);
5838
5839 /*
5840 * Check whether JSOP_DEFLOCALFUN emulated JSOP_ENTERBLOCK for
5841 * the body block in order to correctly scope the local cloned
5842 * function object it creates.
5843 */
5844 parent = fp->scopeChain;
5845 if (OBJ_GET_PROTO(cx, parent) == obj) {
5846 JS_ASSERT(OBJ_GET_CLASS(cx, parent) == &js_BlockClass);
5847 } else {
5848 obj = js_CloneBlockObject(cx, obj, parent, fp);
5849 if (!obj) {
5850 ok = JS_FALSE;
5851 goto out;
5852 }
5853 fp->scopeChain = obj;
5854 }
5855 } else {
5856 JS_ASSERT(!fp->blockChain ||
5857 OBJ_GET_PARENT(cx, obj) == fp->blockChain);
5858 fp->blockChain = obj;
5859 }
5860 END_LITOPX_CASE(JSOP_ENTERBLOCK)
5861
5862 BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
5863 BEGIN_CASE(JSOP_LEAVEBLOCK)
5864 {
5865 JSObject **chainp;
5866
5867 /* Grab the result of the expression. */
5868 if (op == JSOP_LEAVEBLOCKEXPR)
5869 rval = FETCH_OPND(-1);
5870
5871 chainp = &fp->blockChain;
5872 obj = *chainp;
5873 if (!obj) {
5874 chainp = &fp->scopeChain;
5875 obj = *chainp;
5876
5877 /*
5878 * This block was cloned, so clear its private data and sync
5879 * its locals to their property slots.
5880 */
5881 SAVE_SP_AND_PC(fp);
5882 ok = js_PutBlockObject(cx, obj);
5883 if (!ok)
5884 goto out;
5885 }
5886
5887 sp -= GET_UINT16(pc);
5888 JS_ASSERT(fp->spbase <= sp && sp <= fp->spbase + depth);
5889
5890 /* Store the result into the topmost stack slot. */
5891 if (op == JSOP_LEAVEBLOCKEXPR)
5892 STORE_OPND(-1, rval);
5893
5894 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
5895 JS_ASSERT(op == JSOP_LEAVEBLOCKEXPR
5896 ? fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp - 1
5897 : fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp);
5898
5899 *chainp = OBJ_GET_PARENT(cx, obj);
5900 JS_ASSERT(chainp != &fp->blockChain ||
5901 !*chainp ||
5902 OBJ_GET_CLASS(cx, *chainp) == &js_BlockClass);
5903 }
5904 END_CASE(JSOP_LEAVEBLOCK)
5905
5906 BEGIN_CASE(JSOP_GETLOCAL)
5907 slot = GET_UINT16(pc);
5908 JS_ASSERT(slot < (uintN)depth);
5909 PUSH_OPND(fp->spbase[slot]);
5910 obj = NULL;
5911 END_CASE(JSOP_GETLOCAL)
5912
5913 BEGIN_CASE(JSOP_SETLOCAL)
5914 slot = GET_UINT16(pc);
5915 JS_ASSERT(slot < (uintN)depth);
5916 vp = &fp->spbase[slot];
5917 GC_POKE(cx, *vp);
5918 *vp = FETCH_OPND(-1);
5919 obj = NULL;
5920 END_CASE(JSOP_SETLOCAL)
5921
5922 /* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */
5923 #define FAST_LOCAL_INCREMENT_OP(PRE,OPEQ,MINMAX) \
5924 slot = GET_UINT16(pc); \
5925 JS_ASSERT(slot < (uintN)depth); \
5926 vp = fp->spbase + slot; \
5927 rval = *vp; \
5928 if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \
5929 goto do_nonint_fast_incop; \
5930 PRE = rval; \
5931 rval OPEQ 2; \
5932 *vp = rval; \
5933 PUSH_OPND(PRE)
5934
5935 BEGIN_CASE(JSOP_INCLOCAL)
5936 FAST_LOCAL_INCREMENT_OP(rval, +=, MAX);
5937 END_CASE(JSOP_INCLOCAL)
5938
5939 BEGIN_CASE(JSOP_DECLOCAL)
5940 FAST_LOCAL_INCREMENT_OP(rval, -=, MIN);
5941 END_CASE(JSOP_DECLOCAL)
5942
5943 BEGIN_CASE(JSOP_LOCALINC)
5944 FAST_LOCAL_INCREMENT_OP(rtmp, +=, MAX);
5945 END_CASE(JSOP_LOCALINC)
5946
5947 BEGIN_CASE(JSOP_LOCALDEC)
5948 FAST_LOCAL_INCREMENT_OP(rtmp, -=, MIN);
5949 END_CASE(JSOP_LOCALDEC)
5950
5951 #undef FAST_LOCAL_INCREMENT_OP
5952
5953 EMPTY_CASE(JSOP_STARTITER)
5954
5955 BEGIN_CASE(JSOP_ENDITER)
5956 JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-1]));
5957 iterobj = JSVAL_TO_OBJECT(sp[-1]);
5958
5959 /*
5960 * js_CloseNativeIterator checks whether the iterator is not
5961 * native, and also detects the case of a native iterator that
5962 * has already escaped, even though a for-in loop caused it to
5963 * be created. See jsiter.c.
5964 */
5965 SAVE_SP_AND_PC(fp);
5966 js_CloseNativeIterator(cx, iterobj);
5967 *--sp = JSVAL_NULL;
5968 END_CASE(JSOP_ENDITER)
5969
5970 #if JS_HAS_GENERATORS
5971 BEGIN_CASE(JSOP_GENERATOR)
5972 pc += JSOP_GENERATOR_LENGTH;
5973 SAVE_SP_AND_PC(fp);
5974 obj = js_NewGenerator(cx, fp);
5975 if (!obj) {
5976 ok = JS_FALSE;
5977 } else {
5978 JS_ASSERT(!fp->callobj && !fp->argsobj);
5979 fp->rval = OBJECT_TO_JSVAL(obj);
5980 }
5981 goto out;
5982
5983 BEGIN_CASE(JSOP_YIELD)
5984 ASSERT_NOT_THROWING(cx);
5985 if (fp->flags & JSFRAME_FILTERING) {
5986 /* FIXME: bug 309894 -- fix to eliminate this error. */
5987 JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
5988 JSMSG_YIELD_FROM_FILTER);
5989 ok = JS_FALSE;
5990 goto out;
5991 }
5992 if (FRAME_TO_GENERATOR(fp)->state == JSGEN_CLOSING) {
5993 str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
5994 fp->argv[-2], NULL);
5995 if (str) {
5996 JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
5997 JSMSG_BAD_GENERATOR_YIELD,
5998 JSSTRING_CHARS(str));
5999 }
6000 ok = JS_FALSE;
6001 goto out;
6002 }
6003 fp->rval = FETCH_OPND(-1);
6004 fp->flags |= JSFRAME_YIELDING;
6005 pc += JSOP_YIELD_LENGTH;
6006 SAVE_SP_AND_PC(fp);
6007 goto out;
6008
6009 BEGIN_CASE(JSOP_ARRAYPUSH)
6010 slot = GET_UINT16(pc);
6011 JS_ASSERT(slot < (uintN)depth);
6012 lval = fp->spbase[slot];
6013 obj = JSVAL_TO_OBJECT(lval);
6014 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass);
6015 rval = FETCH_OPND(-1);
6016
6017 /* We know that the array is created with only a 'length' slot. */
6018 i = obj->map->freeslot - (JSSLOT_FREE(&js_ArrayClass) + 1);
6019 id = INT_TO_JSID(i);
6020
6021 SAVE_SP_AND_PC(fp);
6022 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
6023 if (!ok)
6024 goto out;
6025 --sp;
6026 END_CASE(JSOP_ARRAYPUSH)
6027 #endif /* JS_HAS_GENERATORS */
6028
6029 #if !JS_HAS_GENERATORS
6030 L_JSOP_GENERATOR:
6031 L_JSOP_YIELD:
6032 L_JSOP_ARRAYPUSH:
6033 #endif
6034
6035 #if !JS_HAS_DESTRUCTURING
6036 L_JSOP_FOREACHKEYVAL:
6037 L_JSOP_ENUMCONSTELEM:
6038 #endif
6039
6040 #ifdef JS_THREADED_INTERP
6041 L_JSOP_BACKPATCH:
6042 L_JSOP_BACKPATCH_POP:
6043 #else
6044 default:
6045 #endif
6046 {
6047 char numBuf[12];
6048 JS_snprintf(numBuf, sizeof numBuf, "%d", op);
6049 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6050 JSMSG_BAD_BYTECODE, numBuf);
6051 ok = JS_FALSE;
6052 goto out;
6053 }
6054
6055 #ifndef JS_THREADED_INTERP
6056
6057 } /* switch (op) */
6058
6059 advance_pc:
6060 pc += len;
6061
6062 #ifdef DEBUG
6063 if (tracefp) {
6064 intN ndefs, n;
6065 jsval *siter;
6066
6067 ndefs = js_CodeSpec[op].ndefs;
6068 if (ndefs) {
6069 SAVE_SP_AND_PC(fp);
6070 if (op == JSOP_FORELEM && sp[-1] == JSVAL_FALSE)
6071 --ndefs;
6072 for (n = -ndefs; n < 0; n++) {
6073 str = js_DecompileValueGenerator(cx, n, sp[n], NULL);
6074 if (str) {
6075 fprintf(tracefp, "%s %s",
6076 (n == -ndefs) ? " output:" : ",",
6077 JS_GetStringBytes(str));
6078 }
6079 }
6080 fprintf(tracefp, " @ %d\n", sp - fp->spbase);
6081 }
6082 fprintf(tracefp, " stack: ");
6083 for (siter = fp->spbase; siter < sp; siter++) {
6084 str = js_ValueToSource(cx, *siter);
6085 fprintf(tracefp, "%s ",
6086 str ? JS_GetStringBytes(str) : "<null>");
6087 }
6088 fputc('\n', tracefp);
6089 }
6090 #endif /* DEBUG */
6091 }
6092 #endif /* !JS_THREADED_INTERP */
6093
6094 out:
6095 if (!ok) {
6096 /*
6097 * Has an exception been raised? Also insist that we are not in an
6098 * XML filtering predicate expression, to avoid catching exceptions
6099 * within the filtering predicate, such as this example taken from
6100 * tests/e4x/Regress/regress-301596.js:
6101 *
6102 * try {
6103 * <xml/>.(@a == 1);
6104 * throw 5;
6105 * } catch (e) {
6106 * }
6107 *
6108 * The inner interpreter activation executing the predicate bytecode
6109 * will throw "reference to undefined XML name @a" (or 5, in older
6110 * versions that followed the first edition of ECMA-357 and evaluated
6111 * unbound identifiers to undefined), and the exception must not be
6112 * caught until control unwinds to the outer interpreter activation.
6113 *
6114 * Otherwise, the wrong stack depth will be restored by JSOP_SETSP,
6115 * and the catch will move into the filtering predicate expression,
6116 * leading to double catch execution if it rethrows.
6117 *
6118 * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=309894
6119 */
6120 if (cx->throwing && !(fp->flags & JSFRAME_FILTERING)) {
6121 /*
6122 * Call debugger throw hook if set (XXX thread safety?).
6123 */
6124 JSTrapHandler handler = rt->throwHook;
6125 if (handler) {
6126 SAVE_SP_AND_PC(fp);
6127 switch (handler(cx, script, pc, &rval, rt->throwHookData)) {
6128 case JSTRAP_ERROR:
6129 cx->throwing = JS_FALSE;
6130 goto no_catch;
6131 case JSTRAP_RETURN:
6132 ok = JS_TRUE;
6133 cx->throwing = JS_FALSE;
6134 fp->rval = rval;
6135 goto no_catch;
6136 case JSTRAP_THROW:
6137 cx->exception = rval;
6138 case JSTRAP_CONTINUE:
6139 default:;
6140 }
6141 LOAD_INTERRUPT_HANDLER(rt);
6142 }
6143
6144 /*
6145 * Look for a try block in script that can catch this exception.
6146 */
6147 #if JS_HAS_GENERATORS
6148 if (JS_LIKELY(cx->exception != JSVAL_ARETURN)) {
6149 SCRIPT_FIND_CATCH_START(script, pc, pc);
6150 if (!pc)
6151 goto no_catch;
6152 } else {
6153 pc = js_FindFinallyHandler(script, pc);
6154 if (!pc) {
6155 cx->throwing = JS_FALSE;
6156 ok = JS_TRUE;
6157 fp->rval = JSVAL_VOID;
6158 goto no_catch;
6159 }
6160 }
6161 #else
6162 SCRIPT_FIND_CATCH_START(script, pc, pc);
6163 if (!pc)
6164 goto no_catch;
6165 #endif
6166
6167 /* Don't clear cx->throwing to save cx->exception from GC. */
6168 len = 0;
6169 ok = JS_TRUE;
6170 DO_NEXT_OP(len);
6171 }
6172 no_catch:;
6173 }
6174
6175 /*
6176 * Check whether control fell off the end of a lightweight function, or an
6177 * exception thrown under such a function was not caught by it. If so, go
6178 * to the inline code under JSOP_RETURN.
6179 */
6180 if (inlineCallCount)
6181 goto inline_return;
6182
6183 /*
6184 * Reset sp before freeing stack slots, because our caller may GC soon.
6185 * Clear spbase to indicate that we've popped the 2 * depth operand slots.
6186 * Restore the previous frame's execution state.
6187 */
6188 if (JS_LIKELY(mark != NULL)) {
6189 /* If fp has blocks on its scope chain, home their locals now. */
6190 if (fp->flags & JSFRAME_POP_BLOCKS) {
6191 SAVE_SP_AND_PC(fp);
6192 ok &= PutBlockObjects(cx, fp);
6193 }
6194
6195 fp->sp = fp->spbase;
6196 fp->spbase = NULL;
6197 js_FreeRawStack(cx, mark);
6198 } else {
6199 SAVE_SP(fp);
6200 }
6201
6202 out2:
6203 if (cx->version == currentVersion && currentVersion != originalVersion)
6204 js_SetVersion(cx, originalVersion);
6205 cx->interpLevel--;
6206 return ok;
6207
6208 atom_not_defined:
6209 {
6210 const char *printable = js_AtomToPrintableString(cx, atom);
6211 if (printable)
6212 js_ReportIsNotDefined(cx, printable);
6213 ok = JS_FALSE;
6214 goto out;
6215 }
6216 }
6217