Bug 506721 - Convert JSVAL_TO_INT and INT_TO_JSVAL to functions. r=Waldo.
[mozilla-central.git] / js / src / jsinterp.cpp
blob1aaa1df5fc7bbe5401b48b75e60c82dd1d4b1c93
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=79:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
25 * Contributor(s):
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
42 * JavaScript bytecode interpreter.
44 #include <stdio.h>
45 #include <string.h>
46 #include <math.h>
47 #include "jstypes.h"
48 #include "jsstdint.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 "jsdate.h"
58 #include "jsversion.h"
59 #include "jsdbgapi.h"
60 #include "jsfun.h"
61 #include "jsgc.h"
62 #include "jsinterp.h"
63 #include "jsiter.h"
64 #include "jslock.h"
65 #include "jsnum.h"
66 #include "jsobj.h"
67 #include "jsopcode.h"
68 #include "jsscan.h"
69 #include "jsscope.h"
70 #include "jsscript.h"
71 #include "jsstr.h"
72 #include "jsstaticcheck.h"
73 #include "jstracer.h"
74 #include "jslibmath.h"
76 #ifdef INCLUDE_MOZILLA_DTRACE
77 #include "jsdtracef.h"
78 #endif
80 #if JS_HAS_XML_SUPPORT
81 #include "jsxml.h"
82 #endif
84 #include "jsatominlines.h"
86 #include "jsautooplen.h"
88 /* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */
89 #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___
91 JS_REQUIRES_STACK JSPropCacheEntry *
92 js_FillPropertyCache(JSContext *cx, JSObject *obj,
93 uintN scopeIndex, uintN protoIndex, JSObject *pobj,
94 JSScopeProperty *sprop, JSBool adding)
96 JSPropertyCache *cache;
97 jsbytecode *pc;
98 JSScope *scope;
99 jsuword kshape, vshape, khash;
100 JSOp op;
101 const JSCodeSpec *cs;
102 jsuword vword;
103 ptrdiff_t pcoff;
104 JSAtom *atom;
105 JSPropCacheEntry *entry;
107 JS_ASSERT(!cx->runtime->gcRunning);
108 cache = &JS_PROPERTY_CACHE(cx);
110 /* FIXME bug 489098: consider enabling the property cache for eval. */
111 if (js_IsPropertyCacheDisabled(cx) || (cx->fp->flags & JSFRAME_EVAL)) {
112 PCMETER(cache->disfills++);
113 return JS_NO_PROP_CACHE_FILL;
117 * Check for fill from js_SetPropertyHelper where the setter removed sprop
118 * from pobj's scope (via unwatch or delete, e.g.).
120 scope = OBJ_SCOPE(pobj);
121 if (!scope->has(sprop)) {
122 PCMETER(cache->oddfills++);
123 return JS_NO_PROP_CACHE_FILL;
127 * Check for overdeep scope and prototype chain. Because resolve, getter,
128 * and setter hooks can change the prototype chain using JS_SetPrototype
129 * after js_LookupPropertyWithFlags has returned the nominal protoIndex,
130 * we have to validate protoIndex if it is non-zero. If it is zero, then
131 * we know thanks to the scope->has test above, combined with the fact that
132 * obj == pobj, that protoIndex is invariant.
134 * The scopeIndex can't be wrong. We require JS_SetParent calls to happen
135 * before any running script might consult a parent-linked scope chain. If
136 * this requirement is not satisfied, the fill in progress will never hit,
137 * but vcap vs. scope shape tests ensure nothing malfunctions.
139 JS_ASSERT_IF(scopeIndex == 0 && protoIndex == 0, obj == pobj);
141 if (protoIndex != 0) {
142 JSObject *tmp = obj;
144 for (uintN i = 0; i != scopeIndex; i++)
145 tmp = OBJ_GET_PARENT(cx, tmp);
146 JS_ASSERT(tmp != pobj);
148 protoIndex = 1;
149 for (;;) {
150 tmp = OBJ_GET_PROTO(cx, tmp);
153 * We cannot cache properties coming from native objects behind
154 * non-native ones on the prototype chain. The non-natives can
155 * mutate in arbitrary way without changing any shapes.
157 if (!tmp || !OBJ_IS_NATIVE(tmp)) {
158 PCMETER(cache->noprotos++);
159 return JS_NO_PROP_CACHE_FILL;
161 if (tmp == pobj)
162 break;
163 ++protoIndex;
167 if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) {
168 PCMETER(cache->longchains++);
169 return JS_NO_PROP_CACHE_FILL;
173 * Optimize the cached vword based on our parameters and the current pc's
174 * opcode format flags.
176 pc = cx->fp->regs->pc;
177 op = js_GetOpcode(cx, cx->fp->script, pc);
178 cs = &js_CodeSpec[op];
179 kshape = 0;
181 do {
183 * Check for a prototype "plain old method" callee computation. What
184 * is a plain old method? It's a function-valued property with stub
185 * getter, so get of a function is idempotent.
187 if ((cs->format & JOF_CALLOP) &&
188 SPROP_HAS_STUB_GETTER(sprop) &&
189 SPROP_HAS_VALID_SLOT(sprop, scope)) {
190 jsval v;
192 v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
193 if (VALUE_IS_FUNCTION(cx, v)) {
195 * Great, we have a function-valued prototype property where
196 * the getter is JS_PropertyStub. The type id in pobj's scope
197 * does not evolve with changes to property values, however.
199 * So here, on first cache fill for this method, we brand the
200 * scope with a new shape and set the SCOPE_BRANDED flag. Once
201 * this scope flag is set, any write to a function-valued plain
202 * old property in pobj will result in shape being regenerated.
204 if (!scope->branded()) {
205 PCMETER(cache->brandfills++);
206 #ifdef DEBUG_notme
207 fprintf(stderr,
208 "branding %p (%s) for funobj %p (%s), shape %lu\n",
209 pobj, LOCKED_OBJ_GET_CLASS(pobj)->name,
210 JSVAL_TO_OBJECT(v),
211 JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v))),
212 OBJ_SHAPE(obj));
213 #endif
214 scope->brandingShapeChange(cx, sprop->slot, v);
215 if (js_IsPropertyCacheDisabled(cx)) /* check for rt->shapeGen overflow */
216 return JS_NO_PROP_CACHE_FILL;
217 scope->setBranded();
219 vword = JSVAL_OBJECT_TO_PCVAL(v);
220 break;
224 /* If getting a value via a stub getter, we can cache the slot. */
225 if (!(cs->format & (JOF_SET | JOF_INCDEC | JOF_FOR)) &&
226 SPROP_HAS_STUB_GETTER(sprop) &&
227 SPROP_HAS_VALID_SLOT(sprop, scope)) {
228 /* Great, let's cache sprop's slot and use it on cache hit. */
229 vword = SLOT_TO_PCVAL(sprop->slot);
230 } else {
231 /* Best we can do is to cache sprop (still a nice speedup). */
232 vword = SPROP_TO_PCVAL(sprop);
233 if (adding &&
234 sprop == scope->lastProp &&
235 scope->shape == sprop->shape) {
237 * Our caller added a new property. We also know that a setter
238 * that js_NativeSet could have run has not mutated the scope
239 * so the added property is still the last one added and the
240 * scope is not branded.
242 * We want to cache under scope's shape before the property
243 * addition to bias for the case when the mutator opcode
244 * always adds the same property. It allows to optimize
245 * periodic execution of object initializers or explicit
246 * initialization sequences like
248 * obj = {}; obj.x = 1; obj.y = 2;
250 * We assume that on average the win from this optimization is
251 * bigger that the cost of an extra mismatch per loop due to
252 * the bias for the following case:
254 * obj = {}; ... for (...) { ... obj.x = ... }
256 * On the first iteration JSOP_SETPROP fills the cache with
257 * the shape of newly created object, not the shape after
258 * obj.x is assigned. That mismatches obj's shape on the
259 * second iteration. Note that on third and the following
260 * iterations the cache will be hit since the shape no longer
261 * mutates.
263 JS_ASSERT(scope->owned());
264 if (sprop->parent) {
265 kshape = sprop->parent->shape;
266 } else {
268 * If obj had its own empty scope before, with a unique
269 * shape, that is lost. Here we only attempt to find a
270 * matching empty scope. In unusual cases involving
271 * __proto__ assignment we may not find one.
273 JSObject *proto = STOBJ_GET_PROTO(obj);
274 if (!proto || !OBJ_IS_NATIVE(proto))
275 return JS_NO_PROP_CACHE_FILL;
276 JSScope *protoscope = OBJ_SCOPE(proto);
277 if (!protoscope->emptyScope ||
278 !js_ObjectIsSimilarToProto(cx, obj, obj->map->ops, OBJ_GET_CLASS(cx, obj),
279 proto)) {
280 return JS_NO_PROP_CACHE_FILL;
282 kshape = protoscope->emptyScope->shape;
286 * When adding we predict no prototype object will later gain a
287 * readonly property or setter.
289 vshape = cx->runtime->protoHazardShape;
292 } while (0);
294 if (kshape == 0) {
295 kshape = OBJ_SHAPE(obj);
296 vshape = scope->shape;
299 khash = PROPERTY_CACHE_HASH_PC(pc, kshape);
300 if (obj == pobj) {
301 JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
302 } else {
303 if (op == JSOP_LENGTH) {
304 atom = cx->runtime->atomState.lengthAtom;
305 } else {
306 pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
307 GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
310 #ifdef DEBUG
311 if (scopeIndex == 0) {
312 JS_ASSERT(protoIndex != 0);
313 JS_ASSERT((protoIndex == 1) == (OBJ_GET_PROTO(cx, obj) == pobj));
315 #endif
317 if (scopeIndex != 0 || protoIndex != 1) {
318 khash = PROPERTY_CACHE_HASH_ATOM(atom, obj);
319 PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1)
320 cache->pcrecycles++);
321 pc = (jsbytecode *) atom;
322 kshape = (jsuword) obj;
325 * Make sure that a later shadowing assignment will enter
326 * PurgeProtoChain and invalidate this entry, bug 479198.
328 * This is thread-safe even though obj is not locked. Only the
329 * DELEGATE bit of obj->classword can change at runtime, given that
330 * obj is native; and the bit is only set, never cleared. And on
331 * platforms where another CPU can fail to see this write, it's OK
332 * because the property cache and JIT cache are thread-local.
334 OBJ_SET_DELEGATE(cx, obj);
338 entry = &cache->table[khash];
339 PCMETER(PCVAL_IS_NULL(entry->vword) || cache->recycles++);
340 entry->kpc = pc;
341 entry->kshape = kshape;
342 entry->vcap = PCVCAP_MAKE(vshape, scopeIndex, protoIndex);
343 entry->vword = vword;
345 cache->empty = JS_FALSE;
346 PCMETER(cache->fills++);
349 * The modfills counter is not exact. It increases if a getter or setter
350 * recurse into the interpreter.
352 PCMETER(entry == cache->pctestentry || cache->modfills++);
353 PCMETER(cache->pctestentry = NULL);
354 return entry;
357 JS_REQUIRES_STACK JSAtom *
358 js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
359 JSObject **objp, JSObject **pobjp,
360 JSPropCacheEntry **entryp)
362 JSOp op;
363 const JSCodeSpec *cs;
364 ptrdiff_t pcoff;
365 JSAtom *atom;
366 JSObject *obj, *pobj, *tmp;
367 JSPropCacheEntry *entry;
368 uint32 vcap;
370 JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code)
371 < cx->fp->script->length);
373 op = js_GetOpcode(cx, cx->fp->script, pc);
374 cs = &js_CodeSpec[op];
375 if (op == JSOP_LENGTH) {
376 atom = cx->runtime->atomState.lengthAtom;
377 } else {
378 pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
379 GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
382 obj = *objp;
383 JS_ASSERT(OBJ_IS_NATIVE(obj));
384 entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_ATOM(atom, obj)];
385 *entryp = entry;
386 vcap = entry->vcap;
388 if (entry->kpc != (jsbytecode *) atom) {
389 PCMETER(JS_PROPERTY_CACHE(cx).idmisses++);
391 #ifdef DEBUG_notme
392 entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_PC(pc, OBJ_SHAPE(obj))];
393 fprintf(stderr,
394 "id miss for %s from %s:%u"
395 " (pc %u, kpc %u, kshape %u, shape %u)\n",
396 js_AtomToPrintableString(cx, atom),
397 cx->fp->script->filename,
398 js_PCToLineNumber(cx, cx->fp->script, pc),
399 pc - cx->fp->script->code,
400 entry->kpc - cx->fp->script->code,
401 entry->kshape,
402 OBJ_SHAPE(obj));
403 js_Disassemble1(cx, cx->fp->script, pc,
404 pc - cx->fp->script->code,
405 JS_FALSE, stderr);
406 #endif
408 return atom;
411 if (entry->kshape != (jsuword) obj) {
412 PCMETER(JS_PROPERTY_CACHE(cx).komisses++);
413 return atom;
416 pobj = obj;
418 if (JOF_MODE(cs->format) == JOF_NAME) {
419 while (vcap & (PCVCAP_SCOPEMASK << PCVCAP_PROTOBITS)) {
420 tmp = OBJ_GET_PARENT(cx, pobj);
421 if (!tmp || !OBJ_IS_NATIVE(tmp))
422 break;
423 pobj = tmp;
424 vcap -= PCVCAP_PROTOSIZE;
427 *objp = pobj;
430 while (vcap & PCVCAP_PROTOMASK) {
431 tmp = OBJ_GET_PROTO(cx, pobj);
432 if (!tmp || !OBJ_IS_NATIVE(tmp))
433 break;
434 pobj = tmp;
435 --vcap;
438 if (JS_LOCK_OBJ_IF_SHAPE(cx, pobj, PCVCAP_SHAPE(vcap))) {
439 #ifdef DEBUG
440 jsid id = ATOM_TO_JSID(atom);
442 id = js_CheckForStringIndex(id);
443 JS_ASSERT(OBJ_SCOPE(pobj)->lookup(id));
444 JS_ASSERT_IF(OBJ_SCOPE(pobj)->object, OBJ_SCOPE(pobj)->object == pobj);
445 #endif
446 *pobjp = pobj;
447 return NULL;
450 PCMETER(JS_PROPERTY_CACHE(cx).vcmisses++);
451 return atom;
454 #ifdef DEBUG
455 #define ASSERT_CACHE_IS_EMPTY(cache) \
456 JS_BEGIN_MACRO \
457 JSPropertyCache *cache_ = (cache); \
458 uintN i_; \
459 JS_ASSERT(cache_->empty); \
460 for (i_ = 0; i_ < PROPERTY_CACHE_SIZE; i_++) { \
461 JS_ASSERT(!cache_->table[i_].kpc); \
462 JS_ASSERT(!cache_->table[i_].kshape); \
463 JS_ASSERT(!cache_->table[i_].vcap); \
464 JS_ASSERT(!cache_->table[i_].vword); \
466 JS_END_MACRO
467 #else
468 #define ASSERT_CACHE_IS_EMPTY(cache) ((void)0)
469 #endif
471 JS_STATIC_ASSERT(PCVAL_NULL == 0);
473 void
474 js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache)
476 if (cache->empty) {
477 ASSERT_CACHE_IS_EMPTY(cache);
478 return;
481 memset(cache->table, 0, sizeof cache->table);
482 cache->empty = JS_TRUE;
484 #ifdef JS_PROPERTY_CACHE_METERING
485 { static FILE *fp;
486 if (!fp)
487 fp = fopen("/tmp/propcache.stats", "w");
488 if (fp) {
489 fputs("Property cache stats for ", fp);
490 #ifdef JS_THREADSAFE
491 fprintf(fp, "thread %lu, ", (unsigned long) cx->thread->id);
492 #endif
493 fprintf(fp, "GC %u\n", cx->runtime->gcNumber);
495 # define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)cache->mem)
496 P(fills);
497 P(nofills);
498 P(rofills);
499 P(disfills);
500 P(oddfills);
501 P(modfills);
502 P(brandfills);
503 P(noprotos);
504 P(longchains);
505 P(recycles);
506 P(pcrecycles);
507 P(tests);
508 P(pchits);
509 P(protopchits);
510 P(initests);
511 P(inipchits);
512 P(inipcmisses);
513 P(settests);
514 P(addpchits);
515 P(setpchits);
516 P(setpcmisses);
517 P(slotchanges);
518 P(setmisses);
519 P(idmisses);
520 P(komisses);
521 P(vcmisses);
522 P(misses);
523 P(flushes);
524 P(pcpurges);
525 # undef P
527 fprintf(fp, "hit rates: pc %g%% (proto %g%%), set %g%%, ini %g%%, full %g%%\n",
528 (100. * cache->pchits) / cache->tests,
529 (100. * cache->protopchits) / cache->tests,
530 (100. * (cache->addpchits + cache->setpchits))
531 / cache->settests,
532 (100. * cache->inipchits) / cache->initests,
533 (100. * (cache->tests - cache->misses)) / cache->tests);
534 fflush(fp);
537 #endif
539 PCMETER(cache->flushes++);
542 void
543 js_PurgePropertyCacheForScript(JSContext *cx, JSScript *script)
545 JSPropertyCache *cache;
546 JSPropCacheEntry *entry;
548 cache = &JS_PROPERTY_CACHE(cx);
549 for (entry = cache->table; entry < cache->table + PROPERTY_CACHE_SIZE;
550 entry++) {
551 if (JS_UPTRDIFF(entry->kpc, script->code) < script->length) {
552 entry->kpc = NULL;
553 entry->kshape = 0;
554 #ifdef DEBUG
555 entry->vcap = entry->vword = 0;
556 #endif
562 * Check if the current arena has enough space to fit nslots after sp and, if
563 * so, reserve the necessary space.
565 static JS_REQUIRES_STACK JSBool
566 AllocateAfterSP(JSContext *cx, jsval *sp, uintN nslots)
568 uintN surplus;
569 jsval *sp2;
571 JS_ASSERT((jsval *) cx->stackPool.current->base <= sp);
572 JS_ASSERT(sp <= (jsval *) cx->stackPool.current->avail);
573 surplus = (jsval *) cx->stackPool.current->avail - sp;
574 if (nslots <= surplus)
575 return JS_TRUE;
578 * No room before current->avail, check if the arena has enough space to
579 * fit the missing slots before the limit.
581 if (nslots > (size_t) ((jsval *) cx->stackPool.current->limit - sp))
582 return JS_FALSE;
584 JS_ARENA_ALLOCATE_CAST(sp2, jsval *, &cx->stackPool,
585 (nslots - surplus) * sizeof(jsval));
586 JS_ASSERT(sp2 == sp + surplus);
587 return JS_TRUE;
590 JS_STATIC_INTERPRET JS_REQUIRES_STACK jsval *
591 js_AllocRawStack(JSContext *cx, uintN nslots, void **markp)
593 jsval *sp;
595 JS_ASSERT(nslots != 0);
596 JS_ASSERT_NOT_ON_TRACE(cx);
598 if (!cx->stackPool.first.next) {
599 int64 *timestamp;
601 JS_ARENA_ALLOCATE_CAST(timestamp, int64 *,
602 &cx->stackPool, sizeof *timestamp);
603 if (!timestamp) {
604 js_ReportOutOfScriptQuota(cx);
605 return NULL;
607 *timestamp = JS_Now();
610 if (markp)
611 *markp = JS_ARENA_MARK(&cx->stackPool);
612 JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval));
613 if (!sp)
614 js_ReportOutOfScriptQuota(cx);
615 return sp;
618 JS_STATIC_INTERPRET JS_REQUIRES_STACK void
619 js_FreeRawStack(JSContext *cx, void *mark)
621 JS_ARENA_RELEASE(&cx->stackPool, mark);
624 JS_REQUIRES_STACK JS_FRIEND_API(jsval *)
625 js_AllocStack(JSContext *cx, uintN nslots, void **markp)
627 jsval *sp;
628 JSArena *a;
629 JSStackHeader *sh;
631 /* Callers don't check for zero nslots: we do to avoid empty segments. */
632 if (nslots == 0) {
633 *markp = NULL;
634 return (jsval *) JS_ARENA_MARK(&cx->stackPool);
637 /* Allocate 2 extra slots for the stack segment header we'll likely need. */
638 sp = js_AllocRawStack(cx, 2 + nslots, markp);
639 if (!sp)
640 return NULL;
642 /* Try to avoid another header if we can piggyback on the last segment. */
643 a = cx->stackPool.current;
644 sh = cx->stackHeaders;
645 if (sh && JS_STACK_SEGMENT(sh) + sh->nslots == sp) {
646 /* Extend the last stack segment, give back the 2 header slots. */
647 sh->nslots += nslots;
648 a->avail -= 2 * sizeof(jsval);
649 } else {
651 * Need a new stack segment, so allocate and push a stack segment
652 * header from the 2 extra slots.
654 sh = (JSStackHeader *)sp;
655 sh->nslots = nslots;
656 sh->down = cx->stackHeaders;
657 cx->stackHeaders = sh;
658 sp += 2;
662 * Store JSVAL_NULL using memset, to let compilers optimize as they see
663 * fit, in case a caller allocates and pushes GC-things one by one, which
664 * could nest a last-ditch GC that will scan this segment.
666 memset(sp, 0, nslots * sizeof(jsval));
667 return sp;
670 JS_REQUIRES_STACK JS_FRIEND_API(void)
671 js_FreeStack(JSContext *cx, void *mark)
673 JSStackHeader *sh;
674 jsuword slotdiff;
676 /* Check for zero nslots allocation special case. */
677 if (!mark)
678 return;
680 /* We can assert because js_FreeStack always balances js_AllocStack. */
681 sh = cx->stackHeaders;
682 JS_ASSERT(sh);
684 /* If mark is in the current segment, reduce sh->nslots, else pop sh. */
685 slotdiff = JS_UPTRDIFF(mark, JS_STACK_SEGMENT(sh)) / sizeof(jsval);
686 if (slotdiff < (jsuword)sh->nslots)
687 sh->nslots = slotdiff;
688 else
689 cx->stackHeaders = sh->down;
691 /* Release the stackPool space allocated since mark was set. */
692 JS_ARENA_RELEASE(&cx->stackPool, mark);
695 JSObject *
696 js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
698 JSObject *sharedBlock = fp->blockChain;
700 if (!sharedBlock) {
702 * Don't force a call object for a lightweight function call, but do
703 * insist that there is a call object for a heavyweight function call.
705 JS_ASSERT(!fp->fun ||
706 !(fp->fun->flags & JSFUN_HEAVYWEIGHT) ||
707 fp->callobj);
708 JS_ASSERT(fp->scopeChain);
709 return fp->scopeChain;
712 /* We don't handle cloning blocks on trace. */
713 js_LeaveTrace(cx);
716 * We have one or more lexical scopes to reflect into fp->scopeChain, so
717 * make sure there's a call object at the current head of the scope chain,
718 * if this frame is a call frame.
720 * Also, identify the innermost compiler-allocated block we needn't clone.
722 JSObject *limitBlock, *limitClone;
723 if (fp->fun && !fp->callobj) {
724 JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass ||
725 fp->scopeChain->getAssignedPrivate() != fp);
726 if (!js_GetCallObject(cx, fp))
727 return NULL;
729 /* We know we must clone everything on blockChain. */
730 limitBlock = limitClone = NULL;
731 } else {
733 * scopeChain includes all blocks whose static scope we're within that
734 * have already been cloned. Find the innermost such block. Its
735 * prototype should appear on blockChain; we'll clone blockChain up
736 * to, but not including, that prototype.
738 limitClone = fp->scopeChain;
739 while (OBJ_GET_CLASS(cx, limitClone) == &js_WithClass)
740 limitClone = OBJ_GET_PARENT(cx, limitClone);
741 JS_ASSERT(limitClone);
744 * It may seem like we don't know enough about limitClone to be able
745 * to just grab its prototype as we do here, but it's actually okay.
747 * If limitClone is a block object belonging to this frame, then its
748 * prototype is the innermost entry in blockChain that we have already
749 * cloned, and is thus the place to stop when we clone below.
751 * Otherwise, there are no blocks for this frame on scopeChain, and we
752 * need to clone the whole blockChain. In this case, limitBlock can
753 * point to any object known not to be on blockChain, since we simply
754 * loop until we hit limitBlock or NULL. If limitClone is a block, it
755 * isn't a block from this function, since blocks can't be nested
756 * within themselves on scopeChain (recursion is dynamic nesting, not
757 * static nesting). If limitClone isn't a block, its prototype won't
758 * be a block either. So we can just grab limitClone's prototype here
759 * regardless of its type or which frame it belongs to.
761 limitBlock = OBJ_GET_PROTO(cx, limitClone);
763 /* If the innermost block has already been cloned, we are done. */
764 if (limitBlock == sharedBlock)
765 return fp->scopeChain;
769 * Special-case cloning the innermost block; this doesn't have enough in
770 * common with subsequent steps to include in the loop.
772 * js_CloneBlockObject leaves the clone's parent slot uninitialized. We
773 * populate it below.
775 JSObject *innermostNewChild = js_CloneBlockObject(cx, sharedBlock, fp);
776 if (!innermostNewChild)
777 return NULL;
778 JSAutoTempValueRooter tvr(cx, innermostNewChild);
781 * Clone our way towards outer scopes until we reach the innermost
782 * enclosing function, or the innermost block we've already cloned.
784 JSObject *newChild = innermostNewChild;
785 for (;;) {
786 JS_ASSERT(OBJ_GET_PROTO(cx, newChild) == sharedBlock);
787 sharedBlock = OBJ_GET_PARENT(cx, sharedBlock);
789 /* Sometimes limitBlock will be NULL, so check that first. */
790 if (sharedBlock == limitBlock || !sharedBlock)
791 break;
793 /* As in the call above, we don't know the real parent yet. */
794 JSObject *clone
795 = js_CloneBlockObject(cx, sharedBlock, fp);
796 if (!clone)
797 return NULL;
800 * Avoid OBJ_SET_PARENT overhead as newChild cannot escape to
801 * other threads.
803 STOBJ_SET_PARENT(newChild, clone);
804 newChild = clone;
806 STOBJ_SET_PARENT(newChild, fp->scopeChain);
810 * If we found a limit block belonging to this frame, then we should have
811 * found it in blockChain.
813 JS_ASSERT_IF(limitBlock &&
814 OBJ_GET_CLASS(cx, limitBlock) == &js_BlockClass &&
815 limitClone->getAssignedPrivate() == fp,
816 sharedBlock);
818 /* Place our newly cloned blocks at the head of the scope chain. */
819 fp->scopeChain = innermostNewChild;
820 return fp->scopeChain;
823 JSBool
824 js_GetPrimitiveThis(JSContext *cx, jsval *vp, JSClass *clasp, jsval *thisvp)
826 jsval v;
827 JSObject *obj;
829 v = vp[1];
830 if (JSVAL_IS_OBJECT(v)) {
831 obj = JS_THIS_OBJECT(cx, vp);
832 if (!JS_InstanceOf(cx, obj, clasp, vp + 2))
833 return JS_FALSE;
834 v = obj->fslots[JSSLOT_PRIVATE];
836 *thisvp = v;
837 return JS_TRUE;
841 * ECMA requires "the global object", but in embeddings such as the browser,
842 * which have multiple top-level objects (windows, frames, etc. in the DOM),
843 * we prefer fun's parent. An example that causes this code to run:
845 * // in window w1
846 * function f() { return this }
847 * function g() { return f }
849 * // in window w2
850 * var h = w1.g()
851 * alert(h() == w1)
853 * The alert should display "true".
855 JS_STATIC_INTERPRET JSObject *
856 js_ComputeGlobalThis(JSContext *cx, JSBool lazy, jsval *argv)
858 JSObject *thisp;
860 if (JSVAL_IS_PRIMITIVE(argv[-2]) ||
861 !OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]))) {
862 thisp = cx->globalObject;
863 } else {
864 JSStackFrame *fp;
865 jsid id;
866 jsval v;
867 uintN attrs;
868 JSBool ok;
869 JSObject *parent;
872 * Walk up the parent chain, first checking that the running script
873 * has access to the callee's parent object. Note that if lazy, the
874 * running script whose principals we want to check is the script
875 * associated with fp->down, not with fp.
877 * FIXME: 417851 -- this access check should not be required, as it
878 * imposes a performance penalty on all js_ComputeGlobalThis calls,
879 * and it represents a maintenance hazard.
881 fp = js_GetTopStackFrame(cx); /* quell GCC overwarning */
882 if (lazy) {
883 JS_ASSERT(fp->argv == argv);
884 fp->dormantNext = cx->dormantFrameChain;
885 cx->dormantFrameChain = fp;
886 cx->fp = fp->down;
887 fp->down = NULL;
889 thisp = JSVAL_TO_OBJECT(argv[-2]);
890 id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
892 ok = OBJ_CHECK_ACCESS(cx, thisp, id, JSACC_PARENT, &v, &attrs);
893 if (lazy) {
894 cx->dormantFrameChain = fp->dormantNext;
895 fp->dormantNext = NULL;
896 fp->down = cx->fp;
897 cx->fp = fp;
899 if (!ok)
900 return NULL;
902 thisp = JSVAL_IS_VOID(v)
903 ? OBJ_GET_PARENT(cx, thisp)
904 : JSVAL_TO_OBJECT(v);
905 while ((parent = OBJ_GET_PARENT(cx, thisp)) != NULL)
906 thisp = parent;
909 /* Some objects (e.g., With) delegate 'this' to another object. */
910 thisp = OBJ_THIS_OBJECT(cx, thisp);
911 if (!thisp)
912 return NULL;
913 argv[-1] = OBJECT_TO_JSVAL(thisp);
914 return thisp;
917 static JSObject *
918 ComputeThis(JSContext *cx, JSBool lazy, jsval *argv)
920 JSObject *thisp;
922 JS_ASSERT(!JSVAL_IS_NULL(argv[-1]));
923 if (!JSVAL_IS_OBJECT(argv[-1])) {
924 if (!js_PrimitiveToObject(cx, &argv[-1]))
925 return NULL;
926 thisp = JSVAL_TO_OBJECT(argv[-1]);
927 } else {
928 thisp = JSVAL_TO_OBJECT(argv[-1]);
929 if (OBJ_GET_CLASS(cx, thisp) == &js_CallClass ||
930 OBJ_GET_CLASS(cx, thisp) == &js_BlockClass) {
931 return js_ComputeGlobalThis(cx, lazy, argv);
934 /* Some objects (e.g., With) delegate 'this' to another object. */
935 thisp = OBJ_THIS_OBJECT(cx, thisp);
936 if (!thisp)
937 return NULL;
938 argv[-1] = OBJECT_TO_JSVAL(thisp);
940 return thisp;
943 JSObject *
944 js_ComputeThis(JSContext *cx, JSBool lazy, jsval *argv)
946 if (JSVAL_IS_NULL(argv[-1]))
947 return js_ComputeGlobalThis(cx, lazy, argv);
948 return ComputeThis(cx, lazy, argv);
951 #if JS_HAS_NO_SUCH_METHOD
953 #define JSSLOT_FOUND_FUNCTION JSSLOT_PRIVATE
954 #define JSSLOT_SAVED_ID (JSSLOT_PRIVATE + 1)
956 JSClass js_NoSuchMethodClass = {
957 "NoSuchMethod",
958 JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
959 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
960 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
961 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
965 * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of
966 * the base object, we search for the __noSuchMethod__ method in the base.
967 * If it exists, we store the method and the property's id into an object of
968 * NoSuchMethod class and store this object into the callee's stack slot.
969 * Later, js_Invoke will recognise such an object and transfer control to
970 * NoSuchMethod that invokes the method like:
972 * this.__noSuchMethod__(id, args)
974 * where id is the name of the method that this invocation attempted to
975 * call by name, and args is an Array containing this invocation's actual
976 * parameters.
978 JS_STATIC_INTERPRET JSBool
979 js_OnUnknownMethod(JSContext *cx, jsval *vp)
981 JSObject *obj;
982 jsid id;
983 JSTempValueRooter tvr;
984 JSBool ok;
986 JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
987 obj = JSVAL_TO_OBJECT(vp[1]);
988 JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
990 MUST_FLOW_THROUGH("out");
991 id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
992 ok = js_GetMethod(cx, obj, id, false, &tvr.u.value);
993 if (!ok)
994 goto out;
995 if (JSVAL_IS_PRIMITIVE(tvr.u.value)) {
996 vp[0] = tvr.u.value;
997 } else {
998 #if JS_HAS_XML_SUPPORT
999 /* Extract the function name from function::name qname. */
1000 if (!JSVAL_IS_PRIMITIVE(vp[0])) {
1001 obj = JSVAL_TO_OBJECT(vp[0]);
1002 ok = js_IsFunctionQName(cx, obj, &id);
1003 if (!ok)
1004 goto out;
1005 if (id != 0)
1006 vp[0] = ID_TO_VALUE(id);
1008 #endif
1009 obj = js_NewObjectWithGivenProto(cx, &js_NoSuchMethodClass,
1010 NULL, NULL);
1011 if (!obj) {
1012 ok = JS_FALSE;
1013 goto out;
1015 obj->fslots[JSSLOT_FOUND_FUNCTION] = tvr.u.value;
1016 obj->fslots[JSSLOT_SAVED_ID] = vp[0];
1017 vp[0] = OBJECT_TO_JSVAL(obj);
1019 ok = JS_TRUE;
1021 out:
1022 JS_POP_TEMP_ROOT(cx, &tvr);
1023 return ok;
1026 static JS_REQUIRES_STACK JSBool
1027 NoSuchMethod(JSContext *cx, uintN argc, jsval *vp, uint32 flags)
1029 jsval *invokevp;
1030 void *mark;
1031 JSBool ok;
1032 JSObject *obj, *argsobj;
1034 invokevp = js_AllocStack(cx, 2 + 2, &mark);
1035 if (!invokevp)
1036 return JS_FALSE;
1038 JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[0]));
1039 JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
1040 obj = JSVAL_TO_OBJECT(vp[0]);
1041 JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NoSuchMethodClass);
1043 invokevp[0] = obj->fslots[JSSLOT_FOUND_FUNCTION];
1044 invokevp[1] = vp[1];
1045 invokevp[2] = obj->fslots[JSSLOT_SAVED_ID];
1046 argsobj = js_NewArrayObject(cx, argc, vp + 2);
1047 if (!argsobj) {
1048 ok = JS_FALSE;
1049 } else {
1050 invokevp[3] = OBJECT_TO_JSVAL(argsobj);
1051 ok = (flags & JSINVOKE_CONSTRUCT)
1052 ? js_InvokeConstructor(cx, 2, JS_TRUE, invokevp)
1053 : js_Invoke(cx, 2, invokevp, flags);
1054 vp[0] = invokevp[0];
1056 js_FreeStack(cx, mark);
1057 return ok;
1060 #endif /* JS_HAS_NO_SUCH_METHOD */
1063 * We check if the function accepts a primitive value as |this|. For that we
1064 * use a table that maps value's tag into the corresponding function flag.
1066 JS_STATIC_ASSERT(JSVAL_INT == 1);
1067 JS_STATIC_ASSERT(JSVAL_DOUBLE == 2);
1068 JS_STATIC_ASSERT(JSVAL_STRING == 4);
1069 JS_STATIC_ASSERT(JSVAL_BOOLEAN == 6);
1071 const uint16 js_PrimitiveTestFlags[] = {
1072 JSFUN_THISP_NUMBER, /* INT */
1073 JSFUN_THISP_NUMBER, /* DOUBLE */
1074 JSFUN_THISP_NUMBER, /* INT */
1075 JSFUN_THISP_STRING, /* STRING */
1076 JSFUN_THISP_NUMBER, /* INT */
1077 JSFUN_THISP_BOOLEAN, /* BOOLEAN */
1078 JSFUN_THISP_NUMBER /* INT */
1082 * Find a function reference and its 'this' object implicit first parameter
1083 * under argc arguments on cx's stack, and call the function. Push missing
1084 * required arguments, allocate declared local variables, and pop everything
1085 * when done. Then push the return value.
1087 JS_REQUIRES_STACK JS_FRIEND_API(JSBool)
1088 js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags)
1090 void *mark;
1091 JSStackFrame frame;
1092 jsval *sp, *argv, *newvp;
1093 jsval v;
1094 JSObject *funobj, *parent;
1095 JSBool ok;
1096 JSClass *clasp;
1097 JSObjectOps *ops;
1098 JSNative native;
1099 JSFunction *fun;
1100 JSScript *script;
1101 uintN nslots, i;
1102 uint32 rootedArgsFlag;
1103 JSInterpreterHook hook;
1104 void *hookData;
1106 /* [vp .. vp + 2 + argc) must belong to the last JS stack arena. */
1107 JS_ASSERT((jsval *) cx->stackPool.current->base <= vp);
1108 JS_ASSERT(vp + 2 + argc <= (jsval *) cx->stackPool.current->avail);
1110 /* Mark the top of stack and load frequently-used registers. */
1111 mark = JS_ARENA_MARK(&cx->stackPool);
1112 MUST_FLOW_THROUGH("out2");
1113 v = *vp;
1115 if (JSVAL_IS_PRIMITIVE(v))
1116 goto bad;
1118 funobj = JSVAL_TO_OBJECT(v);
1119 parent = OBJ_GET_PARENT(cx, funobj);
1120 clasp = OBJ_GET_CLASS(cx, funobj);
1121 if (clasp != &js_FunctionClass) {
1122 #if JS_HAS_NO_SUCH_METHOD
1123 if (clasp == &js_NoSuchMethodClass) {
1124 ok = NoSuchMethod(cx, argc, vp, flags);
1125 goto out2;
1127 #endif
1129 /* Function is inlined, all other classes use object ops. */
1130 ops = funobj->map->ops;
1133 * XXX this makes no sense -- why convert to function if clasp->call?
1134 * XXX better to call that hook without converting
1136 * FIXME bug 408416: try converting to function, for API compatibility
1137 * if there is a call op defined.
1139 if ((ops == &js_ObjectOps) ? clasp->call : ops->call) {
1140 ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v);
1141 if (!ok)
1142 goto out2;
1144 if (VALUE_IS_FUNCTION(cx, v)) {
1145 /* Make vp refer to funobj to keep it available as argv[-2]. */
1146 *vp = v;
1147 funobj = JSVAL_TO_OBJECT(v);
1148 parent = OBJ_GET_PARENT(cx, funobj);
1149 goto have_fun;
1152 fun = NULL;
1153 script = NULL;
1154 nslots = 0;
1156 /* Try a call or construct native object op. */
1157 if (flags & JSINVOKE_CONSTRUCT) {
1158 if (!JSVAL_IS_OBJECT(vp[1])) {
1159 ok = js_PrimitiveToObject(cx, &vp[1]);
1160 if (!ok)
1161 goto out2;
1163 native = ops->construct;
1164 } else {
1165 native = ops->call;
1167 if (!native)
1168 goto bad;
1169 } else {
1170 have_fun:
1171 /* Get private data and set derived locals from it. */
1172 fun = GET_FUNCTION_PRIVATE(cx, funobj);
1173 nslots = FUN_MINARGS(fun);
1174 nslots = (nslots > argc) ? nslots - argc : 0;
1175 if (FUN_INTERPRETED(fun)) {
1176 native = NULL;
1177 script = fun->u.i.script;
1178 JS_ASSERT(script);
1179 } else {
1180 native = fun->u.n.native;
1181 script = NULL;
1182 nslots += fun->u.n.extra;
1185 if (JSFUN_BOUND_METHOD_TEST(fun->flags)) {
1186 /* Handle bound method special case. */
1187 vp[1] = OBJECT_TO_JSVAL(parent);
1188 } else if (!JSVAL_IS_OBJECT(vp[1])) {
1189 JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT));
1190 if (PRIMITIVE_THIS_TEST(fun, vp[1]))
1191 goto start_call;
1195 if (flags & JSINVOKE_CONSTRUCT) {
1196 JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
1197 } else {
1199 * We must call js_ComputeThis in case we are not called from the
1200 * interpreter, where a prior bytecode has computed an appropriate
1201 * |this| already.
1203 * But we need to compute |this| eagerly only for so-called "slow"
1204 * (i.e., not fast) native functions. Fast natives must use either
1205 * JS_THIS or JS_THIS_OBJECT, and scripted functions will go through
1206 * the appropriate this-computing bytecode, e.g., JSOP_THIS.
1208 if (native && (!fun || !(fun->flags & JSFUN_FAST_NATIVE))) {
1209 if (!js_ComputeThis(cx, JS_FALSE, vp + 2)) {
1210 ok = JS_FALSE;
1211 goto out2;
1213 flags |= JSFRAME_COMPUTED_THIS;
1217 start_call:
1218 if (native && fun && (fun->flags & JSFUN_FAST_NATIVE)) {
1219 #ifdef DEBUG_NOT_THROWING
1220 JSBool alreadyThrowing = cx->throwing;
1221 #endif
1222 JS_ASSERT(nslots == 0);
1223 #if JS_HAS_LVALUE_RETURN
1224 /* Set by JS_SetCallReturnValue2, used to return reference types. */
1225 cx->rval2set = JS_FALSE;
1226 #endif
1227 ok = ((JSFastNative) native)(cx, argc, vp);
1228 JS_RUNTIME_METER(cx->runtime, nativeCalls);
1229 #ifdef DEBUG_NOT_THROWING
1230 if (ok && !alreadyThrowing)
1231 ASSERT_NOT_THROWING(cx);
1232 #endif
1233 goto out2;
1236 argv = vp + 2;
1237 sp = argv + argc;
1239 rootedArgsFlag = JSFRAME_ROOTED_ARGV;
1240 if (nslots != 0) {
1242 * The extra slots required by the function continue with argument
1243 * slots. Thus, when the last stack pool arena does not have room to
1244 * fit nslots right after sp and AllocateAfterSP fails, we have to copy
1245 * [vp..vp+2+argc) slots and clear rootedArgsFlag to root the copy.
1247 if (!AllocateAfterSP(cx, sp, nslots)) {
1248 rootedArgsFlag = 0;
1249 newvp = js_AllocRawStack(cx, 2 + argc + nslots, NULL);
1250 if (!newvp) {
1251 ok = JS_FALSE;
1252 goto out2;
1254 memcpy(newvp, vp, (2 + argc) * sizeof(jsval));
1255 argv = newvp + 2;
1256 sp = argv + argc;
1259 /* Push void to initialize missing args. */
1260 i = nslots;
1261 do {
1262 *sp++ = JSVAL_VOID;
1263 } while (--i != 0);
1266 /* Allocate space for local variables and stack of interpreted function. */
1267 if (script && script->nslots != 0) {
1268 if (!AllocateAfterSP(cx, sp, script->nslots)) {
1269 /* NB: Discontinuity between argv and slots, stack slots. */
1270 sp = js_AllocRawStack(cx, script->nslots, NULL);
1271 if (!sp) {
1272 ok = JS_FALSE;
1273 goto out2;
1277 /* Push void to initialize local variables. */
1278 for (jsval *end = sp + fun->u.i.nvars; sp != end; ++sp)
1279 *sp = JSVAL_VOID;
1283 * Initialize the frame.
1285 * To set thisp we use an explicit cast and not JSVAL_TO_OBJECT, as vp[1]
1286 * can be a primitive value here for those native functions specified with
1287 * JSFUN_THISP_(NUMBER|STRING|BOOLEAN) flags.
1289 frame.thisp = (JSObject *)vp[1];
1290 frame.varobj = NULL;
1291 frame.callobj = NULL;
1292 frame.argsobj = NULL;
1293 frame.script = script;
1294 frame.callee = funobj;
1295 frame.fun = fun;
1296 frame.argc = argc;
1297 frame.argv = argv;
1299 /* Default return value for a constructor is the new object. */
1300 frame.rval = (flags & JSINVOKE_CONSTRUCT) ? vp[1] : JSVAL_VOID;
1301 frame.down = cx->fp;
1302 frame.annotation = NULL;
1303 frame.scopeChain = NULL; /* set below for real, after cx->fp is set */
1304 frame.blockChain = NULL;
1305 frame.regs = NULL;
1306 frame.imacpc = NULL;
1307 frame.slots = NULL;
1308 frame.sharpDepth = 0;
1309 frame.sharpArray = NULL;
1310 frame.flags = flags | rootedArgsFlag;
1311 frame.dormantNext = NULL;
1312 frame.xmlNamespace = NULL;
1313 frame.displaySave = NULL;
1315 MUST_FLOW_THROUGH("out");
1316 cx->fp = &frame;
1318 /* Init these now in case we goto out before first hook call. */
1319 hook = cx->debugHooks->callHook;
1320 hookData = NULL;
1322 if (native) {
1323 /* If native, use caller varobj and scopeChain for eval. */
1324 JS_ASSERT(!frame.varobj);
1325 JS_ASSERT(!frame.scopeChain);
1326 if (frame.down) {
1327 frame.varobj = frame.down->varobj;
1328 frame.scopeChain = frame.down->scopeChain;
1331 /* But ensure that we have a scope chain. */
1332 if (!frame.scopeChain)
1333 frame.scopeChain = parent;
1334 } else {
1335 /* Use parent scope so js_GetCallObject can find the right "Call". */
1336 frame.scopeChain = parent;
1337 if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) {
1338 /* Scope with a call object parented by the callee's parent. */
1339 if (!js_GetCallObject(cx, &frame)) {
1340 ok = JS_FALSE;
1341 goto out;
1344 frame.slots = sp - fun->u.i.nvars;
1347 /* Call the hook if present after we fully initialized the frame. */
1348 if (hook)
1349 hookData = hook(cx, &frame, JS_TRUE, 0, cx->debugHooks->callHookData);
1351 #ifdef INCLUDE_MOZILLA_DTRACE
1352 /* DTrace function entry, non-inlines */
1353 if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
1354 jsdtrace_function_entry(cx, &frame, fun);
1355 if (JAVASCRIPT_FUNCTION_INFO_ENABLED())
1356 jsdtrace_function_info(cx, &frame, frame.down, fun);
1357 if (JAVASCRIPT_FUNCTION_ARGS_ENABLED())
1358 jsdtrace_function_args(cx, &frame, fun, frame.argc, frame.argv);
1359 #endif
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
1367 #if JS_HAS_LVALUE_RETURN
1368 /* Set by JS_SetCallReturnValue2, used to return reference types. */
1369 cx->rval2set = JS_FALSE;
1370 #endif
1371 ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval);
1372 JS_RUNTIME_METER(cx->runtime, nativeCalls);
1373 #ifdef DEBUG_NOT_THROWING
1374 if (ok && !alreadyThrowing)
1375 ASSERT_NOT_THROWING(cx);
1376 #endif
1377 } else {
1378 JS_ASSERT(script);
1379 ok = js_Interpret(cx);
1382 #ifdef INCLUDE_MOZILLA_DTRACE
1383 /* DTrace function return, non-inlines */
1384 if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
1385 jsdtrace_function_rval(cx, &frame, fun, &frame.rval);
1386 if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
1387 jsdtrace_function_return(cx, &frame, fun);
1388 #endif
1390 out:
1391 if (hookData) {
1392 hook = cx->debugHooks->callHook;
1393 if (hook)
1394 hook(cx, &frame, JS_FALSE, &ok, hookData);
1397 ok &= frame.putActivationObjects(cx);
1399 *vp = frame.rval;
1401 /* Restore cx->fp now that we're done releasing frame objects. */
1402 cx->fp = frame.down;
1404 out2:
1405 /* Pop everything we may have allocated off the stack. */
1406 JS_ARENA_RELEASE(&cx->stackPool, mark);
1407 if (!ok)
1408 *vp = JSVAL_NULL;
1409 return ok;
1411 bad:
1412 js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
1413 ok = JS_FALSE;
1414 goto out2;
1417 JSBool
1418 js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags,
1419 uintN argc, jsval *argv, jsval *rval)
1421 jsval *invokevp;
1422 void *mark;
1423 JSBool ok;
1425 js_LeaveTrace(cx);
1426 invokevp = js_AllocStack(cx, 2 + argc, &mark);
1427 if (!invokevp)
1428 return JS_FALSE;
1430 invokevp[0] = fval;
1431 invokevp[1] = OBJECT_TO_JSVAL(obj);
1432 memcpy(invokevp + 2, argv, argc * sizeof *argv);
1434 ok = js_Invoke(cx, argc, invokevp, flags);
1435 if (ok) {
1437 * Store *rval in the a scoped local root if a scope is open, else in
1438 * the lastInternalResult pigeon-hole GC root, solely so users of
1439 * js_InternalInvoke and its direct and indirect (js_ValueToString for
1440 * example) callers do not need to manage roots for local, temporary
1441 * references to such results.
1443 *rval = *invokevp;
1444 if (JSVAL_IS_GCTHING(*rval) && *rval != JSVAL_NULL) {
1445 if (cx->localRootStack) {
1446 if (js_PushLocalRoot(cx, cx->localRootStack, *rval) < 0)
1447 ok = JS_FALSE;
1448 } else {
1449 cx->weakRoots.lastInternalResult = *rval;
1454 js_FreeStack(cx, mark);
1455 return ok;
1458 JSBool
1459 js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval,
1460 JSAccessMode mode, uintN argc, jsval *argv, jsval *rval)
1462 JSSecurityCallbacks *callbacks;
1464 js_LeaveTrace(cx);
1467 * js_InternalInvoke could result in another try to get or set the same id
1468 * again, see bug 355497.
1470 JS_CHECK_RECURSION(cx, return JS_FALSE);
1473 * Check general (not object-ops/class-specific) access from the running
1474 * script to obj.id only if id has a scripted getter or setter that we're
1475 * about to invoke. If we don't check this case, nothing else will -- no
1476 * other native code has the chance to check.
1478 * Contrast this non-native (scripted) case with native getter and setter
1479 * accesses, where the native itself must do an access check, if security
1480 * policies requires it. We make a checkAccess or checkObjectAccess call
1481 * back to the embedding program only in those cases where we're not going
1482 * to call an embedding-defined native function, getter, setter, or class
1483 * hook anyway. Where we do call such a native, there's no need for the
1484 * engine to impose a separate access check callback on all embeddings --
1485 * many embeddings have no security policy at all.
1487 JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE);
1488 callbacks = JS_GetSecurityCallbacks(cx);
1489 if (callbacks &&
1490 callbacks->checkObjectAccess &&
1491 VALUE_IS_FUNCTION(cx, fval) &&
1492 FUN_INTERPRETED(GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(fval))) &&
1493 !callbacks->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode, &fval)) {
1494 return JS_FALSE;
1497 return js_InternalCall(cx, obj, fval, argc, argv, rval);
1500 JSBool
1501 js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
1502 JSStackFrame *down, uintN flags, jsval *result)
1504 JSInterpreterHook hook;
1505 void *hookData, *mark;
1506 JSStackFrame *oldfp, frame;
1507 JSObject *obj, *tmp;
1508 JSBool ok;
1510 js_LeaveTrace(cx);
1512 #ifdef INCLUDE_MOZILLA_DTRACE
1513 if (JAVASCRIPT_EXECUTE_START_ENABLED())
1514 jsdtrace_execute_start(script);
1515 #endif
1517 hook = cx->debugHooks->executeHook;
1518 hookData = mark = NULL;
1519 oldfp = js_GetTopStackFrame(cx);
1520 frame.script = script;
1521 if (down) {
1522 /* Propagate arg state for eval and the debugger API. */
1523 frame.callobj = down->callobj;
1524 frame.argsobj = down->argsobj;
1525 frame.varobj = down->varobj;
1526 frame.callee = down->callee;
1527 frame.fun = down->fun;
1528 frame.thisp = down->thisp;
1529 if (down->flags & JSFRAME_COMPUTED_THIS)
1530 flags |= JSFRAME_COMPUTED_THIS;
1531 frame.argc = down->argc;
1532 frame.argv = down->argv;
1533 frame.annotation = down->annotation;
1534 frame.sharpArray = down->sharpArray;
1535 JS_ASSERT(script->nfixed == 0);
1536 } else {
1537 frame.callobj = NULL;
1538 frame.argsobj = NULL;
1539 obj = chain;
1540 if (cx->options & JSOPTION_VAROBJFIX) {
1541 while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
1542 obj = tmp;
1544 frame.varobj = obj;
1545 frame.callee = NULL;
1546 frame.fun = NULL;
1547 frame.thisp = chain;
1548 frame.argc = 0;
1549 frame.argv = NULL;
1550 frame.annotation = NULL;
1551 frame.sharpArray = NULL;
1554 frame.imacpc = NULL;
1555 if (script->nslots != 0) {
1556 frame.slots = js_AllocRawStack(cx, script->nslots, &mark);
1557 if (!frame.slots) {
1558 ok = JS_FALSE;
1559 goto out;
1561 memset(frame.slots, 0, script->nfixed * sizeof(jsval));
1562 } else {
1563 frame.slots = NULL;
1566 frame.rval = JSVAL_VOID;
1567 frame.down = down;
1568 frame.scopeChain = chain;
1569 frame.regs = NULL;
1570 frame.sharpDepth = 0;
1571 frame.flags = flags;
1572 frame.dormantNext = NULL;
1573 frame.xmlNamespace = NULL;
1574 frame.blockChain = NULL;
1577 * Here we wrap the call to js_Interpret with code to (conditionally)
1578 * save and restore the old stack frame chain into a chain of 'dormant'
1579 * frame chains. Since we are replacing cx->fp, we were running into
1580 * the problem that if GC was called under this frame, some of the GC
1581 * things associated with the old frame chain (available here only in
1582 * the C variable 'oldfp') were not rooted and were being collected.
1584 * So, now we preserve the links to these 'dormant' frame chains in cx
1585 * before calling js_Interpret and cleanup afterwards. The GC walks
1586 * these dormant chains and marks objects in the same way that it marks
1587 * objects in the primary cx->fp chain.
1589 if (oldfp && oldfp != down) {
1590 JS_ASSERT(!oldfp->dormantNext);
1591 oldfp->dormantNext = cx->dormantFrameChain;
1592 cx->dormantFrameChain = oldfp;
1595 cx->fp = &frame;
1596 if (!down) {
1597 frame.thisp = OBJ_THIS_OBJECT(cx, frame.thisp);
1598 if (!frame.thisp) {
1599 ok = JS_FALSE;
1600 goto out2;
1602 frame.flags |= JSFRAME_COMPUTED_THIS;
1605 if (hook) {
1606 hookData = hook(cx, &frame, JS_TRUE, 0,
1607 cx->debugHooks->executeHookData);
1610 ok = js_Interpret(cx);
1611 if (result)
1612 *result = frame.rval;
1614 if (hookData) {
1615 hook = cx->debugHooks->executeHook;
1616 if (hook)
1617 hook(cx, &frame, JS_FALSE, &ok, hookData);
1620 out2:
1621 if (mark)
1622 js_FreeRawStack(cx, mark);
1623 cx->fp = oldfp;
1625 if (oldfp && oldfp != down) {
1626 JS_ASSERT(cx->dormantFrameChain == oldfp);
1627 cx->dormantFrameChain = oldfp->dormantNext;
1628 oldfp->dormantNext = NULL;
1631 out:
1632 #ifdef INCLUDE_MOZILLA_DTRACE
1633 if (JAVASCRIPT_EXECUTE_DONE_ENABLED())
1634 jsdtrace_execute_done(script);
1635 #endif
1636 return ok;
1639 JSBool
1640 js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
1641 JSObject **objp, JSProperty **propp)
1643 JSObject *obj2;
1644 JSProperty *prop;
1645 uintN oldAttrs, report;
1646 bool isFunction;
1647 jsval value;
1648 const char *type, *name;
1651 * Both objp and propp must be either null or given. When given, *propp
1652 * must be null. This way we avoid an extra "if (propp) *propp = NULL" for
1653 * the common case of a non-existing property.
1655 JS_ASSERT(!objp == !propp);
1656 JS_ASSERT_IF(propp, !*propp);
1658 /* The JSPROP_INITIALIZER case below may generate a warning. Since we must
1659 * drop the property before reporting it, we insists on !propp to avoid
1660 * looking up the property again after the reporting is done.
1662 JS_ASSERT_IF(attrs & JSPROP_INITIALIZER, attrs == JSPROP_INITIALIZER);
1663 JS_ASSERT_IF(attrs == JSPROP_INITIALIZER, !propp);
1665 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1666 return JS_FALSE;
1667 if (!prop)
1668 return JS_TRUE;
1670 /* Use prop as a speedup hint to OBJ_GET_ATTRIBUTES. */
1671 if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) {
1672 OBJ_DROP_PROPERTY(cx, obj2, prop);
1673 return JS_FALSE;
1677 * If our caller doesn't want prop, drop it (we don't need it any longer).
1679 if (!propp) {
1680 OBJ_DROP_PROPERTY(cx, obj2, prop);
1681 prop = NULL;
1682 } else {
1683 *objp = obj2;
1684 *propp = prop;
1687 if (attrs == JSPROP_INITIALIZER) {
1688 /* Allow the new object to override properties. */
1689 if (obj2 != obj)
1690 return JS_TRUE;
1692 /* The property must be dropped already. */
1693 JS_ASSERT(!prop);
1694 report = JSREPORT_WARNING | JSREPORT_STRICT;
1696 #ifdef __GNUC__
1697 isFunction = false; /* suppress bogus gcc warnings */
1698 #endif
1699 } else {
1700 /* We allow redeclaring some non-readonly properties. */
1701 if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) {
1702 /* Allow redeclaration of variables and functions. */
1703 if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
1704 return JS_TRUE;
1707 * Allow adding a getter only if a property already has a setter
1708 * but no getter and similarly for adding a setter. That is, we
1709 * allow only the following transitions:
1711 * no-property --> getter --> getter + setter
1712 * no-property --> setter --> getter + setter
1714 if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0)
1715 return JS_TRUE;
1718 * Allow redeclaration of an impermanent property (in which case
1719 * anyone could delete it and redefine it, willy-nilly).
1721 if (!(oldAttrs & JSPROP_PERMANENT))
1722 return JS_TRUE;
1724 if (prop)
1725 OBJ_DROP_PROPERTY(cx, obj2, prop);
1727 report = JSREPORT_ERROR;
1728 isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
1729 if (!isFunction) {
1730 if (!OBJ_GET_PROPERTY(cx, obj, id, &value))
1731 return JS_FALSE;
1732 isFunction = VALUE_IS_FUNCTION(cx, value);
1736 type = (attrs == JSPROP_INITIALIZER)
1737 ? "property"
1738 : (oldAttrs & attrs & JSPROP_GETTER)
1739 ? js_getter_str
1740 : (oldAttrs & attrs & JSPROP_SETTER)
1741 ? js_setter_str
1742 : (oldAttrs & JSPROP_READONLY)
1743 ? js_const_str
1744 : isFunction
1745 ? js_function_str
1746 : js_var_str;
1747 name = js_ValueToPrintableString(cx, ID_TO_VALUE(id));
1748 if (!name)
1749 return JS_FALSE;
1750 return JS_ReportErrorFlagsAndNumber(cx, report,
1751 js_GetErrorMessage, NULL,
1752 JSMSG_REDECLARED_VAR,
1753 type, name);
1756 JSBool
1757 js_StrictlyEqual(JSContext *cx, jsval lval, jsval rval)
1759 jsval ltag = JSVAL_TAG(lval), rtag = JSVAL_TAG(rval);
1760 jsdouble ld, rd;
1762 if (ltag == rtag) {
1763 if (ltag == JSVAL_STRING) {
1764 JSString *lstr = JSVAL_TO_STRING(lval),
1765 *rstr = JSVAL_TO_STRING(rval);
1766 return js_EqualStrings(lstr, rstr);
1768 if (ltag == JSVAL_DOUBLE) {
1769 ld = *JSVAL_TO_DOUBLE(lval);
1770 rd = *JSVAL_TO_DOUBLE(rval);
1771 return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1773 if (ltag == JSVAL_OBJECT &&
1774 lval != rval &&
1775 !JSVAL_IS_NULL(lval) &&
1776 !JSVAL_IS_NULL(rval)) {
1777 JSObject *lobj, *robj;
1779 lobj = js_GetWrappedObject(cx, JSVAL_TO_OBJECT(lval));
1780 robj = js_GetWrappedObject(cx, JSVAL_TO_OBJECT(rval));
1781 lval = OBJECT_TO_JSVAL(lobj);
1782 rval = OBJECT_TO_JSVAL(robj);
1784 return lval == rval;
1786 if (ltag == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) {
1787 ld = *JSVAL_TO_DOUBLE(lval);
1788 rd = JSVAL_TO_INT(rval);
1789 return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1791 if (JSVAL_IS_INT(lval) && rtag == JSVAL_DOUBLE) {
1792 ld = JSVAL_TO_INT(lval);
1793 rd = *JSVAL_TO_DOUBLE(rval);
1794 return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1796 return lval == rval;
1799 JS_REQUIRES_STACK JSBool
1800 js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp)
1802 JSFunction *fun, *fun2;
1803 JSObject *obj, *obj2, *proto, *parent;
1804 jsval lval, rval;
1805 JSClass *clasp;
1807 fun = NULL;
1808 obj2 = NULL;
1809 lval = *vp;
1810 if (!JSVAL_IS_OBJECT(lval) ||
1811 (obj2 = JSVAL_TO_OBJECT(lval)) == NULL ||
1812 /* XXX clean up to avoid special cases above ObjectOps layer */
1813 OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass ||
1814 !obj2->map->ops->construct)
1816 fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT);
1817 if (!fun)
1818 return JS_FALSE;
1821 clasp = &js_ObjectClass;
1822 if (!obj2) {
1823 proto = parent = NULL;
1824 fun = NULL;
1825 } else {
1827 * Get the constructor prototype object for this function.
1828 * Use the nominal 'this' parameter slot, vp[1], as a local
1829 * root to protect this prototype, in case it has no other
1830 * strong refs.
1832 if (!OBJ_GET_PROPERTY(cx, obj2,
1833 ATOM_TO_JSID(cx->runtime->atomState
1834 .classPrototypeAtom),
1835 &vp[1])) {
1836 return JS_FALSE;
1838 rval = vp[1];
1839 proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL;
1840 parent = OBJ_GET_PARENT(cx, obj2);
1842 if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) {
1843 fun2 = GET_FUNCTION_PRIVATE(cx, obj2);
1844 if (!FUN_INTERPRETED(fun2) && fun2->u.n.clasp)
1845 clasp = fun2->u.n.clasp;
1848 obj = js_NewObject(cx, clasp, proto, parent);
1849 if (!obj)
1850 return JS_FALSE;
1852 /* Now we have an object with a constructor method; call it. */
1853 vp[1] = OBJECT_TO_JSVAL(obj);
1854 if (!js_Invoke(cx, argc, vp, JSINVOKE_CONSTRUCT)) {
1855 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
1856 return JS_FALSE;
1859 /* Check the return value and if it's primitive, force it to be obj. */
1860 rval = *vp;
1861 if (clampReturn && JSVAL_IS_PRIMITIVE(rval)) {
1862 if (!fun) {
1863 /* native [[Construct]] returning primitive is error */
1864 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1865 JSMSG_BAD_NEW_RESULT,
1866 js_ValueToPrintableString(cx, rval));
1867 return JS_FALSE;
1869 *vp = OBJECT_TO_JSVAL(obj);
1872 JS_RUNTIME_METER(cx->runtime, constructs);
1873 return JS_TRUE;
1876 JSBool
1877 js_InternNonIntElementId(JSContext *cx, JSObject *obj, jsval idval, jsid *idp)
1879 JS_ASSERT(!JSVAL_IS_INT(idval));
1881 #if JS_HAS_XML_SUPPORT
1882 if (!JSVAL_IS_PRIMITIVE(idval)) {
1883 if (OBJECT_IS_XML(cx, obj)) {
1884 *idp = OBJECT_JSVAL_TO_JSID(idval);
1885 return JS_TRUE;
1887 if (!js_IsFunctionQName(cx, JSVAL_TO_OBJECT(idval), idp))
1888 return JS_FALSE;
1889 if (*idp != 0)
1890 return JS_TRUE;
1892 #endif
1894 return js_ValueToStringId(cx, idval, idp);
1898 * Enter the new with scope using an object at sp[-1] and associate the depth
1899 * of the with block with sp + stackIndex.
1901 JS_STATIC_INTERPRET JS_REQUIRES_STACK JSBool
1902 js_EnterWith(JSContext *cx, jsint stackIndex)
1904 JSStackFrame *fp;
1905 jsval *sp;
1906 JSObject *obj, *parent, *withobj;
1908 fp = cx->fp;
1909 sp = fp->regs->sp;
1910 JS_ASSERT(stackIndex < 0);
1911 JS_ASSERT(StackBase(fp) <= sp + stackIndex);
1913 if (!JSVAL_IS_PRIMITIVE(sp[-1])) {
1914 obj = JSVAL_TO_OBJECT(sp[-1]);
1915 } else {
1916 obj = js_ValueToNonNullObject(cx, sp[-1]);
1917 if (!obj)
1918 return JS_FALSE;
1919 sp[-1] = OBJECT_TO_JSVAL(obj);
1922 parent = js_GetScopeChain(cx, fp);
1923 if (!parent)
1924 return JS_FALSE;
1926 OBJ_TO_INNER_OBJECT(cx, obj);
1927 if (!obj)
1928 return JS_FALSE;
1930 withobj = js_NewWithObject(cx, obj, parent,
1931 sp + stackIndex - StackBase(fp));
1932 if (!withobj)
1933 return JS_FALSE;
1935 fp->scopeChain = withobj;
1936 return JS_TRUE;
1939 JS_STATIC_INTERPRET JS_REQUIRES_STACK void
1940 js_LeaveWith(JSContext *cx)
1942 JSObject *withobj;
1944 withobj = cx->fp->scopeChain;
1945 JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass);
1946 JS_ASSERT(withobj->getAssignedPrivate() == cx->fp);
1947 JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);
1948 cx->fp->scopeChain = OBJ_GET_PARENT(cx, withobj);
1949 withobj->setPrivate(NULL);
1952 JS_REQUIRES_STACK JSClass *
1953 js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth)
1955 JSClass *clasp;
1957 clasp = OBJ_GET_CLASS(cx, obj);
1958 if ((clasp == &js_WithClass || clasp == &js_BlockClass) &&
1959 obj->getAssignedPrivate() == cx->fp &&
1960 OBJ_BLOCK_DEPTH(cx, obj) >= stackDepth) {
1961 return clasp;
1963 return NULL;
1967 * Unwind block and scope chains to match the given depth. The function sets
1968 * fp->sp on return to stackDepth.
1970 JS_REQUIRES_STACK JSBool
1971 js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth,
1972 JSBool normalUnwind)
1974 JSObject *obj;
1975 JSClass *clasp;
1977 JS_ASSERT(stackDepth >= 0);
1978 JS_ASSERT(StackBase(fp) + stackDepth <= fp->regs->sp);
1980 for (obj = fp->blockChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
1981 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
1982 if (OBJ_BLOCK_DEPTH(cx, obj) < stackDepth)
1983 break;
1985 fp->blockChain = obj;
1987 for (;;) {
1988 obj = fp->scopeChain;
1989 clasp = js_IsActiveWithOrBlock(cx, obj, stackDepth);
1990 if (!clasp)
1991 break;
1992 if (clasp == &js_BlockClass) {
1993 /* Don't fail until after we've updated all stacks. */
1994 normalUnwind &= js_PutBlockObject(cx, normalUnwind);
1995 } else {
1996 js_LeaveWith(cx);
2000 fp->regs->sp = StackBase(fp) + stackDepth;
2001 return normalUnwind;
2004 JS_STATIC_INTERPRET JSBool
2005 js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2)
2007 jsval v;
2008 jsdouble d;
2010 v = *vp;
2011 if (JSVAL_IS_DOUBLE(v)) {
2012 d = *JSVAL_TO_DOUBLE(v);
2013 } else if (JSVAL_IS_INT(v)) {
2014 d = JSVAL_TO_INT(v);
2015 } else {
2016 d = js_ValueToNumber(cx, vp);
2017 if (JSVAL_IS_NULL(*vp))
2018 return JS_FALSE;
2019 JS_ASSERT(JSVAL_IS_NUMBER(*vp) || *vp == JSVAL_TRUE);
2021 /* Store the result of v conversion back in vp for post increments. */
2022 if ((cs->format & JOF_POST) &&
2023 *vp == JSVAL_TRUE
2024 && !js_NewNumberInRootedValue(cx, d, vp)) {
2025 return JS_FALSE;
2029 (cs->format & JOF_INC) ? d++ : d--;
2030 if (!js_NewNumberInRootedValue(cx, d, vp2))
2031 return JS_FALSE;
2033 if (!(cs->format & JOF_POST))
2034 *vp = *vp2;
2035 return JS_TRUE;
2038 jsval&
2039 js_GetUpvar(JSContext *cx, uintN level, uintN cookie)
2041 level -= UPVAR_FRAME_SKIP(cookie);
2042 JS_ASSERT(level < JS_DISPLAY_SIZE);
2044 JSStackFrame *fp = cx->display[level];
2045 JS_ASSERT(fp->script);
2047 uintN slot = UPVAR_FRAME_SLOT(cookie);
2048 jsval *vp;
2050 if (!fp->fun) {
2051 vp = fp->slots + fp->script->nfixed;
2052 } else if (slot < fp->fun->nargs) {
2053 vp = fp->argv;
2054 } else if (slot == CALLEE_UPVAR_SLOT) {
2055 vp = &fp->argv[-2];
2056 slot = 0;
2057 } else {
2058 slot -= fp->fun->nargs;
2059 JS_ASSERT(slot < fp->script->nslots);
2060 vp = fp->slots;
2063 return vp[slot];
2066 #ifdef DEBUG
2068 JS_STATIC_INTERPRET JS_REQUIRES_STACK void
2069 js_TraceOpcode(JSContext *cx)
2071 FILE *tracefp;
2072 JSStackFrame *fp;
2073 JSFrameRegs *regs;
2074 intN ndefs, n, nuses;
2075 jsval *siter;
2076 JSString *str;
2077 JSOp op;
2079 tracefp = (FILE *) cx->tracefp;
2080 JS_ASSERT(tracefp);
2081 fp = cx->fp;
2082 regs = fp->regs;
2085 * Operations in prologues don't produce interesting values, and
2086 * js_DecompileValueGenerator isn't set up to handle them anyway.
2088 if (cx->tracePrevPc && regs->pc >= fp->script->main) {
2089 JSOp tracePrevOp = JSOp(*cx->tracePrevPc);
2090 ndefs = js_GetStackDefs(cx, &js_CodeSpec[tracePrevOp], tracePrevOp,
2091 fp->script, cx->tracePrevPc);
2094 * If there aren't that many elements on the stack, then
2095 * we have probably entered a new frame, and printing output
2096 * would just be misleading.
2098 if (ndefs != 0 &&
2099 ndefs < regs->sp - fp->slots) {
2100 for (n = -ndefs; n < 0; n++) {
2101 char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
2102 NULL);
2103 if (bytes) {
2104 fprintf(tracefp, "%s %s",
2105 (n == -ndefs) ? " output:" : ",",
2106 bytes);
2107 cx->free(bytes);
2110 fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));
2112 fprintf(tracefp, " stack: ");
2113 for (siter = StackBase(fp); siter < regs->sp; siter++) {
2114 str = js_ValueToString(cx, *siter);
2115 if (!str)
2116 fputs("<null>", tracefp);
2117 else
2118 js_FileEscapedString(tracefp, str, 0);
2119 fputc(' ', tracefp);
2121 fputc('\n', tracefp);
2124 fprintf(tracefp, "%4u: ",
2125 js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : regs->pc));
2126 js_Disassemble1(cx, fp->script, regs->pc,
2127 regs->pc - fp->script->code,
2128 JS_FALSE, tracefp);
2129 op = (JSOp) *regs->pc;
2130 nuses = js_GetStackUses(&js_CodeSpec[op], op, regs->pc);
2131 if (nuses != 0) {
2132 for (n = -nuses; n < 0; n++) {
2133 char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
2134 NULL);
2135 if (bytes) {
2136 fprintf(tracefp, "%s %s",
2137 (n == -nuses) ? " inputs:" : ",",
2138 bytes);
2139 cx->free(bytes);
2142 fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));
2144 cx->tracePrevPc = regs->pc;
2146 /* It's nice to have complete traces when debugging a crash. */
2147 fflush(tracefp);
2150 #endif /* DEBUG */
2152 #ifdef JS_OPMETER
2154 # include <stdlib.h>
2156 # define HIST_NSLOTS 8
2159 * The second dimension is hardcoded at 256 because we know that many bits fit
2160 * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address
2161 * any particular row.
2163 static uint32 succeeds[JSOP_LIMIT][256];
2164 static uint32 slot_ops[JSOP_LIMIT][HIST_NSLOTS];
2166 JS_STATIC_INTERPRET void
2167 js_MeterOpcodePair(JSOp op1, JSOp op2)
2169 if (op1 != JSOP_STOP)
2170 ++succeeds[op1][op2];
2173 JS_STATIC_INTERPRET void
2174 js_MeterSlotOpcode(JSOp op, uint32 slot)
2176 if (slot < HIST_NSLOTS)
2177 ++slot_ops[op][slot];
2180 typedef struct Edge {
2181 const char *from;
2182 const char *to;
2183 uint32 count;
2184 } Edge;
2186 static int
2187 compare_edges(const void *a, const void *b)
2189 const Edge *ea = (const Edge *) a;
2190 const Edge *eb = (const Edge *) b;
2192 return (int32)eb->count - (int32)ea->count;
2195 void
2196 js_DumpOpMeters()
2198 const char *name, *from, *style;
2199 FILE *fp;
2200 uint32 total, count;
2201 uint32 i, j, nedges;
2202 Edge *graph;
2204 name = getenv("JS_OPMETER_FILE");
2205 if (!name)
2206 name = "/tmp/ops.dot";
2207 fp = fopen(name, "w");
2208 if (!fp) {
2209 perror(name);
2210 return;
2213 total = nedges = 0;
2214 for (i = 0; i < JSOP_LIMIT; i++) {
2215 for (j = 0; j < JSOP_LIMIT; j++) {
2216 count = succeeds[i][j];
2217 if (count != 0) {
2218 total += count;
2219 ++nedges;
2224 # define SIGNIFICANT(count,total) (200. * (count) >= (total))
2226 graph = (Edge *) js_calloc(nedges * sizeof graph[0]);
2227 for (i = nedges = 0; i < JSOP_LIMIT; i++) {
2228 from = js_CodeName[i];
2229 for (j = 0; j < JSOP_LIMIT; j++) {
2230 count = succeeds[i][j];
2231 if (count != 0 && SIGNIFICANT(count, total)) {
2232 graph[nedges].from = from;
2233 graph[nedges].to = js_CodeName[j];
2234 graph[nedges].count = count;
2235 ++nedges;
2239 qsort(graph, nedges, sizeof(Edge), compare_edges);
2241 # undef SIGNIFICANT
2243 fputs("digraph {\n", fp);
2244 for (i = 0, style = NULL; i < nedges; i++) {
2245 JS_ASSERT(i == 0 || graph[i-1].count >= graph[i].count);
2246 if (!style || graph[i-1].count != graph[i].count) {
2247 style = (i > nedges * .75) ? "dotted" :
2248 (i > nedges * .50) ? "dashed" :
2249 (i > nedges * .25) ? "solid" : "bold";
2251 fprintf(fp, " %s -> %s [label=\"%lu\" style=%s]\n",
2252 graph[i].from, graph[i].to,
2253 (unsigned long)graph[i].count, style);
2255 js_free(graph);
2256 fputs("}\n", fp);
2257 fclose(fp);
2259 name = getenv("JS_OPMETER_HIST");
2260 if (!name)
2261 name = "/tmp/ops.hist";
2262 fp = fopen(name, "w");
2263 if (!fp) {
2264 perror(name);
2265 return;
2267 fputs("bytecode", fp);
2268 for (j = 0; j < HIST_NSLOTS; j++)
2269 fprintf(fp, " slot %1u", (unsigned)j);
2270 putc('\n', fp);
2271 fputs("========", fp);
2272 for (j = 0; j < HIST_NSLOTS; j++)
2273 fputs(" =======", fp);
2274 putc('\n', fp);
2275 for (i = 0; i < JSOP_LIMIT; i++) {
2276 for (j = 0; j < HIST_NSLOTS; j++) {
2277 if (slot_ops[i][j] != 0) {
2278 /* Reuse j in the next loop, since we break after. */
2279 fprintf(fp, "%-8.8s", js_CodeName[i]);
2280 for (j = 0; j < HIST_NSLOTS; j++)
2281 fprintf(fp, " %7lu", (unsigned long)slot_ops[i][j]);
2282 putc('\n', fp);
2283 break;
2287 fclose(fp);
2290 #endif /* JS_OPSMETER */
2292 #endif /* !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___ */
2294 #ifndef jsinvoke_cpp___
2296 #define PUSH(v) (*regs.sp++ = (v))
2297 #define PUSH_OPND(v) PUSH(v)
2298 #define STORE_OPND(n,v) (regs.sp[n] = (v))
2299 #define POP() (*--regs.sp)
2300 #define POP_OPND() POP()
2301 #define FETCH_OPND(n) (regs.sp[n])
2304 * Push the jsdouble d using sp from the lexical environment. Try to convert d
2305 * to a jsint that fits in a jsval, otherwise GC-alloc space for it and push a
2306 * reference.
2308 #define STORE_NUMBER(cx, n, d) \
2309 JS_BEGIN_MACRO \
2310 jsint i_; \
2312 if (JSDOUBLE_IS_INT(d, i_) && INT_FITS_IN_JSVAL(i_)) \
2313 regs.sp[n] = INT_TO_JSVAL(i_); \
2314 else if (!js_NewDoubleInRootedValue(cx, d, &regs.sp[n])) \
2315 goto error; \
2316 JS_END_MACRO
2318 #define STORE_INT(cx, n, i) \
2319 JS_BEGIN_MACRO \
2320 if (INT_FITS_IN_JSVAL(i)) \
2321 regs.sp[n] = INT_TO_JSVAL(i); \
2322 else if (!js_NewDoubleInRootedValue(cx, (jsdouble) (i), &regs.sp[n])) \
2323 goto error; \
2324 JS_END_MACRO
2326 #define STORE_UINT(cx, n, u) \
2327 JS_BEGIN_MACRO \
2328 if ((u) <= JSVAL_INT_MAX) \
2329 regs.sp[n] = INT_TO_JSVAL(u); \
2330 else if (!js_NewDoubleInRootedValue(cx, (jsdouble) (u), &regs.sp[n])) \
2331 goto error; \
2332 JS_END_MACRO
2334 #define FETCH_NUMBER(cx, n, d) \
2335 JS_BEGIN_MACRO \
2336 jsval v_; \
2338 v_ = FETCH_OPND(n); \
2339 VALUE_TO_NUMBER(cx, n, v_, d); \
2340 JS_END_MACRO
2342 #define FETCH_INT(cx, n, i) \
2343 JS_BEGIN_MACRO \
2344 jsval v_; \
2346 v_= FETCH_OPND(n); \
2347 if (JSVAL_IS_INT(v_)) { \
2348 i = JSVAL_TO_INT(v_); \
2349 } else { \
2350 i = js_ValueToECMAInt32(cx, &regs.sp[n]); \
2351 if (JSVAL_IS_NULL(regs.sp[n])) \
2352 goto error; \
2354 JS_END_MACRO
2356 #define FETCH_UINT(cx, n, ui) \
2357 JS_BEGIN_MACRO \
2358 jsval v_; \
2360 v_= FETCH_OPND(n); \
2361 if (JSVAL_IS_INT(v_)) { \
2362 ui = (uint32) JSVAL_TO_INT(v_); \
2363 } else { \
2364 ui = js_ValueToECMAUint32(cx, &regs.sp[n]); \
2365 if (JSVAL_IS_NULL(regs.sp[n])) \
2366 goto error; \
2368 JS_END_MACRO
2371 * Optimized conversion macros that test for the desired type in v before
2372 * homing sp and calling a conversion function.
2374 #define VALUE_TO_NUMBER(cx, n, v, d) \
2375 JS_BEGIN_MACRO \
2376 JS_ASSERT(v == regs.sp[n]); \
2377 if (JSVAL_IS_INT(v)) { \
2378 d = (jsdouble)JSVAL_TO_INT(v); \
2379 } else if (JSVAL_IS_DOUBLE(v)) { \
2380 d = *JSVAL_TO_DOUBLE(v); \
2381 } else { \
2382 d = js_ValueToNumber(cx, &regs.sp[n]); \
2383 if (JSVAL_IS_NULL(regs.sp[n])) \
2384 goto error; \
2385 JS_ASSERT(JSVAL_IS_NUMBER(regs.sp[n]) || \
2386 regs.sp[n] == JSVAL_TRUE); \
2388 JS_END_MACRO
2390 #define POP_BOOLEAN(cx, v, b) \
2391 JS_BEGIN_MACRO \
2392 v = FETCH_OPND(-1); \
2393 if (v == JSVAL_NULL) { \
2394 b = JS_FALSE; \
2395 } else if (JSVAL_IS_BOOLEAN(v)) { \
2396 b = JSVAL_TO_BOOLEAN(v); \
2397 } else { \
2398 b = js_ValueToBoolean(v); \
2400 regs.sp--; \
2401 JS_END_MACRO
2403 #define VALUE_TO_OBJECT(cx, n, v, obj) \
2404 JS_BEGIN_MACRO \
2405 if (!JSVAL_IS_PRIMITIVE(v)) { \
2406 obj = JSVAL_TO_OBJECT(v); \
2407 } else { \
2408 obj = js_ValueToNonNullObject(cx, v); \
2409 if (!obj) \
2410 goto error; \
2411 STORE_OPND(n, OBJECT_TO_JSVAL(obj)); \
2413 JS_END_MACRO
2415 #define FETCH_OBJECT(cx, n, v, obj) \
2416 JS_BEGIN_MACRO \
2417 v = FETCH_OPND(n); \
2418 VALUE_TO_OBJECT(cx, n, v, obj); \
2419 JS_END_MACRO
2421 #define DEFAULT_VALUE(cx, n, hint, v) \
2422 JS_BEGIN_MACRO \
2423 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); \
2424 JS_ASSERT(v == regs.sp[n]); \
2425 if (!OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, &regs.sp[n])) \
2426 goto error; \
2427 v = regs.sp[n]; \
2428 JS_END_MACRO
2431 * Quickly test if v is an int from the [-2**29, 2**29) range, that is, when
2432 * the lowest bit of v is 1 and the bits 30 and 31 are both either 0 or 1. For
2433 * such v we can do increment or decrement via adding or subtracting two
2434 * without checking that the result overflows JSVAL_INT_MIN or JSVAL_INT_MAX.
2436 #define CAN_DO_FAST_INC_DEC(v) (((((v) << 1) ^ v) & 0x80000001) == 1)
2438 JS_STATIC_ASSERT(JSVAL_INT == 1);
2439 JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL_CONSTEXPR(JSVAL_INT_MIN)));
2440 JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL_CONSTEXPR(JSVAL_INT_MAX)));
2443 * Conditional assert to detect failure to clear a pending exception that is
2444 * suppressed (or unintentional suppression of a wanted exception).
2446 #if defined DEBUG_brendan || defined DEBUG_mrbkap || defined DEBUG_shaver
2447 # define DEBUG_NOT_THROWING 1
2448 #endif
2450 #ifdef DEBUG_NOT_THROWING
2451 # define ASSERT_NOT_THROWING(cx) JS_ASSERT(!(cx)->throwing)
2452 #else
2453 # define ASSERT_NOT_THROWING(cx) /* nothing */
2454 #endif
2457 * Define JS_OPMETER to instrument bytecode succession, generating a .dot file
2458 * on shutdown that shows the graph of significant predecessor/successor pairs
2459 * executed, where the edge labels give the succession counts. The .dot file
2460 * is named by the JS_OPMETER_FILE envariable, and defaults to /tmp/ops.dot.
2462 * Bonus feature: JS_OPMETER also enables counters for stack-addressing ops
2463 * such as JSOP_GETLOCAL, JSOP_INCARG, via METER_SLOT_OP. The resulting counts
2464 * are written to JS_OPMETER_HIST, defaulting to /tmp/ops.hist.
2466 #ifndef JS_OPMETER
2467 # define METER_OP_INIT(op) /* nothing */
2468 # define METER_OP_PAIR(op1,op2) /* nothing */
2469 # define METER_SLOT_OP(op,slot) /* nothing */
2470 #else
2473 * The second dimension is hardcoded at 256 because we know that many bits fit
2474 * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address
2475 * any particular row.
2477 # define METER_OP_INIT(op) ((op) = JSOP_STOP)
2478 # define METER_OP_PAIR(op1,op2) (js_MeterOpcodePair(op1, op2))
2479 # define METER_SLOT_OP(op,slot) (js_MeterSlotOpcode(op, slot))
2481 #endif
2483 #define MAX_INLINE_CALL_COUNT 3000
2486 * Threaded interpretation via computed goto appears to be well-supported by
2487 * GCC 3 and higher. IBM's C compiler when run with the right options (e.g.,
2488 * -qlanglvl=extended) also supports threading. Ditto the SunPro C compiler.
2489 * Currently it's broken for JS_VERSION < 160, though this isn't worth fixing.
2490 * Add your compiler support macros here.
2492 #ifndef JS_THREADED_INTERP
2493 # if JS_VERSION >= 160 && ( \
2494 __GNUC__ >= 3 || \
2495 (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \
2496 __SUNPRO_C >= 0x570)
2497 # define JS_THREADED_INTERP 1
2498 # else
2499 # define JS_THREADED_INTERP 0
2500 # endif
2501 #endif
2504 * Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on
2505 * single-thread DEBUG js shell testing to verify property cache hits.
2507 #if defined DEBUG && !defined JS_THREADSAFE
2509 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) \
2510 JS_BEGIN_MACRO \
2511 if (!AssertValidPropertyCacheHit(cx, script, regs, pcoff, obj, pobj, \
2512 entry)) { \
2513 goto error; \
2515 JS_END_MACRO
2517 static bool
2518 AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
2519 ptrdiff_t pcoff, JSObject *start, JSObject *found,
2520 JSPropCacheEntry *entry)
2522 uint32 sample = cx->runtime->gcNumber;
2524 JSAtom *atom;
2525 if (pcoff >= 0)
2526 GET_ATOM_FROM_BYTECODE(script, regs.pc, pcoff, atom);
2527 else
2528 atom = cx->runtime->atomState.lengthAtom;
2530 JSObject *obj, *pobj;
2531 JSProperty *prop;
2532 bool ok;
2534 if (JOF_OPMODE(*regs.pc) == JOF_NAME) {
2535 ok = js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &pobj, &prop);
2536 } else {
2537 obj = start;
2538 ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop);
2540 if (!ok)
2541 return false;
2542 if (!prop)
2543 return true;
2544 if (cx->runtime->gcNumber != sample ||
2545 PCVCAP_SHAPE(entry->vcap) != OBJ_SHAPE(pobj)) {
2546 OBJ_DROP_PROPERTY(cx, pobj, prop);
2547 return true;
2549 JS_ASSERT(prop);
2550 JS_ASSERT(pobj == found);
2552 JSScopeProperty *sprop = (JSScopeProperty *) prop;
2553 if (PCVAL_IS_SLOT(entry->vword)) {
2554 JS_ASSERT(PCVAL_TO_SLOT(entry->vword) == sprop->slot);
2555 } else if (PCVAL_IS_SPROP(entry->vword)) {
2556 JS_ASSERT(PCVAL_TO_SPROP(entry->vword) == sprop);
2557 } else {
2558 jsval v;
2559 JS_ASSERT(PCVAL_IS_OBJECT(entry->vword));
2560 JS_ASSERT(entry->vword != PCVAL_NULL);
2561 JS_ASSERT(OBJ_SCOPE(pobj)->branded());
2562 JS_ASSERT(SPROP_HAS_STUB_GETTER(sprop));
2563 JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)));
2564 v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
2565 JS_ASSERT(VALUE_IS_FUNCTION(cx, v));
2566 JS_ASSERT(PCVAL_TO_OBJECT(entry->vword) == JSVAL_TO_OBJECT(v));
2569 OBJ_DROP_PROPERTY(cx, pobj, prop);
2570 return true;
2573 #else
2574 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0)
2575 #endif
2578 * Ensure that the intrepreter switch can close call-bytecode cases in the
2579 * same way as non-call bytecodes.
2581 JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
2582 JS_STATIC_ASSERT(JSOP_GETGVAR_LENGTH == JSOP_CALLGVAR_LENGTH);
2583 JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH);
2584 JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_CALLUPVAR_DBG_LENGTH);
2585 JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_GETUPVAR_LENGTH);
2586 JS_STATIC_ASSERT(JSOP_GETDSLOT_LENGTH == JSOP_CALLDSLOT_LENGTH);
2587 JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
2588 JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
2589 JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH);
2592 * Same for debuggable flat closures defined at top level in another function
2593 * or program fragment.
2595 JS_STATIC_ASSERT(JSOP_DEFFUN_FC_LENGTH == JSOP_DEFFUN_DBGFC_LENGTH);
2598 * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
2599 * remain distinct for the decompiler.
2601 JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
2603 /* See TRY_BRANCH_AFTER_COND. */
2604 JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH);
2605 JS_STATIC_ASSERT(JSOP_IFNE == JSOP_IFEQ + 1);
2607 /* For the fastest case inder JSOP_INCNAME, etc. */
2608 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_DECNAME_LENGTH);
2609 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEINC_LENGTH);
2610 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH);
2612 #ifdef JS_TRACER
2613 # define ABORT_RECORDING(cx, reason) \
2614 JS_BEGIN_MACRO \
2615 if (TRACE_RECORDER(cx)) \
2616 js_AbortRecording(cx, reason); \
2617 JS_END_MACRO
2618 #else
2619 # define ABORT_RECORDING(cx, reason) ((void) 0)
2620 #endif
2622 JS_REQUIRES_STACK JSBool
2623 js_Interpret(JSContext *cx)
2625 #ifdef MOZ_TRACEVIS
2626 TraceVisStateObj tvso(cx, S_INTERP);
2627 #endif
2629 JSRuntime *rt;
2630 JSStackFrame *fp;
2631 JSScript *script;
2632 uintN inlineCallCount;
2633 JSAtom **atoms;
2634 JSVersion currentVersion, originalVersion;
2635 JSFrameRegs regs;
2636 JSObject *obj, *obj2, *parent;
2637 JSBool ok, cond;
2638 jsint len;
2639 jsbytecode *endpc, *pc2;
2640 JSOp op, op2;
2641 jsatomid index;
2642 JSAtom *atom;
2643 uintN argc, attrs, flags;
2644 uint32 slot;
2645 jsval *vp, lval, rval, ltmp, rtmp;
2646 jsid id;
2647 JSProperty *prop;
2648 JSScopeProperty *sprop;
2649 JSString *str, *str2;
2650 jsint i, j;
2651 jsdouble d, d2;
2652 JSClass *clasp;
2653 JSFunction *fun;
2654 JSType type;
2655 jsint low, high, off, npairs;
2656 JSBool match;
2657 #if JS_HAS_GETTER_SETTER
2658 JSPropertyOp getter, setter;
2659 #endif
2660 JSAutoResolveFlags rf(cx, JSRESOLVE_INFER);
2662 #ifdef __GNUC__
2663 # define JS_EXTENSION __extension__
2664 # define JS_EXTENSION_(s) __extension__ ({ s; })
2665 #else
2666 # define JS_EXTENSION
2667 # define JS_EXTENSION_(s) s
2668 #endif
2670 # ifdef DEBUG
2672 * We call this macro from BEGIN_CASE in threaded interpreters,
2673 * and before entering the switch in non-threaded interpreters.
2674 * However, reaching such points doesn't mean we've actually
2675 * fetched an OP from the instruction stream: some opcodes use
2676 * 'op=x; DO_OP()' to let another opcode's implementation finish
2677 * their work, and many opcodes share entry points with a run of
2678 * consecutive BEGIN_CASEs.
2680 * Take care to trace OP only when it is the opcode fetched from
2681 * the instruction stream, so the trace matches what one would
2682 * expect from looking at the code. (We do omit POPs after SETs;
2683 * unfortunate, but not worth fixing.)
2685 # define TRACE_OPCODE(OP) JS_BEGIN_MACRO \
2686 if (JS_UNLIKELY(cx->tracefp != NULL) && \
2687 (OP) == *regs.pc) \
2688 js_TraceOpcode(cx); \
2689 JS_END_MACRO
2690 # else
2691 # define TRACE_OPCODE(OP) ((void) 0)
2692 # endif
2694 #if JS_THREADED_INTERP
2695 static void *const normalJumpTable[] = {
2696 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2697 JS_EXTENSION &&L_##op,
2698 # include "jsopcode.tbl"
2699 # undef OPDEF
2702 static void *const interruptJumpTable[] = {
2703 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2704 JS_EXTENSION &&interrupt,
2705 # include "jsopcode.tbl"
2706 # undef OPDEF
2709 register void * const *jumpTable = normalJumpTable;
2711 METER_OP_INIT(op); /* to nullify first METER_OP_PAIR */
2713 # define ENABLE_INTERRUPTS() ((void) (jumpTable = interruptJumpTable))
2715 # ifdef JS_TRACER
2716 # define CHECK_RECORDER() \
2717 JS_ASSERT_IF(TRACE_RECORDER(cx), jumpTable == interruptJumpTable)
2718 # else
2719 # define CHECK_RECORDER() ((void)0)
2720 # endif
2722 # define DO_OP() JS_BEGIN_MACRO \
2723 CHECK_RECORDER(); \
2724 JS_EXTENSION_(goto *jumpTable[op]); \
2725 JS_END_MACRO
2726 # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
2727 METER_OP_PAIR(op, regs.pc[n]); \
2728 op = (JSOp) *(regs.pc += (n)); \
2729 DO_OP(); \
2730 JS_END_MACRO
2732 # define BEGIN_CASE(OP) L_##OP: TRACE_OPCODE(OP); CHECK_RECORDER();
2733 # define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH);
2734 # define END_VARLEN_CASE DO_NEXT_OP(len);
2735 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP) \
2736 JS_ASSERT(js_CodeSpec[OP].length == 1); \
2737 op = (JSOp) *++regs.pc; \
2738 DO_OP();
2740 # define END_EMPTY_CASES
2742 #else /* !JS_THREADED_INTERP */
2744 register intN switchMask = 0;
2745 intN switchOp;
2747 # define ENABLE_INTERRUPTS() ((void) (switchMask = -1))
2749 # ifdef JS_TRACER
2750 # define CHECK_RECORDER() \
2751 JS_ASSERT_IF(TRACE_RECORDER(cx), switchMask == -1)
2752 # else
2753 # define CHECK_RECORDER() ((void)0)
2754 # endif
2756 # define DO_OP() goto do_op
2757 # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
2758 JS_ASSERT((n) == len); \
2759 goto advance_pc; \
2760 JS_END_MACRO
2762 # define BEGIN_CASE(OP) case OP: CHECK_RECORDER();
2763 # define END_CASE(OP) END_CASE_LEN(OP##_LENGTH)
2764 # define END_CASE_LEN(n) END_CASE_LENX(n)
2765 # define END_CASE_LENX(n) END_CASE_LEN##n
2768 * To share the code for all len == 1 cases we use the specialized label with
2769 * code that falls through to advance_pc: .
2771 # define END_CASE_LEN1 goto advance_pc_by_one;
2772 # define END_CASE_LEN2 len = 2; goto advance_pc;
2773 # define END_CASE_LEN3 len = 3; goto advance_pc;
2774 # define END_CASE_LEN4 len = 4; goto advance_pc;
2775 # define END_CASE_LEN5 len = 5; goto advance_pc;
2776 # define END_VARLEN_CASE goto advance_pc;
2777 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)
2778 # define END_EMPTY_CASES goto advance_pc_by_one;
2780 #endif /* !JS_THREADED_INTERP */
2782 #ifdef JS_TRACER
2783 /* We had better not be entering the interpreter from JIT-compiled code. */
2784 TraceRecorder *tr = TRACE_RECORDER(cx);
2785 SET_TRACE_RECORDER(cx, NULL);
2787 /* If a recorder is pending and we try to re-enter the interpreter, flag
2788 the recorder to be destroyed when we return. */
2789 if (tr) {
2790 if (tr->wasDeepAborted())
2791 tr->removeFragmentoReferences();
2792 else
2793 tr->pushAbortStack();
2795 #endif
2797 /* Check for too deep of a native thread stack. */
2798 JS_CHECK_RECURSION(cx, return JS_FALSE);
2800 rt = cx->runtime;
2802 /* Set registerized frame pointer and derived script pointer. */
2803 fp = cx->fp;
2804 script = fp->script;
2805 JS_ASSERT(script->length != 0);
2807 /* Count of JS function calls that nest in this C js_Interpret frame. */
2808 inlineCallCount = 0;
2811 * Initialize the index segment register used by LOAD_ATOM and
2812 * GET_FULL_INDEX macros below. As a register we use a pointer based on
2813 * the atom map to turn frequently executed LOAD_ATOM into simple array
2814 * access. For less frequent object and regexp loads we have to recover
2815 * the segment from atoms pointer first.
2817 atoms = script->atomMap.vector;
2819 #define LOAD_ATOM(PCOFF) \
2820 JS_BEGIN_MACRO \
2821 JS_ASSERT(fp->imacpc \
2822 ? atoms == COMMON_ATOMS_START(&rt->atomState) && \
2823 GET_INDEX(regs.pc + PCOFF) < js_common_atom_count \
2824 : (size_t)(atoms - script->atomMap.vector) < \
2825 (size_t)(script->atomMap.length - \
2826 GET_INDEX(regs.pc + PCOFF))); \
2827 atom = atoms[GET_INDEX(regs.pc + PCOFF)]; \
2828 JS_END_MACRO
2830 #define GET_FULL_INDEX(PCOFF) \
2831 (atoms - script->atomMap.vector + GET_INDEX(regs.pc + PCOFF))
2833 #define LOAD_OBJECT(PCOFF) \
2834 JS_GET_SCRIPT_OBJECT(script, GET_FULL_INDEX(PCOFF), obj)
2836 #define LOAD_FUNCTION(PCOFF) \
2837 JS_GET_SCRIPT_FUNCTION(script, GET_FULL_INDEX(PCOFF), fun)
2839 #ifdef JS_TRACER
2841 #ifdef MOZ_TRACEVIS
2842 #if JS_THREADED_INTERP
2843 #define MONITOR_BRANCH_TRACEVIS \
2844 JS_BEGIN_MACRO \
2845 if (jumpTable != interruptJumpTable) \
2846 js_EnterTraceVisState(cx, S_RECORD, R_NONE); \
2847 JS_END_MACRO
2848 #else /* !JS_THREADED_INTERP */
2849 #define MONITOR_BRANCH_TRACEVIS \
2850 JS_BEGIN_MACRO \
2851 js_EnterTraceVisState(cx, S_RECORD, R_NONE); \
2852 JS_END_MACRO
2853 #endif
2854 #else
2855 #define MONITOR_BRANCH_TRACEVIS
2856 #endif
2858 #define MONITOR_BRANCH() \
2859 JS_BEGIN_MACRO \
2860 if (TRACING_ENABLED(cx)) { \
2861 if (js_MonitorLoopEdge(cx, inlineCallCount)) { \
2862 JS_ASSERT(TRACE_RECORDER(cx)); \
2863 MONITOR_BRANCH_TRACEVIS; \
2864 ENABLE_INTERRUPTS(); \
2866 fp = cx->fp; \
2867 script = fp->script; \
2868 atoms = FrameAtomBase(cx, fp); \
2869 currentVersion = (JSVersion) script->version; \
2870 JS_ASSERT(fp->regs == &regs); \
2871 if (cx->throwing) \
2872 goto error; \
2874 JS_END_MACRO
2876 #else /* !JS_TRACER */
2878 #define MONITOR_BRANCH() ((void) 0)
2880 #endif /* !JS_TRACER */
2883 * Prepare to call a user-supplied branch handler, and abort the script
2884 * if it returns false.
2886 #define CHECK_BRANCH() \
2887 JS_BEGIN_MACRO \
2888 if (!JS_CHECK_OPERATION_LIMIT(cx)) \
2889 goto error; \
2890 JS_END_MACRO
2892 #ifndef TRACE_RECORDER
2893 #define TRACE_RECORDER(cx) (false)
2894 #endif
2896 #define BRANCH(n) \
2897 JS_BEGIN_MACRO \
2898 regs.pc += (n); \
2899 op = (JSOp) *regs.pc; \
2900 if ((n) <= 0) { \
2901 CHECK_BRANCH(); \
2902 if (op == JSOP_NOP) { \
2903 if (TRACE_RECORDER(cx)) { \
2904 MONITOR_BRANCH(); \
2905 op = (JSOp) *regs.pc; \
2906 } else { \
2907 op = (JSOp) *++regs.pc; \
2909 } else if (op == JSOP_LOOP) { \
2910 MONITOR_BRANCH(); \
2911 op = (JSOp) *regs.pc; \
2914 DO_OP(); \
2915 JS_END_MACRO
2917 MUST_FLOW_THROUGH("exit");
2918 ++cx->interpLevel;
2921 * Optimized Get and SetVersion for proper script language versioning.
2923 * If any native method or JSClass/JSObjectOps hook calls js_SetVersion
2924 * and changes cx->version, the effect will "stick" and we will stop
2925 * maintaining currentVersion. This is relied upon by testsuites, for
2926 * the most part -- web browsers select version before compiling and not
2927 * at run-time.
2929 currentVersion = (JSVersion) script->version;
2930 originalVersion = (JSVersion) cx->version;
2931 if (currentVersion != originalVersion)
2932 js_SetVersion(cx, currentVersion);
2934 /* Update the static-link display. */
2935 if (script->staticLevel < JS_DISPLAY_SIZE) {
2936 JSStackFrame **disp = &cx->display[script->staticLevel];
2937 fp->displaySave = *disp;
2938 *disp = fp;
2941 # define CHECK_INTERRUPT_HANDLER() \
2942 JS_BEGIN_MACRO \
2943 if (cx->debugHooks->interruptHandler) \
2944 ENABLE_INTERRUPTS(); \
2945 JS_END_MACRO
2948 * Load the debugger's interrupt hook here and after calling out to native
2949 * functions (but not to getters, setters, or other native hooks), so we do
2950 * not have to reload it each time through the interpreter loop -- we hope
2951 * the compiler can keep it in a register when it is non-null.
2953 CHECK_INTERRUPT_HANDLER();
2955 #if !JS_HAS_GENERATORS
2956 JS_ASSERT(!fp->regs);
2957 #else
2958 /* Initialize the pc and sp registers unless we're resuming a generator. */
2959 if (JS_LIKELY(!fp->regs)) {
2960 #endif
2961 ASSERT_NOT_THROWING(cx);
2962 regs.pc = script->code;
2963 regs.sp = StackBase(fp);
2964 fp->regs = &regs;
2965 #if JS_HAS_GENERATORS
2966 } else {
2967 JSGenerator *gen;
2969 JS_ASSERT(fp->flags & JSFRAME_GENERATOR);
2970 gen = FRAME_TO_GENERATOR(fp);
2971 JS_ASSERT(fp->regs == &gen->savedRegs);
2972 regs = gen->savedRegs;
2973 fp->regs = &regs;
2974 JS_ASSERT((size_t) (regs.pc - script->code) <= script->length);
2975 JS_ASSERT((size_t) (regs.sp - StackBase(fp)) <= StackDepth(script));
2978 * To support generator_throw and to catch ignored exceptions,
2979 * fail if cx->throwing is set.
2981 if (cx->throwing) {
2982 #ifdef DEBUG_NOT_THROWING
2983 if (cx->exception != JSVAL_ARETURN) {
2984 printf("JS INTERPRETER CALLED WITH PENDING EXCEPTION %lx\n",
2985 (unsigned long) cx->exception);
2987 #endif
2988 goto error;
2991 #endif /* JS_HAS_GENERATORS */
2994 * It is important that "op" be initialized before calling DO_OP because
2995 * it is possible for "op" to be specially assigned during the normal
2996 * processing of an opcode while looping. We rely on DO_NEXT_OP to manage
2997 * "op" correctly in all other cases.
2999 len = 0;
3000 DO_NEXT_OP(len);
3002 #if JS_THREADED_INTERP
3004 * This is a loop, but it does not look like a loop. The loop-closing
3005 * jump is distributed throughout goto *jumpTable[op] inside of DO_OP.
3006 * When interrupts are enabled, jumpTable is set to interruptJumpTable
3007 * where all jumps point to the interrupt label. The latter, after
3008 * calling the interrupt handler, dispatches through normalJumpTable to
3009 * continue the normal bytecode processing.
3011 interrupt:
3012 #else /* !JS_THREADED_INTERP */
3013 for (;;) {
3014 advance_pc_by_one:
3015 JS_ASSERT(js_CodeSpec[op].length == 1);
3016 len = 1;
3017 advance_pc:
3018 regs.pc += len;
3019 op = (JSOp) *regs.pc;
3021 do_op:
3022 CHECK_RECORDER();
3023 TRACE_OPCODE(op);
3024 switchOp = intN(op) | switchMask;
3025 do_switch:
3026 switch (switchOp) {
3027 case -1:
3028 JS_ASSERT(switchMask == -1);
3029 #endif /* !JS_THREADED_INTERP */
3031 bool moreInterrupts = false;
3032 JSTrapHandler handler = cx->debugHooks->interruptHandler;
3033 if (handler) {
3034 #ifdef JS_TRACER
3035 if (TRACE_RECORDER(cx))
3036 js_AbortRecording(cx, "interrupt handler");
3037 #endif
3038 switch (handler(cx, script, regs.pc, &rval,
3039 cx->debugHooks->interruptHandlerData)) {
3040 case JSTRAP_ERROR:
3041 goto error;
3042 case JSTRAP_CONTINUE:
3043 break;
3044 case JSTRAP_RETURN:
3045 fp->rval = rval;
3046 ok = JS_TRUE;
3047 goto forced_return;
3048 case JSTRAP_THROW:
3049 cx->throwing = JS_TRUE;
3050 cx->exception = rval;
3051 goto error;
3052 default:;
3054 moreInterrupts = true;
3057 #ifdef JS_TRACER
3058 TraceRecorder* tr = TRACE_RECORDER(cx);
3059 if (tr) {
3060 JSRecordingStatus status = TraceRecorder::monitorRecording(cx, tr, op);
3061 switch (status) {
3062 case JSRS_CONTINUE:
3063 moreInterrupts = true;
3064 break;
3065 case JSRS_IMACRO:
3066 atoms = COMMON_ATOMS_START(&rt->atomState);
3067 op = JSOp(*regs.pc);
3068 DO_OP(); /* keep interrupting for op. */
3069 break;
3070 case JSRS_ERROR:
3071 // The code at 'error:' aborts the recording.
3072 goto error;
3073 case JSRS_STOP:
3074 break;
3075 default:
3076 JS_NOT_REACHED("Bad recording status");
3079 #endif /* !JS_TRACER */
3081 #if JS_THREADED_INTERP
3082 #ifdef MOZ_TRACEVIS
3083 if (!moreInterrupts)
3084 js_ExitTraceVisState(cx, R_ABORT);
3085 #endif
3086 jumpTable = moreInterrupts ? interruptJumpTable : normalJumpTable;
3087 JS_EXTENSION_(goto *normalJumpTable[op]);
3088 #else
3089 switchMask = moreInterrupts ? -1 : 0;
3090 switchOp = intN(op);
3091 goto do_switch;
3092 #endif
3095 /* No-ops for ease of decompilation. */
3096 ADD_EMPTY_CASE(JSOP_NOP)
3097 ADD_EMPTY_CASE(JSOP_CONDSWITCH)
3098 ADD_EMPTY_CASE(JSOP_TRY)
3099 #if JS_HAS_XML_SUPPORT
3100 ADD_EMPTY_CASE(JSOP_STARTXML)
3101 ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
3102 #endif
3103 END_EMPTY_CASES
3105 /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
3106 BEGIN_CASE(JSOP_LINENO)
3107 END_CASE(JSOP_LINENO)
3109 BEGIN_CASE(JSOP_PUSH)
3110 PUSH_OPND(JSVAL_VOID);
3111 END_CASE(JSOP_PUSH)
3113 BEGIN_CASE(JSOP_POP)
3114 regs.sp--;
3115 END_CASE(JSOP_POP)
3117 BEGIN_CASE(JSOP_POPN)
3118 regs.sp -= GET_UINT16(regs.pc);
3119 #ifdef DEBUG
3120 JS_ASSERT(StackBase(fp) <= regs.sp);
3121 obj = fp->blockChain;
3122 JS_ASSERT_IF(obj,
3123 OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
3124 <= (size_t) (regs.sp - StackBase(fp)));
3125 for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
3126 clasp = OBJ_GET_CLASS(cx, obj);
3127 if (clasp != &js_BlockClass && clasp != &js_WithClass)
3128 continue;
3129 if (obj->getAssignedPrivate() != fp)
3130 break;
3131 JS_ASSERT(StackBase(fp) + OBJ_BLOCK_DEPTH(cx, obj)
3132 + ((clasp == &js_BlockClass)
3133 ? OBJ_BLOCK_COUNT(cx, obj)
3134 : 1)
3135 <= regs.sp);
3137 #endif
3138 END_CASE(JSOP_POPN)
3140 BEGIN_CASE(JSOP_SETRVAL)
3141 BEGIN_CASE(JSOP_POPV)
3142 ASSERT_NOT_THROWING(cx);
3143 fp->rval = POP_OPND();
3144 END_CASE(JSOP_POPV)
3146 BEGIN_CASE(JSOP_ENTERWITH)
3147 if (!js_EnterWith(cx, -1))
3148 goto error;
3151 * We must ensure that different "with" blocks have different
3152 * stack depth associated with them. This allows the try handler
3153 * search to properly recover the scope chain. Thus we must keep
3154 * the stack at least at the current level.
3156 * We set sp[-1] to the current "with" object to help asserting
3157 * the enter/leave balance in [leavewith].
3159 regs.sp[-1] = OBJECT_TO_JSVAL(fp->scopeChain);
3160 END_CASE(JSOP_ENTERWITH)
3162 BEGIN_CASE(JSOP_LEAVEWITH)
3163 JS_ASSERT(regs.sp[-1] == OBJECT_TO_JSVAL(fp->scopeChain));
3164 regs.sp--;
3165 js_LeaveWith(cx);
3166 END_CASE(JSOP_LEAVEWITH)
3168 BEGIN_CASE(JSOP_RETURN)
3169 fp->rval = POP_OPND();
3170 /* FALL THROUGH */
3172 BEGIN_CASE(JSOP_RETRVAL) /* fp->rval already set */
3173 BEGIN_CASE(JSOP_STOP)
3175 * When the inlined frame exits with an exception or an error, ok
3176 * will be false after the inline_return label.
3178 ASSERT_NOT_THROWING(cx);
3179 CHECK_BRANCH();
3181 if (fp->imacpc) {
3183 * If we are at the end of an imacro, return to its caller in
3184 * the current frame.
3186 JS_ASSERT(op == JSOP_STOP);
3188 end_imacro:
3189 JS_ASSERT((uintN)(regs.sp - fp->slots) <= script->nslots);
3190 regs.pc = fp->imacpc + js_CodeSpec[*fp->imacpc].length;
3191 fp->imacpc = NULL;
3192 atoms = script->atomMap.vector;
3193 op = JSOp(*regs.pc);
3194 DO_OP();
3197 JS_ASSERT(regs.sp == StackBase(fp));
3198 if ((fp->flags & JSFRAME_CONSTRUCTING) &&
3199 JSVAL_IS_PRIMITIVE(fp->rval)) {
3200 if (!fp->fun) {
3201 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3202 JSMSG_BAD_NEW_RESULT,
3203 js_ValueToPrintableString(cx, rval));
3204 goto error;
3206 fp->rval = OBJECT_TO_JSVAL(fp->thisp);
3208 ok = JS_TRUE;
3209 if (inlineCallCount)
3210 inline_return:
3212 JSInlineFrame *ifp = (JSInlineFrame *) fp;
3213 void *hookData = ifp->hookData;
3215 JS_ASSERT(!fp->blockChain);
3216 JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
3218 if (script->staticLevel < JS_DISPLAY_SIZE)
3219 cx->display[script->staticLevel] = fp->displaySave;
3221 if (hookData) {
3222 JSInterpreterHook hook;
3223 JSBool status;
3225 hook = cx->debugHooks->callHook;
3226 if (hook) {
3228 * Do not pass &ok directly as exposing the address
3229 * inhibits optimizations and uninitialised warnings.
3231 status = ok;
3232 hook(cx, fp, JS_FALSE, &status, hookData);
3233 ok = status;
3234 CHECK_INTERRUPT_HANDLER();
3239 * If fp has a call object, sync values and clear the back-
3240 * pointer. This can happen for a lightweight function if it
3241 * calls eval unexpectedly (in a way that is hidden from the
3242 * compiler). See bug 325540.
3244 ok &= fp->putActivationObjects(cx);
3246 #ifdef INCLUDE_MOZILLA_DTRACE
3247 /* DTrace function return, inlines */
3248 if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
3249 jsdtrace_function_rval(cx, fp, fp->fun, &fp->rval);
3250 if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
3251 jsdtrace_function_return(cx, fp, fp->fun);
3252 #endif
3254 /* Restore context version only if callee hasn't set version. */
3255 if (JS_LIKELY(cx->version == currentVersion)) {
3256 currentVersion = ifp->callerVersion;
3257 if (currentVersion != cx->version)
3258 js_SetVersion(cx, currentVersion);
3262 * If inline-constructing, replace primitive rval with the new
3263 * object passed in via |this|, and instrument this constructor
3264 * invocation
3266 if (fp->flags & JSFRAME_CONSTRUCTING) {
3267 if (JSVAL_IS_PRIMITIVE(fp->rval))
3268 fp->rval = OBJECT_TO_JSVAL(fp->thisp);
3269 JS_RUNTIME_METER(cx->runtime, constructs);
3272 /* Restore caller's registers. */
3273 regs = ifp->callerRegs;
3275 /* Store the return value in the caller's operand frame. */
3276 regs.sp -= 1 + (size_t) ifp->frame.argc;
3277 regs.sp[-1] = fp->rval;
3279 /* Restore cx->fp and release the inline frame's space. */
3280 cx->fp = fp = fp->down;
3281 JS_ASSERT(fp->regs == &ifp->callerRegs);
3282 fp->regs = &regs;
3283 JS_ARENA_RELEASE(&cx->stackPool, ifp->mark);
3285 /* Restore the calling script's interpreter registers. */
3286 script = fp->script;
3287 atoms = FrameAtomBase(cx, fp);
3289 /* Resume execution in the calling frame. */
3290 inlineCallCount--;
3291 if (JS_LIKELY(ok)) {
3292 TRACE_0(LeaveFrame);
3293 JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, script, regs.pc)].length
3294 == JSOP_CALL_LENGTH);
3295 len = JSOP_CALL_LENGTH;
3296 DO_NEXT_OP(len);
3298 goto error;
3300 goto exit;
3302 BEGIN_CASE(JSOP_DEFAULT)
3303 (void) POP();
3304 /* FALL THROUGH */
3305 BEGIN_CASE(JSOP_GOTO)
3306 len = GET_JUMP_OFFSET(regs.pc);
3307 BRANCH(len);
3308 END_CASE(JSOP_GOTO)
3310 BEGIN_CASE(JSOP_IFEQ)
3311 POP_BOOLEAN(cx, rval, cond);
3312 if (cond == JS_FALSE) {
3313 len = GET_JUMP_OFFSET(regs.pc);
3314 BRANCH(len);
3316 END_CASE(JSOP_IFEQ)
3318 BEGIN_CASE(JSOP_IFNE)
3319 POP_BOOLEAN(cx, rval, cond);
3320 if (cond != JS_FALSE) {
3321 len = GET_JUMP_OFFSET(regs.pc);
3322 BRANCH(len);
3324 END_CASE(JSOP_IFNE)
3326 BEGIN_CASE(JSOP_OR)
3327 POP_BOOLEAN(cx, rval, cond);
3328 if (cond == JS_TRUE) {
3329 len = GET_JUMP_OFFSET(regs.pc);
3330 PUSH_OPND(rval);
3331 DO_NEXT_OP(len);
3333 END_CASE(JSOP_OR)
3335 BEGIN_CASE(JSOP_AND)
3336 POP_BOOLEAN(cx, rval, cond);
3337 if (cond == JS_FALSE) {
3338 len = GET_JUMP_OFFSET(regs.pc);
3339 PUSH_OPND(rval);
3340 DO_NEXT_OP(len);
3342 END_CASE(JSOP_AND)
3344 BEGIN_CASE(JSOP_DEFAULTX)
3345 (void) POP();
3346 /* FALL THROUGH */
3347 BEGIN_CASE(JSOP_GOTOX)
3348 len = GET_JUMPX_OFFSET(regs.pc);
3349 BRANCH(len);
3350 END_CASE(JSOP_GOTOX);
3352 BEGIN_CASE(JSOP_IFEQX)
3353 POP_BOOLEAN(cx, rval, cond);
3354 if (cond == JS_FALSE) {
3355 len = GET_JUMPX_OFFSET(regs.pc);
3356 BRANCH(len);
3358 END_CASE(JSOP_IFEQX)
3360 BEGIN_CASE(JSOP_IFNEX)
3361 POP_BOOLEAN(cx, rval, cond);
3362 if (cond != JS_FALSE) {
3363 len = GET_JUMPX_OFFSET(regs.pc);
3364 BRANCH(len);
3366 END_CASE(JSOP_IFNEX)
3368 BEGIN_CASE(JSOP_ORX)
3369 POP_BOOLEAN(cx, rval, cond);
3370 if (cond == JS_TRUE) {
3371 len = GET_JUMPX_OFFSET(regs.pc);
3372 PUSH_OPND(rval);
3373 DO_NEXT_OP(len);
3375 END_CASE(JSOP_ORX)
3377 BEGIN_CASE(JSOP_ANDX)
3378 POP_BOOLEAN(cx, rval, cond);
3379 if (cond == JS_FALSE) {
3380 len = GET_JUMPX_OFFSET(regs.pc);
3381 PUSH_OPND(rval);
3382 DO_NEXT_OP(len);
3384 END_CASE(JSOP_ANDX)
3387 * If the index value at sp[n] is not an int that fits in a jsval, it could
3388 * be an object (an XML QName, AttributeName, or AnyName), but only if we are
3389 * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a
3390 * string atom id.
3392 #define FETCH_ELEMENT_ID(obj, n, id) \
3393 JS_BEGIN_MACRO \
3394 jsval idval_ = FETCH_OPND(n); \
3395 if (JSVAL_IS_INT(idval_)) { \
3396 id = INT_JSVAL_TO_JSID(idval_); \
3397 } else { \
3398 if (!js_InternNonIntElementId(cx, obj, idval_, &id)) \
3399 goto error; \
3400 regs.sp[n] = ID_TO_VALUE(id); \
3402 JS_END_MACRO
3404 #define TRY_BRANCH_AFTER_COND(cond,spdec) \
3405 JS_BEGIN_MACRO \
3406 uintN diff_; \
3407 JS_ASSERT(js_CodeSpec[op].length == 1); \
3408 diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ; \
3409 if (diff_ <= 1) { \
3410 regs.sp -= spdec; \
3411 if (cond == (diff_ != 0)) { \
3412 ++regs.pc; \
3413 len = GET_JUMP_OFFSET(regs.pc); \
3414 BRANCH(len); \
3416 len = 1 + JSOP_IFEQ_LENGTH; \
3417 DO_NEXT_OP(len); \
3419 JS_END_MACRO
3421 BEGIN_CASE(JSOP_IN)
3422 rval = FETCH_OPND(-1);
3423 if (JSVAL_IS_PRIMITIVE(rval)) {
3424 js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rval, NULL);
3425 goto error;
3427 obj = JSVAL_TO_OBJECT(rval);
3428 FETCH_ELEMENT_ID(obj, -2, id);
3429 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
3430 goto error;
3431 cond = prop != NULL;
3432 if (prop)
3433 OBJ_DROP_PROPERTY(cx, obj2, prop);
3434 TRY_BRANCH_AFTER_COND(cond, 2);
3435 regs.sp--;
3436 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));
3437 END_CASE(JSOP_IN)
3439 BEGIN_CASE(JSOP_ITER)
3440 JS_ASSERT(regs.sp > StackBase(fp));
3441 flags = regs.pc[1];
3442 if (!js_ValueToIterator(cx, flags, &regs.sp[-1]))
3443 goto error;
3444 CHECK_INTERRUPT_HANDLER();
3445 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));
3446 PUSH(JSVAL_VOID);
3447 END_CASE(JSOP_ITER)
3449 BEGIN_CASE(JSOP_NEXTITER)
3450 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3451 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-2]));
3452 if (!js_CallIteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-2]), &regs.sp[-1]))
3453 goto error;
3454 CHECK_INTERRUPT_HANDLER();
3455 rval = BOOLEAN_TO_JSVAL(regs.sp[-1] != JSVAL_HOLE);
3456 PUSH(rval);
3457 END_CASE(JSOP_NEXTITER)
3459 BEGIN_CASE(JSOP_ENDITER)
3461 * Decrease the stack pointer even when !ok -- see comments in the
3462 * exception capturing code for details.
3464 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3465 ok = js_CloseIterator(cx, regs.sp[-2]);
3466 regs.sp -= 2;
3467 if (!ok)
3468 goto error;
3469 END_CASE(JSOP_ENDITER)
3471 BEGIN_CASE(JSOP_FORARG)
3472 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3473 slot = GET_ARGNO(regs.pc);
3474 JS_ASSERT(slot < fp->fun->nargs);
3475 fp->argv[slot] = regs.sp[-1];
3476 END_CASE(JSOP_FORARG)
3478 BEGIN_CASE(JSOP_FORLOCAL)
3479 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3480 slot = GET_SLOTNO(regs.pc);
3481 JS_ASSERT(slot < fp->script->nslots);
3482 fp->slots[slot] = regs.sp[-1];
3483 END_CASE(JSOP_FORLOCAL)
3485 BEGIN_CASE(JSOP_FORNAME)
3486 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3487 LOAD_ATOM(0);
3488 id = ATOM_TO_JSID(atom);
3489 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
3490 goto error;
3491 if (prop)
3492 OBJ_DROP_PROPERTY(cx, obj2, prop);
3493 ok = OBJ_SET_PROPERTY(cx, obj, id, &regs.sp[-1]);
3494 if (!ok)
3495 goto error;
3496 END_CASE(JSOP_FORNAME)
3498 BEGIN_CASE(JSOP_FORPROP)
3499 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3500 LOAD_ATOM(0);
3501 id = ATOM_TO_JSID(atom);
3502 FETCH_OBJECT(cx, -1, lval, obj);
3503 ok = OBJ_SET_PROPERTY(cx, obj, id, &regs.sp[-2]);
3504 if (!ok)
3505 goto error;
3506 regs.sp--;
3507 END_CASE(JSOP_FORPROP)
3509 BEGIN_CASE(JSOP_FORELEM)
3511 * JSOP_FORELEM simply dups the property identifier at top of stack
3512 * and lets the subsequent JSOP_ENUMELEM opcode sequence handle the
3513 * left-hand side expression evaluation and assignment. This opcode
3514 * exists solely to help the decompiler.
3516 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3517 rval = FETCH_OPND(-1);
3518 PUSH(rval);
3519 END_CASE(JSOP_FORELEM)
3521 BEGIN_CASE(JSOP_DUP)
3522 JS_ASSERT(regs.sp > StackBase(fp));
3523 rval = FETCH_OPND(-1);
3524 PUSH(rval);
3525 END_CASE(JSOP_DUP)
3527 BEGIN_CASE(JSOP_DUP2)
3528 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3529 lval = FETCH_OPND(-2);
3530 rval = FETCH_OPND(-1);
3531 PUSH(lval);
3532 PUSH(rval);
3533 END_CASE(JSOP_DUP2)
3535 BEGIN_CASE(JSOP_SWAP)
3536 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3537 lval = FETCH_OPND(-2);
3538 rval = FETCH_OPND(-1);
3539 STORE_OPND(-1, lval);
3540 STORE_OPND(-2, rval);
3541 END_CASE(JSOP_SWAP)
3543 BEGIN_CASE(JSOP_PICK)
3544 i = regs.pc[1];
3545 JS_ASSERT(regs.sp - (i+1) >= StackBase(fp));
3546 lval = regs.sp[-(i+1)];
3547 memmove(regs.sp - (i+1), regs.sp - i, sizeof(jsval)*i);
3548 regs.sp[-1] = lval;
3549 END_CASE(JSOP_PICK)
3551 #define PROPERTY_OP(n, call) \
3552 JS_BEGIN_MACRO \
3553 /* Fetch the left part and resolve it to a non-null object. */ \
3554 FETCH_OBJECT(cx, n, lval, obj); \
3556 /* Get or set the property. */ \
3557 if (!call) \
3558 goto error; \
3559 JS_END_MACRO
3561 #define ELEMENT_OP(n, call) \
3562 JS_BEGIN_MACRO \
3563 /* Fetch the left part and resolve it to a non-null object. */ \
3564 FETCH_OBJECT(cx, n - 1, lval, obj); \
3566 /* Fetch index and convert it to id suitable for use with obj. */ \
3567 FETCH_ELEMENT_ID(obj, n, id); \
3569 /* Get or set the element. */ \
3570 if (!call) \
3571 goto error; \
3572 JS_END_MACRO
3574 #define NATIVE_GET(cx,obj,pobj,sprop,vp) \
3575 JS_BEGIN_MACRO \
3576 if (SPROP_HAS_STUB_GETTER(sprop)) { \
3577 /* Fast path for Object instance properties. */ \
3578 JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT || \
3579 !SPROP_HAS_STUB_SETTER(sprop)); \
3580 *vp = ((sprop)->slot != SPROP_INVALID_SLOT) \
3581 ? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot) \
3582 : JSVAL_VOID; \
3583 } else { \
3584 if (!js_NativeGet(cx, obj, pobj, sprop, vp)) \
3585 goto error; \
3587 JS_END_MACRO
3589 #define NATIVE_SET(cx,obj,sprop,entry,vp) \
3590 JS_BEGIN_MACRO \
3591 TRACE_2(SetPropHit, entry, sprop); \
3592 if (SPROP_HAS_STUB_SETTER(sprop) && \
3593 (sprop)->slot != SPROP_INVALID_SLOT) { \
3594 /* Fast path for, e.g., Object instance properties. */ \
3595 LOCKED_OBJ_WRITE_SLOT(cx, obj, (sprop)->slot, *vp); \
3596 } else { \
3597 if (!js_NativeSet(cx, obj, sprop, vp)) \
3598 goto error; \
3600 JS_END_MACRO
3603 * Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is
3604 * the constant length of the SET opcode sequence, and spdec is the constant
3605 * by which to decrease the stack pointer to pop all of the SET op's operands.
3607 * NB: unlike macros that could conceivably be replaced by functions (ignoring
3608 * goto error), where a call should not have to be braced in order to expand
3609 * correctly (e.g., in if (cond) FOO(); else BAR()), these three macros lack
3610 * JS_{BEGIN,END}_MACRO brackets. They are also indented so as to align with
3611 * nearby opcode code.
3613 #define SKIP_POP_AFTER_SET(oplen,spdec) \
3614 if (regs.pc[oplen] == JSOP_POP) { \
3615 regs.sp -= spdec; \
3616 regs.pc += oplen + JSOP_POP_LENGTH; \
3617 op = (JSOp) *regs.pc; \
3618 DO_OP(); \
3621 #define END_SET_CASE(OP) \
3622 SKIP_POP_AFTER_SET(OP##_LENGTH, 1); \
3623 END_CASE(OP)
3625 #define END_SET_CASE_STORE_RVAL(OP,spdec) \
3626 SKIP_POP_AFTER_SET(OP##_LENGTH, spdec); \
3627 rval = FETCH_OPND(-1); \
3628 regs.sp -= (spdec) - 1; \
3629 STORE_OPND(-1, rval); \
3630 END_CASE(OP)
3632 BEGIN_CASE(JSOP_SETCONST)
3633 LOAD_ATOM(0);
3634 obj = fp->varobj;
3635 rval = FETCH_OPND(-1);
3636 if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval,
3637 JS_PropertyStub, JS_PropertyStub,
3638 JSPROP_ENUMERATE | JSPROP_PERMANENT |
3639 JSPROP_READONLY,
3640 NULL)) {
3641 goto error;
3643 END_SET_CASE(JSOP_SETCONST);
3645 #if JS_HAS_DESTRUCTURING
3646 BEGIN_CASE(JSOP_ENUMCONSTELEM)
3647 rval = FETCH_OPND(-3);
3648 FETCH_OBJECT(cx, -2, lval, obj);
3649 FETCH_ELEMENT_ID(obj, -1, id);
3650 if (!OBJ_DEFINE_PROPERTY(cx, obj, id, rval,
3651 JS_PropertyStub, JS_PropertyStub,
3652 JSPROP_ENUMERATE | JSPROP_PERMANENT |
3653 JSPROP_READONLY,
3654 NULL)) {
3655 goto error;
3657 regs.sp -= 3;
3658 END_CASE(JSOP_ENUMCONSTELEM)
3659 #endif
3661 BEGIN_CASE(JSOP_BINDNAME)
3662 do {
3663 JSPropCacheEntry *entry;
3666 * We can skip the property lookup for the global object. If
3667 * the property does not exist anywhere on the scope chain,
3668 * JSOP_SETNAME adds the property to the global.
3670 * As a consequence of this optimization for the global object
3671 * we run its JSRESOLVE_ASSIGNING-tolerant resolve hooks only
3672 * in JSOP_SETNAME, after the interpreter evaluates the right-
3673 * hand-side of the assignment, and not here.
3675 * This should be transparent to the hooks because the script,
3676 * instead of name = rhs, could have used global.name = rhs
3677 * given a global object reference, which also calls the hooks
3678 * only after evaluating the rhs. We desire such resolve hook
3679 * equivalence between the two forms.
3681 obj = fp->scopeChain;
3682 if (!OBJ_GET_PARENT(cx, obj))
3683 break;
3684 if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
3685 PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);
3686 if (!atom) {
3687 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
3688 JS_UNLOCK_OBJ(cx, obj2);
3689 break;
3691 } else {
3692 entry = NULL;
3693 LOAD_ATOM(0);
3695 id = ATOM_TO_JSID(atom);
3696 obj = js_FindIdentifierBase(cx, fp->scopeChain, id);
3697 if (!obj)
3698 goto error;
3699 } while (0);
3700 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3701 END_CASE(JSOP_BINDNAME)
3703 BEGIN_CASE(JSOP_IMACOP)
3704 JS_ASSERT(JS_UPTRDIFF(fp->imacpc, script->code) < script->length);
3705 op = JSOp(*fp->imacpc);
3706 DO_OP();
3708 #define BITWISE_OP(OP) \
3709 JS_BEGIN_MACRO \
3710 FETCH_INT(cx, -2, i); \
3711 FETCH_INT(cx, -1, j); \
3712 i = i OP j; \
3713 regs.sp--; \
3714 STORE_INT(cx, -1, i); \
3715 JS_END_MACRO
3717 BEGIN_CASE(JSOP_BITOR)
3718 BITWISE_OP(|);
3719 END_CASE(JSOP_BITOR)
3721 BEGIN_CASE(JSOP_BITXOR)
3722 BITWISE_OP(^);
3723 END_CASE(JSOP_BITXOR)
3725 BEGIN_CASE(JSOP_BITAND)
3726 BITWISE_OP(&);
3727 END_CASE(JSOP_BITAND)
3729 #define RELATIONAL_OP(OP) \
3730 JS_BEGIN_MACRO \
3731 rval = FETCH_OPND(-1); \
3732 lval = FETCH_OPND(-2); \
3733 /* Optimize for two int-tagged operands (typical loop control). */ \
3734 if ((lval & rval) & JSVAL_INT) { \
3735 cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval); \
3736 } else { \
3737 if (!JSVAL_IS_PRIMITIVE(lval)) \
3738 DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \
3739 if (!JSVAL_IS_PRIMITIVE(rval)) \
3740 DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \
3741 if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { \
3742 str = JSVAL_TO_STRING(lval); \
3743 str2 = JSVAL_TO_STRING(rval); \
3744 cond = js_CompareStrings(str, str2) OP 0; \
3745 } else { \
3746 VALUE_TO_NUMBER(cx, -2, lval, d); \
3747 VALUE_TO_NUMBER(cx, -1, rval, d2); \
3748 cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \
3751 TRY_BRANCH_AFTER_COND(cond, 2); \
3752 regs.sp--; \
3753 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
3754 JS_END_MACRO
3757 * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies
3758 * because they begin if/else chains, so callers must not put semicolons after
3759 * the call expressions!
3761 #if JS_HAS_XML_SUPPORT
3762 #define XML_EQUALITY_OP(OP) \
3763 if ((ltmp == JSVAL_OBJECT && \
3764 (obj2 = JSVAL_TO_OBJECT(lval)) && \
3765 OBJECT_IS_XML(cx, obj2)) || \
3766 (rtmp == JSVAL_OBJECT && \
3767 (obj2 = JSVAL_TO_OBJECT(rval)) && \
3768 OBJECT_IS_XML(cx, obj2))) { \
3769 if (JSVAL_IS_OBJECT(rval) && obj2 == JSVAL_TO_OBJECT(rval)) \
3770 rval = lval; \
3771 if (!js_TestXMLEquality(cx, obj2, rval, &cond)) \
3772 goto error; \
3773 cond = cond OP JS_TRUE; \
3774 } else
3776 #define EXTENDED_EQUALITY_OP(OP) \
3777 if (ltmp == JSVAL_OBJECT && \
3778 (obj2 = JSVAL_TO_OBJECT(lval)) && \
3779 ((clasp = OBJ_GET_CLASS(cx, obj2))->flags & JSCLASS_IS_EXTENDED)) { \
3780 JSExtendedClass *xclasp; \
3782 xclasp = (JSExtendedClass *) clasp; \
3783 if (!xclasp->equality(cx, obj2, rval, &cond)) \
3784 goto error; \
3785 cond = cond OP JS_TRUE; \
3786 } else
3787 #else
3788 #define XML_EQUALITY_OP(OP) /* nothing */
3789 #define EXTENDED_EQUALITY_OP(OP) /* nothing */
3790 #endif
3792 #define EQUALITY_OP(OP, IFNAN) \
3793 JS_BEGIN_MACRO \
3794 rval = FETCH_OPND(-1); \
3795 lval = FETCH_OPND(-2); \
3796 ltmp = JSVAL_TAG(lval); \
3797 rtmp = JSVAL_TAG(rval); \
3798 XML_EQUALITY_OP(OP) \
3799 if (ltmp == rtmp) { \
3800 if (ltmp == JSVAL_STRING) { \
3801 str = JSVAL_TO_STRING(lval); \
3802 str2 = JSVAL_TO_STRING(rval); \
3803 cond = js_EqualStrings(str, str2) OP JS_TRUE; \
3804 } else if (ltmp == JSVAL_DOUBLE) { \
3805 d = *JSVAL_TO_DOUBLE(lval); \
3806 d2 = *JSVAL_TO_DOUBLE(rval); \
3807 cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \
3808 } else { \
3809 EXTENDED_EQUALITY_OP(OP) \
3810 /* Handle all undefined (=>NaN) and int combinations. */ \
3811 cond = lval OP rval; \
3813 } else { \
3814 if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) { \
3815 cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) OP 1; \
3816 } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) { \
3817 cond = 1 OP 0; \
3818 } else { \
3819 if (ltmp == JSVAL_OBJECT) { \
3820 DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval); \
3821 ltmp = JSVAL_TAG(lval); \
3822 } else if (rtmp == JSVAL_OBJECT) { \
3823 DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); \
3824 rtmp = JSVAL_TAG(rval); \
3826 if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) { \
3827 str = JSVAL_TO_STRING(lval); \
3828 str2 = JSVAL_TO_STRING(rval); \
3829 cond = js_EqualStrings(str, str2) OP JS_TRUE; \
3830 } else { \
3831 VALUE_TO_NUMBER(cx, -2, lval, d); \
3832 VALUE_TO_NUMBER(cx, -1, rval, d2); \
3833 cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \
3837 TRY_BRANCH_AFTER_COND(cond, 2); \
3838 regs.sp--; \
3839 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
3840 JS_END_MACRO
3842 BEGIN_CASE(JSOP_EQ)
3843 EQUALITY_OP(==, JS_FALSE);
3844 END_CASE(JSOP_EQ)
3846 BEGIN_CASE(JSOP_NE)
3847 EQUALITY_OP(!=, JS_TRUE);
3848 END_CASE(JSOP_NE)
3850 #define STRICT_EQUALITY_OP(OP) \
3851 JS_BEGIN_MACRO \
3852 rval = FETCH_OPND(-1); \
3853 lval = FETCH_OPND(-2); \
3854 cond = js_StrictlyEqual(cx, lval, rval) OP JS_TRUE; \
3855 regs.sp--; \
3856 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
3857 JS_END_MACRO
3859 BEGIN_CASE(JSOP_STRICTEQ)
3860 STRICT_EQUALITY_OP(==);
3861 END_CASE(JSOP_STRICTEQ)
3863 BEGIN_CASE(JSOP_STRICTNE)
3864 STRICT_EQUALITY_OP(!=);
3865 END_CASE(JSOP_STRICTNE)
3867 BEGIN_CASE(JSOP_CASE)
3868 STRICT_EQUALITY_OP(==);
3869 (void) POP();
3870 if (cond) {
3871 len = GET_JUMP_OFFSET(regs.pc);
3872 BRANCH(len);
3874 PUSH(lval);
3875 END_CASE(JSOP_CASE)
3877 BEGIN_CASE(JSOP_CASEX)
3878 STRICT_EQUALITY_OP(==);
3879 (void) POP();
3880 if (cond) {
3881 len = GET_JUMPX_OFFSET(regs.pc);
3882 BRANCH(len);
3884 PUSH(lval);
3885 END_CASE(JSOP_CASEX)
3887 BEGIN_CASE(JSOP_LT)
3888 RELATIONAL_OP(<);
3889 END_CASE(JSOP_LT)
3891 BEGIN_CASE(JSOP_LE)
3892 RELATIONAL_OP(<=);
3893 END_CASE(JSOP_LE)
3895 BEGIN_CASE(JSOP_GT)
3896 RELATIONAL_OP(>);
3897 END_CASE(JSOP_GT)
3899 BEGIN_CASE(JSOP_GE)
3900 RELATIONAL_OP(>=);
3901 END_CASE(JSOP_GE)
3903 #undef EQUALITY_OP
3904 #undef RELATIONAL_OP
3906 #define SIGNED_SHIFT_OP(OP) \
3907 JS_BEGIN_MACRO \
3908 FETCH_INT(cx, -2, i); \
3909 FETCH_INT(cx, -1, j); \
3910 i = i OP (j & 31); \
3911 regs.sp--; \
3912 STORE_INT(cx, -1, i); \
3913 JS_END_MACRO
3915 BEGIN_CASE(JSOP_LSH)
3916 SIGNED_SHIFT_OP(<<);
3917 END_CASE(JSOP_LSH)
3919 BEGIN_CASE(JSOP_RSH)
3920 SIGNED_SHIFT_OP(>>);
3921 END_CASE(JSOP_RSH)
3923 BEGIN_CASE(JSOP_URSH)
3925 uint32 u;
3927 FETCH_UINT(cx, -2, u);
3928 FETCH_INT(cx, -1, j);
3929 u >>= (j & 31);
3930 regs.sp--;
3931 STORE_UINT(cx, -1, u);
3933 END_CASE(JSOP_URSH)
3935 #undef BITWISE_OP
3936 #undef SIGNED_SHIFT_OP
3938 BEGIN_CASE(JSOP_ADD)
3939 rval = FETCH_OPND(-1);
3940 lval = FETCH_OPND(-2);
3941 #if JS_HAS_XML_SUPPORT
3942 if (!JSVAL_IS_PRIMITIVE(lval) &&
3943 (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) &&
3944 VALUE_IS_XML(cx, rval)) {
3945 if (!js_ConcatenateXML(cx, obj2, rval, &rval))
3946 goto error;
3947 regs.sp--;
3948 STORE_OPND(-1, rval);
3949 } else
3950 #endif
3952 if (!JSVAL_IS_PRIMITIVE(lval))
3953 DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval);
3954 if (!JSVAL_IS_PRIMITIVE(rval))
3955 DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval);
3956 if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) {
3957 if (cond) {
3958 str = JSVAL_TO_STRING(lval);
3959 str2 = js_ValueToString(cx, rval);
3960 if (!str2)
3961 goto error;
3962 regs.sp[-1] = STRING_TO_JSVAL(str2);
3963 } else {
3964 str2 = JSVAL_TO_STRING(rval);
3965 str = js_ValueToString(cx, lval);
3966 if (!str)
3967 goto error;
3968 regs.sp[-2] = STRING_TO_JSVAL(str);
3970 str = js_ConcatStrings(cx, str, str2);
3971 if (!str)
3972 goto error;
3973 regs.sp--;
3974 STORE_OPND(-1, STRING_TO_JSVAL(str));
3975 } else {
3976 VALUE_TO_NUMBER(cx, -2, lval, d);
3977 VALUE_TO_NUMBER(cx, -1, rval, d2);
3978 d += d2;
3979 regs.sp--;
3980 STORE_NUMBER(cx, -1, d);
3983 END_CASE(JSOP_ADD)
3985 #define BINARY_OP(OP) \
3986 JS_BEGIN_MACRO \
3987 FETCH_NUMBER(cx, -2, d); \
3988 FETCH_NUMBER(cx, -1, d2); \
3989 d = d OP d2; \
3990 regs.sp--; \
3991 STORE_NUMBER(cx, -1, d); \
3992 JS_END_MACRO
3994 BEGIN_CASE(JSOP_SUB)
3995 BINARY_OP(-);
3996 END_CASE(JSOP_SUB)
3998 BEGIN_CASE(JSOP_MUL)
3999 BINARY_OP(*);
4000 END_CASE(JSOP_MUL)
4002 BEGIN_CASE(JSOP_DIV)
4003 FETCH_NUMBER(cx, -1, d2);
4004 FETCH_NUMBER(cx, -2, d);
4005 regs.sp--;
4006 if (d2 == 0) {
4007 #ifdef XP_WIN
4008 /* XXX MSVC miscompiles such that (NaN == 0) */
4009 if (JSDOUBLE_IS_NaN(d2))
4010 rval = DOUBLE_TO_JSVAL(rt->jsNaN);
4011 else
4012 #endif
4013 if (d == 0 || JSDOUBLE_IS_NaN(d))
4014 rval = DOUBLE_TO_JSVAL(rt->jsNaN);
4015 else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
4016 rval = DOUBLE_TO_JSVAL(rt->jsNegativeInfinity);
4017 else
4018 rval = DOUBLE_TO_JSVAL(rt->jsPositiveInfinity);
4019 STORE_OPND(-1, rval);
4020 } else {
4021 d /= d2;
4022 STORE_NUMBER(cx, -1, d);
4024 END_CASE(JSOP_DIV)
4026 BEGIN_CASE(JSOP_MOD)
4027 FETCH_NUMBER(cx, -1, d2);
4028 FETCH_NUMBER(cx, -2, d);
4029 regs.sp--;
4030 if (d2 == 0) {
4031 STORE_OPND(-1, DOUBLE_TO_JSVAL(rt->jsNaN));
4032 } else {
4033 d = js_fmod(d, d2);
4034 STORE_NUMBER(cx, -1, d);
4036 END_CASE(JSOP_MOD)
4038 BEGIN_CASE(JSOP_NOT)
4039 POP_BOOLEAN(cx, rval, cond);
4040 PUSH_OPND(BOOLEAN_TO_JSVAL(!cond));
4041 END_CASE(JSOP_NOT)
4043 BEGIN_CASE(JSOP_BITNOT)
4044 FETCH_INT(cx, -1, i);
4045 i = ~i;
4046 STORE_INT(cx, -1, i);
4047 END_CASE(JSOP_BITNOT)
4049 BEGIN_CASE(JSOP_NEG)
4051 * When the operand is int jsval, INT_FITS_IN_JSVAL(i) implies
4052 * INT_FITS_IN_JSVAL(-i) unless i is 0 or JSVAL_INT_MIN when the
4053 * results, -0.0 or JSVAL_INT_MAX + 1, are jsdouble values.
4055 rval = FETCH_OPND(-1);
4056 if (JSVAL_IS_INT(rval) &&
4057 rval != INT_TO_JSVAL(JSVAL_INT_MIN) &&
4058 (i = JSVAL_TO_INT(rval)) != 0) {
4059 JS_STATIC_ASSERT(!INT_FITS_IN_JSVAL(-JSVAL_INT_MIN));
4060 i = -i;
4061 JS_ASSERT(INT_FITS_IN_JSVAL(i));
4062 regs.sp[-1] = INT_TO_JSVAL(i);
4063 } else {
4064 if (JSVAL_IS_DOUBLE(rval)) {
4065 d = *JSVAL_TO_DOUBLE(rval);
4066 } else {
4067 d = js_ValueToNumber(cx, &regs.sp[-1]);
4068 if (JSVAL_IS_NULL(regs.sp[-1]))
4069 goto error;
4070 JS_ASSERT(JSVAL_IS_NUMBER(regs.sp[-1]) ||
4071 regs.sp[-1] == JSVAL_TRUE);
4073 #ifdef HPUX
4075 * Negation of a zero doesn't produce a negative
4076 * zero on HPUX. Perform the operation by bit
4077 * twiddling.
4079 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
4080 #else
4081 d = -d;
4082 #endif
4083 if (!js_NewNumberInRootedValue(cx, d, &regs.sp[-1]))
4084 goto error;
4086 END_CASE(JSOP_NEG)
4088 BEGIN_CASE(JSOP_POS)
4089 rval = FETCH_OPND(-1);
4090 if (!JSVAL_IS_NUMBER(rval)) {
4091 d = js_ValueToNumber(cx, &regs.sp[-1]);
4092 rval = regs.sp[-1];
4093 if (JSVAL_IS_NULL(rval))
4094 goto error;
4095 if (rval == JSVAL_TRUE) {
4096 if (!js_NewNumberInRootedValue(cx, d, &regs.sp[-1]))
4097 goto error;
4098 } else {
4099 JS_ASSERT(JSVAL_IS_NUMBER(rval));
4102 END_CASE(JSOP_POS)
4104 BEGIN_CASE(JSOP_DELNAME)
4105 LOAD_ATOM(0);
4106 id = ATOM_TO_JSID(atom);
4107 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
4108 goto error;
4110 /* ECMA says to return true if name is undefined or inherited. */
4111 PUSH_OPND(JSVAL_TRUE);
4112 if (prop) {
4113 OBJ_DROP_PROPERTY(cx, obj2, prop);
4114 if (!OBJ_DELETE_PROPERTY(cx, obj, id, &regs.sp[-1]))
4115 goto error;
4117 END_CASE(JSOP_DELNAME)
4119 BEGIN_CASE(JSOP_DELPROP)
4120 LOAD_ATOM(0);
4121 id = ATOM_TO_JSID(atom);
4122 PROPERTY_OP(-1, OBJ_DELETE_PROPERTY(cx, obj, id, &rval));
4123 STORE_OPND(-1, rval);
4124 END_CASE(JSOP_DELPROP)
4126 BEGIN_CASE(JSOP_DELELEM)
4127 ELEMENT_OP(-1, OBJ_DELETE_PROPERTY(cx, obj, id, &rval));
4128 regs.sp--;
4129 STORE_OPND(-1, rval);
4130 END_CASE(JSOP_DELELEM)
4132 BEGIN_CASE(JSOP_TYPEOFEXPR)
4133 BEGIN_CASE(JSOP_TYPEOF)
4134 rval = FETCH_OPND(-1);
4135 type = JS_TypeOfValue(cx, rval);
4136 atom = rt->atomState.typeAtoms[type];
4137 STORE_OPND(-1, ATOM_KEY(atom));
4138 END_CASE(JSOP_TYPEOF)
4140 BEGIN_CASE(JSOP_VOID)
4141 STORE_OPND(-1, JSVAL_VOID);
4142 END_CASE(JSOP_VOID)
4144 BEGIN_CASE(JSOP_INCELEM)
4145 BEGIN_CASE(JSOP_DECELEM)
4146 BEGIN_CASE(JSOP_ELEMINC)
4147 BEGIN_CASE(JSOP_ELEMDEC)
4149 * Delay fetching of id until we have the object to ensure
4150 * the proper evaluation order. See bug 372331.
4152 id = 0;
4153 i = -2;
4154 goto fetch_incop_obj;
4156 BEGIN_CASE(JSOP_INCPROP)
4157 BEGIN_CASE(JSOP_DECPROP)
4158 BEGIN_CASE(JSOP_PROPINC)
4159 BEGIN_CASE(JSOP_PROPDEC)
4160 LOAD_ATOM(0);
4161 id = ATOM_TO_JSID(atom);
4162 i = -1;
4164 fetch_incop_obj:
4165 FETCH_OBJECT(cx, i, lval, obj);
4166 if (id == 0)
4167 FETCH_ELEMENT_ID(obj, -1, id);
4168 goto do_incop;
4170 BEGIN_CASE(JSOP_INCNAME)
4171 BEGIN_CASE(JSOP_DECNAME)
4172 BEGIN_CASE(JSOP_NAMEINC)
4173 BEGIN_CASE(JSOP_NAMEDEC)
4175 JSPropCacheEntry *entry;
4177 obj = fp->scopeChain;
4178 if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
4179 PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);
4180 if (!atom) {
4181 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
4182 if (obj == obj2 && PCVAL_IS_SLOT(entry->vword)) {
4183 slot = PCVAL_TO_SLOT(entry->vword);
4184 JS_ASSERT(slot < OBJ_SCOPE(obj)->freeslot);
4185 rval = LOCKED_OBJ_GET_SLOT(obj, slot);
4186 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {
4187 rtmp = rval;
4188 rval += (js_CodeSpec[op].format & JOF_INC) ? 2 : -2;
4189 if (!(js_CodeSpec[op].format & JOF_POST))
4190 rtmp = rval;
4191 LOCKED_OBJ_SET_SLOT(obj, slot, rval);
4192 JS_UNLOCK_OBJ(cx, obj);
4193 PUSH_OPND(rtmp);
4194 len = JSOP_INCNAME_LENGTH;
4195 DO_NEXT_OP(len);
4198 JS_UNLOCK_OBJ(cx, obj2);
4199 LOAD_ATOM(0);
4201 } else {
4202 LOAD_ATOM(0);
4204 id = ATOM_TO_JSID(atom);
4205 if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
4206 goto error;
4207 if (!prop)
4208 goto atom_not_defined;
4209 OBJ_DROP_PROPERTY(cx, obj2, prop);
4212 do_incop:
4214 const JSCodeSpec *cs;
4215 jsval v;
4218 * We need a root to store the value to leave on the stack until
4219 * we have done with OBJ_SET_PROPERTY.
4221 PUSH_OPND(JSVAL_NULL);
4222 if (!OBJ_GET_PROPERTY(cx, obj, id, &regs.sp[-1]))
4223 goto error;
4225 cs = &js_CodeSpec[op];
4226 JS_ASSERT(cs->ndefs == 1);
4227 JS_ASSERT((cs->format & JOF_TMPSLOT_MASK) == JOF_TMPSLOT2);
4228 v = regs.sp[-1];
4229 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(v))) {
4230 jsval incr;
4232 incr = (cs->format & JOF_INC) ? 2 : -2;
4233 if (cs->format & JOF_POST) {
4234 regs.sp[-1] = v + incr;
4235 } else {
4236 v += incr;
4237 regs.sp[-1] = v;
4239 fp->flags |= JSFRAME_ASSIGNING;
4240 ok = OBJ_SET_PROPERTY(cx, obj, id, &regs.sp[-1]);
4241 fp->flags &= ~JSFRAME_ASSIGNING;
4242 if (!ok)
4243 goto error;
4246 * We must set regs.sp[-1] to v for both post and pre increments
4247 * as the setter overwrites regs.sp[-1].
4249 regs.sp[-1] = v;
4250 } else {
4251 /* We need an extra root for the result. */
4252 PUSH_OPND(JSVAL_NULL);
4253 if (!js_DoIncDec(cx, cs, &regs.sp[-2], &regs.sp[-1]))
4254 goto error;
4255 fp->flags |= JSFRAME_ASSIGNING;
4256 ok = OBJ_SET_PROPERTY(cx, obj, id, &regs.sp[-1]);
4257 fp->flags &= ~JSFRAME_ASSIGNING;
4258 if (!ok)
4259 goto error;
4260 regs.sp--;
4263 if (cs->nuses == 0) {
4264 /* regs.sp[-1] already contains the result of name increment. */
4265 } else {
4266 rtmp = regs.sp[-1];
4267 regs.sp -= cs->nuses;
4268 regs.sp[-1] = rtmp;
4270 len = cs->length;
4271 DO_NEXT_OP(len);
4275 jsval incr, incr2;
4277 /* Position cases so the most frequent i++ does not need a jump. */
4278 BEGIN_CASE(JSOP_DECARG)
4279 incr = -2; incr2 = -2; goto do_arg_incop;
4280 BEGIN_CASE(JSOP_ARGDEC)
4281 incr = -2; incr2 = 0; goto do_arg_incop;
4282 BEGIN_CASE(JSOP_INCARG)
4283 incr = 2; incr2 = 2; goto do_arg_incop;
4284 BEGIN_CASE(JSOP_ARGINC)
4285 incr = 2; incr2 = 0;
4287 do_arg_incop:
4288 slot = GET_ARGNO(regs.pc);
4289 JS_ASSERT(slot < fp->fun->nargs);
4290 METER_SLOT_OP(op, slot);
4291 vp = fp->argv + slot;
4292 goto do_int_fast_incop;
4294 BEGIN_CASE(JSOP_DECLOCAL)
4295 incr = -2; incr2 = -2; goto do_local_incop;
4296 BEGIN_CASE(JSOP_LOCALDEC)
4297 incr = -2; incr2 = 0; goto do_local_incop;
4298 BEGIN_CASE(JSOP_INCLOCAL)
4299 incr = 2; incr2 = 2; goto do_local_incop;
4300 BEGIN_CASE(JSOP_LOCALINC)
4301 incr = 2; incr2 = 0;
4304 * do_local_incop comes right before do_int_fast_incop as we want to
4305 * avoid an extra jump for variable cases as local++ is more frequent
4306 * than arg++.
4308 do_local_incop:
4309 slot = GET_SLOTNO(regs.pc);
4310 JS_ASSERT(slot < fp->script->nslots);
4311 vp = fp->slots + slot;
4312 METER_SLOT_OP(op, slot);
4313 vp = fp->slots + slot;
4315 do_int_fast_incop:
4316 rval = *vp;
4317 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {
4318 *vp = rval + incr;
4319 JS_ASSERT(JSOP_INCARG_LENGTH == js_CodeSpec[op].length);
4320 SKIP_POP_AFTER_SET(JSOP_INCARG_LENGTH, 0);
4321 PUSH_OPND(rval + incr2);
4322 } else {
4323 PUSH_OPND(rval);
4324 if (!js_DoIncDec(cx, &js_CodeSpec[op], &regs.sp[-1], vp))
4325 goto error;
4327 len = JSOP_INCARG_LENGTH;
4328 JS_ASSERT(len == js_CodeSpec[op].length);
4329 DO_NEXT_OP(len);
4332 /* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */
4333 #define FAST_GLOBAL_INCREMENT_OP(SLOWOP,INCR,INCR2) \
4334 op2 = SLOWOP; \
4335 incr = INCR; \
4336 incr2 = INCR2; \
4337 goto do_global_incop
4340 jsval incr, incr2;
4342 BEGIN_CASE(JSOP_DECGVAR)
4343 FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, -2, -2);
4344 BEGIN_CASE(JSOP_GVARDEC)
4345 FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, -2, 0);
4346 BEGIN_CASE(JSOP_INCGVAR)
4347 FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, 2, 2);
4348 BEGIN_CASE(JSOP_GVARINC)
4349 FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, 2, 0);
4351 #undef FAST_GLOBAL_INCREMENT_OP
4353 do_global_incop:
4354 JS_ASSERT((js_CodeSpec[op].format & JOF_TMPSLOT_MASK) ==
4355 JOF_TMPSLOT2);
4356 slot = GET_SLOTNO(regs.pc);
4357 JS_ASSERT(slot < GlobalVarCount(fp));
4358 METER_SLOT_OP(op, slot);
4359 lval = fp->slots[slot];
4360 if (JSVAL_IS_NULL(lval)) {
4361 op = op2;
4362 DO_OP();
4364 slot = JSVAL_TO_INT(lval);
4365 rval = OBJ_GET_SLOT(cx, fp->varobj, slot);
4366 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {
4367 PUSH_OPND(rval + incr2);
4368 rval += incr;
4369 } else {
4370 PUSH_OPND(rval);
4371 PUSH_OPND(JSVAL_NULL); /* Extra root */
4372 if (!js_DoIncDec(cx, &js_CodeSpec[op], &regs.sp[-2], &regs.sp[-1]))
4373 goto error;
4374 rval = regs.sp[-1];
4375 --regs.sp;
4377 OBJ_SET_SLOT(cx, fp->varobj, slot, rval);
4378 len = JSOP_INCGVAR_LENGTH; /* all gvar incops are same length */
4379 JS_ASSERT(len == js_CodeSpec[op].length);
4380 DO_NEXT_OP(len);
4383 #define COMPUTE_THIS(cx, fp, obj) \
4384 JS_BEGIN_MACRO \
4385 if (!(obj = js_ComputeThisForFrame(cx, fp))) \
4386 goto error; \
4387 JS_END_MACRO
4389 BEGIN_CASE(JSOP_THIS)
4390 COMPUTE_THIS(cx, fp, obj);
4391 PUSH_OPND(OBJECT_TO_JSVAL(obj));
4392 END_CASE(JSOP_THIS)
4394 BEGIN_CASE(JSOP_GETTHISPROP)
4395 i = 0;
4396 COMPUTE_THIS(cx, fp, obj);
4397 PUSH(JSVAL_NULL);
4398 goto do_getprop_with_obj;
4400 #undef COMPUTE_THIS
4402 BEGIN_CASE(JSOP_GETARGPROP)
4403 i = ARGNO_LEN;
4404 slot = GET_ARGNO(regs.pc);
4405 JS_ASSERT(slot < fp->fun->nargs);
4406 PUSH_OPND(fp->argv[slot]);
4407 goto do_getprop_body;
4409 BEGIN_CASE(JSOP_GETLOCALPROP)
4410 i = SLOTNO_LEN;
4411 slot = GET_SLOTNO(regs.pc);
4412 JS_ASSERT(slot < script->nslots);
4413 PUSH_OPND(fp->slots[slot]);
4414 goto do_getprop_body;
4416 BEGIN_CASE(JSOP_GETPROP)
4417 BEGIN_CASE(JSOP_GETXPROP)
4418 i = 0;
4420 do_getprop_body:
4421 lval = FETCH_OPND(-1);
4423 do_getprop_with_lval:
4424 VALUE_TO_OBJECT(cx, -1, lval, obj);
4426 do_getprop_with_obj:
4427 do {
4428 JSObject *aobj;
4429 JSPropCacheEntry *entry;
4431 aobj = js_GetProtoIfDenseArray(cx, obj);
4432 if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) {
4433 PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom);
4434 if (!atom) {
4435 ASSERT_VALID_PROPERTY_CACHE_HIT(i, aobj, obj2, entry);
4436 if (PCVAL_IS_OBJECT(entry->vword)) {
4437 rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);
4438 } else if (PCVAL_IS_SLOT(entry->vword)) {
4439 slot = PCVAL_TO_SLOT(entry->vword);
4440 JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
4441 rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
4442 } else {
4443 JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
4444 sprop = PCVAL_TO_SPROP(entry->vword);
4445 NATIVE_GET(cx, obj, obj2, sprop, &rval);
4447 JS_UNLOCK_OBJ(cx, obj2);
4448 break;
4450 } else {
4451 entry = NULL;
4452 if (i < 0)
4453 atom = rt->atomState.lengthAtom;
4454 else
4455 LOAD_ATOM(i);
4457 id = ATOM_TO_JSID(atom);
4458 if (entry
4459 ? !js_GetPropertyHelper(cx, obj, id, true, &rval)
4460 : !OBJ_GET_PROPERTY(cx, obj, id, &rval)) {
4461 goto error;
4463 } while (0);
4465 STORE_OPND(-1, rval);
4466 JS_ASSERT(JSOP_GETPROP_LENGTH + i == js_CodeSpec[op].length);
4467 len = JSOP_GETPROP_LENGTH + i;
4468 END_VARLEN_CASE
4470 BEGIN_CASE(JSOP_LENGTH)
4471 lval = FETCH_OPND(-1);
4472 if (JSVAL_IS_STRING(lval)) {
4473 str = JSVAL_TO_STRING(lval);
4474 regs.sp[-1] = INT_TO_JSVAL(str->length());
4475 } else if (!JSVAL_IS_PRIMITIVE(lval) &&
4476 (obj = JSVAL_TO_OBJECT(lval), OBJ_IS_ARRAY(cx, obj))) {
4477 jsuint length;
4480 * We know that the array is created with only its 'length'
4481 * private data in a fixed slot at JSSLOT_ARRAY_LENGTH. See
4482 * also JSOP_ARRAYPUSH, far below.
4484 length = obj->fslots[JSSLOT_ARRAY_LENGTH];
4485 if (length <= JSVAL_INT_MAX) {
4486 regs.sp[-1] = INT_TO_JSVAL(length);
4487 } else if (!js_NewDoubleInRootedValue(cx, (jsdouble) length,
4488 &regs.sp[-1])) {
4489 goto error;
4491 } else {
4492 i = -2;
4493 goto do_getprop_with_lval;
4495 END_CASE(JSOP_LENGTH)
4497 BEGIN_CASE(JSOP_CALLPROP)
4499 JSObject *aobj;
4500 JSPropCacheEntry *entry;
4502 lval = FETCH_OPND(-1);
4503 if (!JSVAL_IS_PRIMITIVE(lval)) {
4504 obj = JSVAL_TO_OBJECT(lval);
4505 } else {
4506 if (JSVAL_IS_STRING(lval)) {
4507 i = JSProto_String;
4508 } else if (JSVAL_IS_NUMBER(lval)) {
4509 i = JSProto_Number;
4510 } else if (JSVAL_IS_BOOLEAN(lval)) {
4511 i = JSProto_Boolean;
4512 } else {
4513 JS_ASSERT(JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval));
4514 js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
4515 goto error;
4518 if (!js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj))
4519 goto error;
4522 aobj = js_GetProtoIfDenseArray(cx, obj);
4523 if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) {
4524 PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom);
4525 if (!atom) {
4526 ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry);
4527 if (PCVAL_IS_OBJECT(entry->vword)) {
4528 rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);
4529 } else if (PCVAL_IS_SLOT(entry->vword)) {
4530 slot = PCVAL_TO_SLOT(entry->vword);
4531 JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
4532 rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
4533 } else {
4534 JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
4535 sprop = PCVAL_TO_SPROP(entry->vword);
4536 NATIVE_GET(cx, obj, obj2, sprop, &rval);
4538 JS_UNLOCK_OBJ(cx, obj2);
4539 STORE_OPND(-1, rval);
4540 PUSH_OPND(lval);
4541 goto end_callprop;
4543 } else {
4544 entry = NULL;
4545 LOAD_ATOM(0);
4549 * Cache miss: use the immediate atom that was loaded for us under
4550 * PROPERTY_CACHE_TEST.
4552 id = ATOM_TO_JSID(atom);
4553 PUSH(JSVAL_NULL);
4554 if (!JSVAL_IS_PRIMITIVE(lval)) {
4555 if (!js_GetMethod(cx, obj, id, !!entry, &rval))
4556 goto error;
4557 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
4558 STORE_OPND(-2, rval);
4559 } else {
4560 JS_ASSERT(obj->map->ops->getProperty == js_GetProperty);
4561 if (!js_GetPropertyHelper(cx, obj, id, true, &rval))
4562 goto error;
4563 STORE_OPND(-1, lval);
4564 STORE_OPND(-2, rval);
4567 end_callprop:
4568 /* Wrap primitive lval in object clothing if necessary. */
4569 if (JSVAL_IS_PRIMITIVE(lval)) {
4570 /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */
4571 if (!VALUE_IS_FUNCTION(cx, rval) ||
4572 (obj = JSVAL_TO_OBJECT(rval),
4573 fun = GET_FUNCTION_PRIVATE(cx, obj),
4574 !PRIMITIVE_THIS_TEST(fun, lval))) {
4575 if (!js_PrimitiveToObject(cx, &regs.sp[-1]))
4576 goto error;
4579 #if JS_HAS_NO_SUCH_METHOD
4580 if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) {
4581 LOAD_ATOM(0);
4582 regs.sp[-2] = ATOM_KEY(atom);
4583 if (!js_OnUnknownMethod(cx, regs.sp - 2))
4584 goto error;
4586 #endif
4588 END_CASE(JSOP_CALLPROP)
4590 BEGIN_CASE(JSOP_SETNAME)
4591 BEGIN_CASE(JSOP_SETPROP)
4592 rval = FETCH_OPND(-1);
4593 lval = FETCH_OPND(-2);
4594 JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval) || op == JSOP_SETPROP);
4595 VALUE_TO_OBJECT(cx, -2, lval, obj);
4597 do {
4598 JSPropCacheEntry *entry;
4600 entry = NULL;
4601 atom = NULL;
4602 if (JS_LIKELY(obj->map->ops->setProperty == js_SetProperty)) {
4603 JSPropertyCache *cache = &JS_PROPERTY_CACHE(cx);
4604 uint32 kshape = OBJ_SHAPE(obj);
4607 * Open-code PROPERTY_CACHE_TEST, specializing for two
4608 * important set-property cases. First:
4610 * function f(a, b, c) {
4611 * var o = {p:a, q:b, r:c};
4612 * return o;
4615 * or similar real-world cases, which evolve a newborn
4616 * native object predicatably through some bounded number
4617 * of property additions. And second:
4619 * o.p = x;
4621 * in a frequently executed method or loop body, where p
4622 * will (possibly after the first iteration) always exist
4623 * in native object o.
4625 entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)];
4626 PCMETER(cache->pctestentry = entry);
4627 PCMETER(cache->tests++);
4628 PCMETER(cache->settests++);
4629 if (entry->kpc == regs.pc && entry->kshape == kshape) {
4630 JS_ASSERT(PCVCAP_TAG(entry->vcap) <= 1);
4631 if (JS_LOCK_OBJ_IF_SHAPE(cx, obj, kshape)) {
4632 JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
4633 sprop = PCVAL_TO_SPROP(entry->vword);
4634 JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
4635 JS_ASSERT_IF(!(sprop->attrs & JSPROP_SHARED),
4636 PCVCAP_TAG(entry->vcap) == 0);
4638 JSScope *scope = OBJ_SCOPE(obj);
4639 JS_ASSERT(!scope->sealed());
4642 * Fastest path: check whether the cached sprop is
4643 * already in scope and call NATIVE_SET and break
4644 * to get out of the do-while(0). But we can call
4645 * NATIVE_SET only if obj owns scope or sprop is
4646 * shared.
4648 bool checkForAdd;
4649 if (sprop->attrs & JSPROP_SHARED) {
4650 if (PCVCAP_TAG(entry->vcap) == 0 ||
4651 ((obj2 = OBJ_GET_PROTO(cx, obj)) &&
4652 OBJ_IS_NATIVE(obj2) &&
4653 OBJ_SHAPE(obj2) == PCVCAP_SHAPE(entry->vcap))) {
4654 goto fast_set_propcache_hit;
4657 /* The cache entry doesn't apply. vshape mismatch. */
4658 checkForAdd = false;
4659 } else if (scope->owned()) {
4660 if (sprop == scope->lastProp || scope->has(sprop)) {
4661 fast_set_propcache_hit:
4662 PCMETER(cache->pchits++);
4663 PCMETER(cache->setpchits++);
4664 NATIVE_SET(cx, obj, sprop, entry, &rval);
4665 JS_UNLOCK_SCOPE(cx, scope);
4666 break;
4668 checkForAdd =
4669 !(sprop->attrs & JSPROP_SHARED) &&
4670 sprop->parent == scope->lastProp &&
4671 !scope->hadMiddleDelete();
4672 } else {
4673 scope = js_GetMutableScope(cx, obj);
4674 if (!scope) {
4675 JS_UNLOCK_OBJ(cx, obj);
4676 goto error;
4678 checkForAdd = !sprop->parent;
4681 if (checkForAdd &&
4682 SPROP_HAS_STUB_SETTER(sprop) &&
4683 (slot = sprop->slot) == scope->freeslot) {
4685 * Fast path: adding a plain old property that
4686 * was once at the frontier of the property
4687 * tree, whose slot is next to claim among the
4688 * allocated slots in obj, where scope->table
4689 * has not been created yet.
4691 * We may want to remove hazard conditions
4692 * above and inline compensation code here,
4693 * depending on real-world workloads.
4695 JS_ASSERT(!(LOCKED_OBJ_GET_CLASS(obj)->flags &
4696 JSCLASS_SHARE_ALL_PROPERTIES));
4698 PCMETER(cache->pchits++);
4699 PCMETER(cache->addpchits++);
4702 * Beware classes such as Function that use
4703 * the reserveSlots hook to allocate a number
4704 * of reserved slots that may vary with obj.
4706 if (slot < STOBJ_NSLOTS(obj) &&
4707 !OBJ_GET_CLASS(cx, obj)->reserveSlots) {
4708 ++scope->freeslot;
4709 } else {
4710 if (!js_AllocSlot(cx, obj, &slot)) {
4711 JS_UNLOCK_SCOPE(cx, scope);
4712 goto error;
4717 * If this obj's number of reserved slots
4718 * differed, or if something created a hash
4719 * table for scope, we must pay the price of
4720 * JSScope::add.
4722 * If slot does not match the cached sprop's
4723 * slot, update the cache entry in the hope
4724 * that obj and other instances with the same
4725 * number of reserved slots are now "hot".
4727 if (slot != sprop->slot || scope->table) {
4728 JSScopeProperty *sprop2 =
4729 scope->add(cx, sprop->id,
4730 sprop->getter, sprop->setter,
4731 slot, sprop->attrs,
4732 sprop->flags, sprop->shortid);
4733 if (!sprop2) {
4734 js_FreeSlot(cx, obj, slot);
4735 JS_UNLOCK_SCOPE(cx, scope);
4736 goto error;
4738 if (sprop2 != sprop) {
4739 PCMETER(cache->slotchanges++);
4740 JS_ASSERT(slot != sprop->slot &&
4741 slot == sprop2->slot &&
4742 sprop2->id == sprop->id);
4743 entry->vword = SPROP_TO_PCVAL(sprop2);
4745 sprop = sprop2;
4746 } else {
4747 scope->extend(cx, sprop);
4750 LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, rval);
4751 TRACE_2(SetPropHit, entry, sprop);
4752 LOCKED_OBJ_SET_SLOT(obj, slot, rval);
4753 JS_UNLOCK_SCOPE(cx, scope);
4756 * Purge the property cache of the id we may
4757 * have just shadowed in obj's scope and proto
4758 * chains. We do this after unlocking obj's
4759 * scope to avoid lock nesting.
4761 js_PurgeScopeChain(cx, obj, sprop->id);
4762 break;
4764 JS_UNLOCK_SCOPE(cx, scope);
4765 PCMETER(cache->setpcmisses++);
4769 atom = js_FullTestPropertyCache(cx, regs.pc, &obj, &obj2,
4770 &entry);
4771 if (atom) {
4772 PCMETER(cache->misses++);
4773 PCMETER(cache->setmisses++);
4774 } else {
4775 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
4776 sprop = NULL;
4777 if (obj == obj2) {
4778 JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
4779 sprop = PCVAL_TO_SPROP(entry->vword);
4780 JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
4781 JS_ASSERT(!OBJ_SCOPE(obj2)->sealed());
4782 NATIVE_SET(cx, obj, sprop, entry, &rval);
4784 JS_UNLOCK_OBJ(cx, obj2);
4785 if (sprop)
4786 break;
4790 if (!atom)
4791 LOAD_ATOM(0);
4792 id = ATOM_TO_JSID(atom);
4793 if (entry) {
4794 if (!js_SetPropertyHelper(cx, obj, id, true, &rval))
4795 goto error;
4796 } else {
4797 if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
4798 goto error;
4799 ABORT_RECORDING(cx, "Non-native set");
4801 } while (0);
4802 END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2);
4804 BEGIN_CASE(JSOP_GETELEM)
4805 /* Open-coded ELEMENT_OP optimized for strings and dense arrays. */
4806 lval = FETCH_OPND(-2);
4807 rval = FETCH_OPND(-1);
4808 if (JSVAL_IS_STRING(lval) && JSVAL_IS_INT(rval)) {
4809 str = JSVAL_TO_STRING(lval);
4810 i = JSVAL_TO_INT(rval);
4811 if ((size_t)i < str->length()) {
4812 str = js_GetUnitString(cx, str, (size_t)i);
4813 if (!str)
4814 goto error;
4815 rval = STRING_TO_JSVAL(str);
4816 goto end_getelem;
4820 VALUE_TO_OBJECT(cx, -2, lval, obj);
4821 if (JSVAL_IS_INT(rval)) {
4822 if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
4823 jsuint length;
4825 length = js_DenseArrayCapacity(obj);
4826 i = JSVAL_TO_INT(rval);
4827 if ((jsuint)i < length &&
4828 i < obj->fslots[JSSLOT_ARRAY_LENGTH]) {
4829 rval = obj->dslots[i];
4830 if (rval != JSVAL_HOLE)
4831 goto end_getelem;
4833 /* Reload rval from the stack in the rare hole case. */
4834 rval = FETCH_OPND(-1);
4837 id = INT_JSVAL_TO_JSID(rval);
4838 } else {
4839 if (!js_InternNonIntElementId(cx, obj, rval, &id))
4840 goto error;
4843 if (!OBJ_GET_PROPERTY(cx, obj, id, &rval))
4844 goto error;
4845 end_getelem:
4846 regs.sp--;
4847 STORE_OPND(-1, rval);
4848 END_CASE(JSOP_GETELEM)
4850 BEGIN_CASE(JSOP_CALLELEM)
4851 ELEMENT_OP(-1, js_GetMethod(cx, obj, id, false, &rval));
4852 #if JS_HAS_NO_SUCH_METHOD
4853 if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) {
4854 regs.sp[-2] = regs.sp[-1];
4855 regs.sp[-1] = OBJECT_TO_JSVAL(obj);
4856 if (!js_OnUnknownMethod(cx, regs.sp - 2))
4857 goto error;
4858 } else
4859 #endif
4861 STORE_OPND(-2, rval);
4862 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
4864 END_CASE(JSOP_CALLELEM)
4866 BEGIN_CASE(JSOP_SETELEM)
4867 rval = FETCH_OPND(-1);
4868 FETCH_OBJECT(cx, -3, lval, obj);
4869 FETCH_ELEMENT_ID(obj, -2, id);
4870 do {
4871 if (OBJ_IS_DENSE_ARRAY(cx, obj) && JSID_IS_INT(id)) {
4872 jsuint length;
4874 length = js_DenseArrayCapacity(obj);
4875 i = JSID_TO_INT(id);
4876 if ((jsuint)i < length) {
4877 if (obj->dslots[i] == JSVAL_HOLE) {
4878 if (js_PrototypeHasIndexedProperties(cx, obj))
4879 break;
4880 if (i >= obj->fslots[JSSLOT_ARRAY_LENGTH])
4881 obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1;
4882 obj->fslots[JSSLOT_ARRAY_COUNT]++;
4884 obj->dslots[i] = rval;
4885 goto end_setelem;
4888 } while (0);
4889 if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
4890 goto error;
4891 end_setelem:
4892 END_SET_CASE_STORE_RVAL(JSOP_SETELEM, 3)
4894 BEGIN_CASE(JSOP_ENUMELEM)
4895 /* Funky: the value to set is under the [obj, id] pair. */
4896 rval = FETCH_OPND(-3);
4897 FETCH_OBJECT(cx, -2, lval, obj);
4898 FETCH_ELEMENT_ID(obj, -1, id);
4899 if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
4900 goto error;
4901 regs.sp -= 3;
4902 END_CASE(JSOP_ENUMELEM)
4904 BEGIN_CASE(JSOP_NEW)
4905 /* Get immediate argc and find the constructor function. */
4906 argc = GET_ARGC(regs.pc);
4907 vp = regs.sp - (2 + argc);
4908 JS_ASSERT(vp >= StackBase(fp));
4911 * Assign lval, obj, and fun exactly as the code at inline_call:
4912 * expects to find them, to avoid nesting a js_Interpret call via
4913 * js_InvokeConstructor.
4915 lval = *vp;
4916 if (VALUE_IS_FUNCTION(cx, lval)) {
4917 obj = JSVAL_TO_OBJECT(lval);
4918 fun = GET_FUNCTION_PRIVATE(cx, obj);
4919 if (FUN_INTERPRETED(fun)) {
4920 /* Root as we go using vp[1]. */
4921 if (!OBJ_GET_PROPERTY(cx, obj,
4922 ATOM_TO_JSID(cx->runtime->atomState
4923 .classPrototypeAtom),
4924 &vp[1])) {
4925 goto error;
4927 rval = vp[1];
4928 obj2 = js_NewObject(cx, &js_ObjectClass,
4929 JSVAL_IS_OBJECT(rval)
4930 ? JSVAL_TO_OBJECT(rval)
4931 : NULL,
4932 OBJ_GET_PARENT(cx, obj));
4933 if (!obj2)
4934 goto error;
4935 vp[1] = OBJECT_TO_JSVAL(obj2);
4936 flags = JSFRAME_CONSTRUCTING;
4937 goto inline_call;
4941 if (!js_InvokeConstructor(cx, argc, JS_FALSE, vp))
4942 goto error;
4943 regs.sp = vp + 1;
4944 CHECK_INTERRUPT_HANDLER();
4945 TRACE_0(NativeCallComplete);
4946 END_CASE(JSOP_NEW)
4948 BEGIN_CASE(JSOP_CALL)
4949 BEGIN_CASE(JSOP_EVAL)
4950 BEGIN_CASE(JSOP_APPLY)
4951 argc = GET_ARGC(regs.pc);
4952 vp = regs.sp - (argc + 2);
4954 lval = *vp;
4955 if (VALUE_IS_FUNCTION(cx, lval)) {
4956 obj = JSVAL_TO_OBJECT(lval);
4957 fun = GET_FUNCTION_PRIVATE(cx, obj);
4959 /* Clear frame flags since this is not a constructor call. */
4960 flags = 0;
4961 if (FUN_INTERPRETED(fun))
4962 inline_call:
4964 uintN nframeslots, nvars, missing;
4965 JSArena *a;
4966 jsuword nbytes;
4967 void *newmark;
4968 jsval *newsp;
4969 JSInlineFrame *newifp;
4970 JSInterpreterHook hook;
4972 /* Restrict recursion of lightweight functions. */
4973 if (inlineCallCount == MAX_INLINE_CALL_COUNT) {
4974 js_ReportOverRecursed(cx);
4975 goto error;
4978 /* Compute the total number of stack slots needed by fun. */
4979 nframeslots = JS_HOWMANY(sizeof(JSInlineFrame),
4980 sizeof(jsval));
4981 script = fun->u.i.script;
4982 atoms = script->atomMap.vector;
4983 nbytes = (nframeslots + script->nslots) * sizeof(jsval);
4985 /* Allocate missing expected args adjacent to actuals. */
4986 a = cx->stackPool.current;
4987 newmark = (void *) a->avail;
4988 if (fun->nargs <= argc) {
4989 missing = 0;
4990 } else {
4991 newsp = vp + 2 + fun->nargs;
4992 JS_ASSERT(newsp > regs.sp);
4993 if ((jsuword) newsp <= a->limit) {
4994 if ((jsuword) newsp > a->avail)
4995 a->avail = (jsuword) newsp;
4996 jsval *argsp = newsp;
4997 do {
4998 *--argsp = JSVAL_VOID;
4999 } while (argsp != regs.sp);
5000 missing = 0;
5001 } else {
5002 missing = fun->nargs - argc;
5003 nbytes += (2 + fun->nargs) * sizeof(jsval);
5007 /* Allocate the inline frame with its slots and operands. */
5008 if (a->avail + nbytes <= a->limit) {
5009 newsp = (jsval *) a->avail;
5010 a->avail += nbytes;
5011 JS_ASSERT(missing == 0);
5012 } else {
5013 JS_ARENA_ALLOCATE_CAST(newsp, jsval *, &cx->stackPool,
5014 nbytes);
5015 if (!newsp) {
5016 js_ReportOutOfScriptQuota(cx);
5017 goto bad_inline_call;
5021 * Move args if the missing ones overflow arena a, then
5022 * push undefined for the missing args.
5024 if (missing) {
5025 memcpy(newsp, vp, (2 + argc) * sizeof(jsval));
5026 vp = newsp;
5027 newsp = vp + 2 + argc;
5028 do {
5029 *newsp++ = JSVAL_VOID;
5030 } while (--missing != 0);
5034 /* Claim space for the stack frame and initialize it. */
5035 newifp = (JSInlineFrame *) newsp;
5036 newsp += nframeslots;
5037 newifp->frame.callobj = NULL;
5038 newifp->frame.argsobj = NULL;
5039 newifp->frame.varobj = NULL;
5040 newifp->frame.script = script;
5041 newifp->frame.callee = obj;
5042 newifp->frame.fun = fun;
5043 newifp->frame.argc = argc;
5044 newifp->frame.argv = vp + 2;
5045 newifp->frame.rval = JSVAL_VOID;
5046 newifp->frame.down = fp;
5047 newifp->frame.annotation = NULL;
5048 newifp->frame.scopeChain = parent = OBJ_GET_PARENT(cx, obj);
5049 newifp->frame.sharpDepth = 0;
5050 newifp->frame.sharpArray = NULL;
5051 newifp->frame.flags = flags;
5052 newifp->frame.dormantNext = NULL;
5053 newifp->frame.xmlNamespace = NULL;
5054 newifp->frame.blockChain = NULL;
5055 if (script->staticLevel < JS_DISPLAY_SIZE) {
5056 JSStackFrame **disp = &cx->display[script->staticLevel];
5057 newifp->frame.displaySave = *disp;
5058 *disp = &newifp->frame;
5060 newifp->mark = newmark;
5062 /* Compute the 'this' parameter now that argv is set. */
5063 JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags));
5064 JS_ASSERT(JSVAL_IS_OBJECT(vp[1]));
5065 newifp->frame.thisp = (JSObject *)vp[1];
5067 newifp->frame.regs = NULL;
5068 newifp->frame.imacpc = NULL;
5069 newifp->frame.slots = newsp;
5071 /* Push void to initialize local variables. */
5072 nvars = fun->u.i.nvars;
5073 while (nvars--)
5074 *newsp++ = JSVAL_VOID;
5076 /* Scope with a call object parented by callee's parent. */
5077 if (JSFUN_HEAVYWEIGHT_TEST(fun->flags) &&
5078 !js_GetCallObject(cx, &newifp->frame)) {
5079 goto bad_inline_call;
5082 /* Switch version if currentVersion wasn't overridden. */
5083 newifp->callerVersion = (JSVersion) cx->version;
5084 if (JS_LIKELY(cx->version == currentVersion)) {
5085 currentVersion = (JSVersion) script->version;
5086 if (currentVersion != cx->version)
5087 js_SetVersion(cx, currentVersion);
5090 /* Push the frame and set interpreter registers. */
5091 newifp->callerRegs = regs;
5092 fp->regs = &newifp->callerRegs;
5093 regs.sp = newsp;
5094 regs.pc = script->code;
5095 newifp->frame.regs = &regs;
5096 cx->fp = fp = &newifp->frame;
5098 /* Call the debugger hook if present. */
5099 hook = cx->debugHooks->callHook;
5100 if (hook) {
5101 newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0,
5102 cx->debugHooks->callHookData);
5103 CHECK_INTERRUPT_HANDLER();
5104 } else {
5105 newifp->hookData = NULL;
5108 TRACE_0(EnterFrame);
5110 inlineCallCount++;
5111 JS_RUNTIME_METER(rt, inlineCalls);
5113 #ifdef INCLUDE_MOZILLA_DTRACE
5114 /* DTrace function entry, inlines */
5115 if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
5116 jsdtrace_function_entry(cx, fp, fun);
5117 if (JAVASCRIPT_FUNCTION_INFO_ENABLED())
5118 jsdtrace_function_info(cx, fp, fp->down, fun);
5119 if (JAVASCRIPT_FUNCTION_ARGS_ENABLED())
5120 jsdtrace_function_args(cx, fp, fun, fp->argc, fp->argv);
5121 #endif
5123 /* Load first op and dispatch it (safe since JSOP_STOP). */
5124 op = (JSOp) *regs.pc;
5125 DO_OP();
5127 bad_inline_call:
5128 JS_ASSERT(fp->regs == &regs);
5129 script = fp->script;
5130 atoms = script->atomMap.vector;
5131 js_FreeRawStack(cx, newmark);
5132 goto error;
5135 if (fun->flags & JSFUN_FAST_NATIVE) {
5136 #ifdef INCLUDE_MOZILLA_DTRACE
5137 /* DTrace function entry, non-inlines */
5138 if (VALUE_IS_FUNCTION(cx, lval)) {
5139 if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
5140 jsdtrace_function_entry(cx, NULL, fun);
5141 if (JAVASCRIPT_FUNCTION_INFO_ENABLED())
5142 jsdtrace_function_info(cx, NULL, fp, fun);
5143 if (JAVASCRIPT_FUNCTION_ARGS_ENABLED())
5144 jsdtrace_function_args(cx, fp, fun, argc, vp+2);
5146 #endif
5148 JS_ASSERT(fun->u.n.extra == 0);
5149 JS_ASSERT(JSVAL_IS_OBJECT(vp[1]) ||
5150 PRIMITIVE_THIS_TEST(fun, vp[1]));
5151 ok = ((JSFastNative) fun->u.n.native)(cx, argc, vp);
5152 #ifdef INCLUDE_MOZILLA_DTRACE
5153 if (VALUE_IS_FUNCTION(cx, lval)) {
5154 if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
5155 jsdtrace_function_rval(cx, NULL, fun, vp);
5156 if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
5157 jsdtrace_function_return(cx, NULL, fun);
5159 #endif
5160 regs.sp = vp + 1;
5161 if (!ok) {
5163 * If we are executing the JSOP_NEXTITER imacro and a Stopiteration
5164 * exception is raised, transform it into a JSVAL_HOLE return value.
5165 * The tracer generates equivalent code by calling CatchStopIteration_tn.
5167 if (fp->imacpc && *fp->imacpc == JSOP_NEXTITER &&
5168 cx->throwing && js_ValueIsStopIteration(cx->exception)) {
5169 // pc may point to JSOP_DUP here due to bug 474854.
5170 JS_ASSERT(*regs.pc == JSOP_CALL || *regs.pc == JSOP_DUP);
5171 cx->throwing = JS_FALSE;
5172 cx->exception = JSVAL_VOID;
5173 regs.sp[-1] = JSVAL_HOLE;
5174 } else {
5175 goto error;
5178 TRACE_0(NativeCallComplete);
5179 goto end_call;
5183 ok = js_Invoke(cx, argc, vp, 0);
5184 regs.sp = vp + 1;
5185 CHECK_INTERRUPT_HANDLER();
5186 if (!ok)
5187 goto error;
5188 JS_RUNTIME_METER(rt, nonInlineCalls);
5189 TRACE_0(NativeCallComplete);
5191 end_call:
5192 #if JS_HAS_LVALUE_RETURN
5193 if (cx->rval2set) {
5195 * Use the stack depth we didn't claim in our budget, but that
5196 * we know is there on account of [fun, this] already having
5197 * been pushed, at a minimum (if no args). Those two slots
5198 * have been popped and [rval] has been pushed, which leaves
5199 * one more slot for rval2 before we might overflow.
5201 * NB: rval2 must be the property identifier, and rval the
5202 * object from which to get the property. The pair form an
5203 * ECMA "reference type", which can be used on the right- or
5204 * left-hand side of assignment ops. Note well: only native
5205 * methods can return reference types. See JSOP_SETCALL just
5206 * below for the left-hand-side case.
5208 PUSH_OPND(cx->rval2);
5209 ELEMENT_OP(-1, OBJ_GET_PROPERTY(cx, obj, id, &rval));
5211 regs.sp--;
5212 STORE_OPND(-1, rval);
5213 cx->rval2set = JS_FALSE;
5215 #endif /* JS_HAS_LVALUE_RETURN */
5216 END_CASE(JSOP_CALL)
5218 #if JS_HAS_LVALUE_RETURN
5219 BEGIN_CASE(JSOP_SETCALL)
5220 argc = GET_ARGC(regs.pc);
5221 vp = regs.sp - argc - 2;
5222 ok = js_Invoke(cx, argc, vp, 0);
5223 regs.sp = vp + 1;
5224 CHECK_INTERRUPT_HANDLER();
5225 if (!ok)
5226 goto error;
5227 if (!cx->rval2set) {
5228 op2 = js_GetOpcode(cx, script, regs.pc + JSOP_SETCALL_LENGTH);
5229 if (op2 != JSOP_DELELEM) {
5230 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5231 JSMSG_BAD_LEFTSIDE_OF_ASS);
5232 goto error;
5236 * Store true as the result of the emulated delete of a
5237 * non-existent property. NB: We don't METER_OP_PAIR here;
5238 * it doesn't seem worth the code for this obscure case.
5240 *vp = JSVAL_TRUE;
5241 regs.pc += JSOP_SETCALL_LENGTH + JSOP_DELELEM_LENGTH;
5242 op = (JSOp) *regs.pc;
5243 DO_OP();
5245 PUSH_OPND(cx->rval2);
5246 cx->rval2set = JS_FALSE;
5247 END_CASE(JSOP_SETCALL)
5248 #endif
5250 BEGIN_CASE(JSOP_NAME)
5251 BEGIN_CASE(JSOP_CALLNAME)
5253 JSPropCacheEntry *entry;
5255 obj = fp->scopeChain;
5256 if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
5257 PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);
5258 if (!atom) {
5259 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
5260 if (PCVAL_IS_OBJECT(entry->vword)) {
5261 rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);
5262 JS_UNLOCK_OBJ(cx, obj2);
5263 goto do_push_rval;
5266 if (PCVAL_IS_SLOT(entry->vword)) {
5267 slot = PCVAL_TO_SLOT(entry->vword);
5268 JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
5269 rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
5270 JS_UNLOCK_OBJ(cx, obj2);
5271 goto do_push_rval;
5274 JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
5275 sprop = PCVAL_TO_SPROP(entry->vword);
5276 goto do_native_get;
5278 } else {
5279 LOAD_ATOM(0);
5282 id = ATOM_TO_JSID(atom);
5283 if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
5284 goto error;
5285 if (!prop) {
5286 /* Kludge to allow (typeof foo == "undefined") tests. */
5287 endpc = script->code + script->length;
5288 op2 = js_GetOpcode(cx, script, regs.pc + JSOP_NAME_LENGTH);
5289 if (op2 == JSOP_TYPEOF) {
5290 PUSH_OPND(JSVAL_VOID);
5291 len = JSOP_NAME_LENGTH;
5292 DO_NEXT_OP(len);
5294 goto atom_not_defined;
5297 /* Take the slow path if prop was not found in a native object. */
5298 if (!OBJ_IS_NATIVE(obj) || !OBJ_IS_NATIVE(obj2)) {
5299 OBJ_DROP_PROPERTY(cx, obj2, prop);
5300 if (!OBJ_GET_PROPERTY(cx, obj, id, &rval))
5301 goto error;
5302 } else {
5303 sprop = (JSScopeProperty *)prop;
5304 do_native_get:
5305 NATIVE_GET(cx, obj, obj2, sprop, &rval);
5306 OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *) sprop);
5309 do_push_rval:
5310 PUSH_OPND(rval);
5311 if (op == JSOP_CALLNAME)
5312 PUSH_OPND(OBJECT_TO_JSVAL(obj));
5314 END_CASE(JSOP_NAME)
5316 BEGIN_CASE(JSOP_UINT16)
5317 i = (jsint) GET_UINT16(regs.pc);
5318 rval = INT_TO_JSVAL(i);
5319 PUSH_OPND(rval);
5320 END_CASE(JSOP_UINT16)
5322 BEGIN_CASE(JSOP_UINT24)
5323 i = (jsint) GET_UINT24(regs.pc);
5324 rval = INT_TO_JSVAL(i);
5325 PUSH_OPND(rval);
5326 END_CASE(JSOP_UINT24)
5328 BEGIN_CASE(JSOP_INT8)
5329 i = GET_INT8(regs.pc);
5330 rval = INT_TO_JSVAL(i);
5331 PUSH_OPND(rval);
5332 END_CASE(JSOP_INT8)
5334 BEGIN_CASE(JSOP_INT32)
5335 i = GET_INT32(regs.pc);
5336 rval = INT_TO_JSVAL(i);
5337 PUSH_OPND(rval);
5338 END_CASE(JSOP_INT32)
5340 BEGIN_CASE(JSOP_INDEXBASE)
5342 * Here atoms can exceed script->atomMap.length as we use atoms
5343 * as a segment register for object literals as well.
5345 atoms += GET_INDEXBASE(regs.pc);
5346 END_CASE(JSOP_INDEXBASE)
5348 BEGIN_CASE(JSOP_INDEXBASE1)
5349 BEGIN_CASE(JSOP_INDEXBASE2)
5350 BEGIN_CASE(JSOP_INDEXBASE3)
5351 atoms += (op - JSOP_INDEXBASE1 + 1) << 16;
5352 END_CASE(JSOP_INDEXBASE3)
5354 BEGIN_CASE(JSOP_RESETBASE0)
5355 BEGIN_CASE(JSOP_RESETBASE)
5356 atoms = script->atomMap.vector;
5357 END_CASE(JSOP_RESETBASE)
5359 BEGIN_CASE(JSOP_DOUBLE)
5360 BEGIN_CASE(JSOP_STRING)
5361 LOAD_ATOM(0);
5362 PUSH_OPND(ATOM_KEY(atom));
5363 END_CASE(JSOP_DOUBLE)
5365 BEGIN_CASE(JSOP_OBJECT)
5366 LOAD_OBJECT(0);
5367 PUSH_OPND(OBJECT_TO_JSVAL(obj));
5368 END_CASE(JSOP_OBJECT)
5370 BEGIN_CASE(JSOP_REGEXP)
5372 JSObject *funobj;
5375 * Push a regexp object for the atom mapped by the bytecode at pc,
5376 * cloning the literal's regexp object if necessary, to simulate in
5377 * the pre-compile/execute-later case what ECMA specifies for the
5378 * compile-and-go case: that scanning each regexp literal creates
5379 * a single corresponding RegExp object.
5381 * To support pre-compilation transparently, we must handle the
5382 * case where a regexp object literal is used in a different global
5383 * at execution time from the global with which it was scanned at
5384 * compile time. We do this by re-wrapping the JSRegExp private
5385 * data struct with a cloned object having the right prototype and
5386 * parent, and having its own lastIndex property value storage.
5388 * Unlike JSOP_DEFFUN and other prolog bytecodes that may clone
5389 * literal objects, we don't want to pay a script prolog execution
5390 * price for all regexp literals in a script (many may not be used
5391 * by a particular execution of that script, depending on control
5392 * flow), so we initialize lazily here.
5394 * XXX This code is specific to regular expression objects. If we
5395 * need a similar op for other kinds of object literals, we should
5396 * push cloning down under JSObjectOps and reuse code here.
5398 index = GET_FULL_INDEX(0);
5399 JS_ASSERT(index < JS_SCRIPT_REGEXPS(script)->length);
5401 slot = index;
5402 if (fp->fun) {
5404 * We're in function code, not global or eval code (in eval
5405 * code, JSOP_REGEXP is never emitted). The cloned funobj
5406 * contains JS_SCRIPT_REGEXPS(script)->length reserved slots
5407 * for the cloned regexps; see fun_reserveSlots, jsfun.c.
5409 funobj = fp->callee;
5410 slot += JSCLASS_RESERVED_SLOTS(&js_FunctionClass);
5411 if (script->upvarsOffset != 0)
5412 slot += JS_SCRIPT_UPVARS(script)->length;
5413 if (!JS_GetReservedSlot(cx, funobj, slot, &rval))
5414 goto error;
5415 if (JSVAL_IS_VOID(rval))
5416 rval = JSVAL_NULL;
5417 } else {
5419 * We're in global code. The code generator reserved a slot
5420 * for the regexp among script->nfixed slots. All such slots
5421 * are initialized to null, not void, for faster testing in
5422 * JSOP_*GVAR cases. To simplify index calculations we count
5423 * regexps in the reverse order down from script->nslots - 1.
5425 JS_ASSERT(slot < script->nfixed);
5426 slot = script->nfixed - slot - 1;
5427 rval = fp->slots[slot];
5428 #ifdef __GNUC__
5429 funobj = NULL; /* suppress bogus gcc warnings */
5430 #endif
5433 if (JSVAL_IS_NULL(rval)) {
5434 /* Compute the current global object in obj2. */
5435 obj2 = fp->scopeChain;
5436 while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL)
5437 obj2 = parent;
5440 * If obj's parent is not obj2, we must clone obj so that it
5441 * has the right parent, and therefore, the right prototype.
5443 * Yes, this means we assume that the correct RegExp.prototype
5444 * to which regexp instances (including literals) delegate can
5445 * be distinguished solely by the instance's parent, which was
5446 * set to the parent of the RegExp constructor function object
5447 * when the instance was created. In other words,
5449 * (/x/.__parent__ == RegExp.__parent__) implies
5450 * (/x/.__proto__ == RegExp.prototype)
5452 * (unless you assign a different object to RegExp.prototype
5453 * at runtime, in which case, ECMA doesn't specify operation,
5454 * and you get what you deserve).
5456 * This same coupling between instance parent and constructor
5457 * parent turns up everywhere (see jsobj.c's FindClassObject,
5458 * js_ConstructObject, and js_NewObject). It's fundamental to
5459 * the design of the language when you consider multiple global
5460 * objects and separate compilation and execution, even though
5461 * it is not specified fully in ECMA.
5463 JS_GET_SCRIPT_REGEXP(script, index, obj);
5464 if (OBJ_GET_PARENT(cx, obj) != obj2) {
5465 obj = js_CloneRegExpObject(cx, obj, obj2);
5466 if (!obj)
5467 goto error;
5469 rval = OBJECT_TO_JSVAL(obj);
5471 /* Store the regexp object value in its cloneIndex slot. */
5472 if (fp->fun) {
5473 if (!JS_SetReservedSlot(cx, funobj, slot, rval))
5474 goto error;
5475 } else {
5476 fp->slots[slot] = rval;
5480 PUSH_OPND(rval);
5482 END_CASE(JSOP_REGEXP)
5484 BEGIN_CASE(JSOP_ZERO)
5485 PUSH_OPND(JSVAL_ZERO);
5486 END_CASE(JSOP_ZERO)
5488 BEGIN_CASE(JSOP_ONE)
5489 PUSH_OPND(JSVAL_ONE);
5490 END_CASE(JSOP_ONE)
5492 BEGIN_CASE(JSOP_NULL)
5493 PUSH_OPND(JSVAL_NULL);
5494 END_CASE(JSOP_NULL)
5496 BEGIN_CASE(JSOP_FALSE)
5497 PUSH_OPND(JSVAL_FALSE);
5498 END_CASE(JSOP_FALSE)
5500 BEGIN_CASE(JSOP_TRUE)
5501 PUSH_OPND(JSVAL_TRUE);
5502 END_CASE(JSOP_TRUE)
5504 BEGIN_CASE(JSOP_TABLESWITCH)
5505 pc2 = regs.pc;
5506 len = GET_JUMP_OFFSET(pc2);
5509 * ECMAv2+ forbids conversion of discriminant, so we will skip to
5510 * the default case if the discriminant isn't already an int jsval.
5511 * (This opcode is emitted only for dense jsint-domain switches.)
5513 rval = POP_OPND();
5514 if (JSVAL_IS_INT(rval)) {
5515 i = JSVAL_TO_INT(rval);
5516 } else if (JSVAL_IS_DOUBLE(rval) && *JSVAL_TO_DOUBLE(rval) == 0) {
5517 /* Treat -0 (double) as 0. */
5518 i = 0;
5519 } else {
5520 DO_NEXT_OP(len);
5523 pc2 += JUMP_OFFSET_LEN;
5524 low = GET_JUMP_OFFSET(pc2);
5525 pc2 += JUMP_OFFSET_LEN;
5526 high = GET_JUMP_OFFSET(pc2);
5528 i -= low;
5529 if ((jsuint)i < (jsuint)(high - low + 1)) {
5530 pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i;
5531 off = (jsint) GET_JUMP_OFFSET(pc2);
5532 if (off)
5533 len = off;
5535 END_VARLEN_CASE
5537 BEGIN_CASE(JSOP_TABLESWITCHX)
5538 pc2 = regs.pc;
5539 len = GET_JUMPX_OFFSET(pc2);
5542 * ECMAv2+ forbids conversion of discriminant, so we will skip to
5543 * the default case if the discriminant isn't already an int jsval.
5544 * (This opcode is emitted only for dense jsint-domain switches.)
5546 rval = POP_OPND();
5547 if (JSVAL_IS_INT(rval)) {
5548 i = JSVAL_TO_INT(rval);
5549 } else if (JSVAL_IS_DOUBLE(rval) && *JSVAL_TO_DOUBLE(rval) == 0) {
5550 /* Treat -0 (double) as 0. */
5551 i = 0;
5552 } else {
5553 DO_NEXT_OP(len);
5556 pc2 += JUMPX_OFFSET_LEN;
5557 low = GET_JUMP_OFFSET(pc2);
5558 pc2 += JUMP_OFFSET_LEN;
5559 high = GET_JUMP_OFFSET(pc2);
5561 i -= low;
5562 if ((jsuint)i < (jsuint)(high - low + 1)) {
5563 pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i;
5564 off = (jsint) GET_JUMPX_OFFSET(pc2);
5565 if (off)
5566 len = off;
5568 END_VARLEN_CASE
5570 BEGIN_CASE(JSOP_LOOKUPSWITCHX)
5571 off = JUMPX_OFFSET_LEN;
5572 goto do_lookup_switch;
5574 BEGIN_CASE(JSOP_LOOKUPSWITCH)
5575 off = JUMP_OFFSET_LEN;
5577 do_lookup_switch:
5579 * JSOP_LOOKUPSWITCH and JSOP_LOOKUPSWITCHX are never used if
5580 * any atom index in it would exceed 64K limit.
5582 JS_ASSERT(atoms == script->atomMap.vector);
5583 pc2 = regs.pc;
5584 lval = POP_OPND();
5586 if (!JSVAL_IS_NUMBER(lval) &&
5587 !JSVAL_IS_STRING(lval) &&
5588 !JSVAL_IS_BOOLEAN(lval)) {
5589 goto end_lookup_switch;
5592 pc2 += off;
5593 npairs = (jsint) GET_UINT16(pc2);
5594 pc2 += UINT16_LEN;
5595 JS_ASSERT(npairs); /* empty switch uses JSOP_TABLESWITCH */
5597 #define SEARCH_PAIRS(MATCH_CODE) \
5598 for (;;) { \
5599 JS_ASSERT(GET_INDEX(pc2) < script->atomMap.length); \
5600 atom = atoms[GET_INDEX(pc2)]; \
5601 rval = ATOM_KEY(atom); \
5602 MATCH_CODE \
5603 pc2 += INDEX_LEN; \
5604 if (match) \
5605 break; \
5606 pc2 += off; \
5607 if (--npairs == 0) { \
5608 pc2 = regs.pc; \
5609 break; \
5612 if (JSVAL_IS_STRING(lval)) {
5613 str = JSVAL_TO_STRING(lval);
5614 SEARCH_PAIRS(
5615 match = (JSVAL_IS_STRING(rval) &&
5616 ((str2 = JSVAL_TO_STRING(rval)) == str ||
5617 js_EqualStrings(str2, str)));
5619 } else if (JSVAL_IS_DOUBLE(lval)) {
5620 d = *JSVAL_TO_DOUBLE(lval);
5621 SEARCH_PAIRS(
5622 match = (JSVAL_IS_DOUBLE(rval) &&
5623 *JSVAL_TO_DOUBLE(rval) == d);
5625 } else {
5626 SEARCH_PAIRS(
5627 match = (lval == rval);
5630 #undef SEARCH_PAIRS
5632 end_lookup_switch:
5633 len = (op == JSOP_LOOKUPSWITCH)
5634 ? GET_JUMP_OFFSET(pc2)
5635 : GET_JUMPX_OFFSET(pc2);
5636 END_VARLEN_CASE
5638 BEGIN_CASE(JSOP_TRAP)
5640 JSTrapStatus status;
5642 status = JS_HandleTrap(cx, script, regs.pc, &rval);
5643 switch (status) {
5644 case JSTRAP_ERROR:
5645 goto error;
5646 case JSTRAP_RETURN:
5647 fp->rval = rval;
5648 ok = JS_TRUE;
5649 goto forced_return;
5650 case JSTRAP_THROW:
5651 cx->throwing = JS_TRUE;
5652 cx->exception = rval;
5653 goto error;
5654 default:;
5655 break;
5657 JS_ASSERT(status == JSTRAP_CONTINUE);
5658 CHECK_INTERRUPT_HANDLER();
5659 JS_ASSERT(JSVAL_IS_INT(rval));
5660 op = (JSOp) JSVAL_TO_INT(rval);
5661 JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
5662 DO_OP();
5665 BEGIN_CASE(JSOP_ARGUMENTS)
5666 if (!js_GetArgsValue(cx, fp, &rval))
5667 goto error;
5668 PUSH_OPND(rval);
5669 END_CASE(JSOP_ARGUMENTS)
5671 BEGIN_CASE(JSOP_ARGSUB)
5672 id = INT_TO_JSID(GET_ARGNO(regs.pc));
5673 if (!js_GetArgsProperty(cx, fp, id, &rval))
5674 goto error;
5675 PUSH_OPND(rval);
5676 END_CASE(JSOP_ARGSUB)
5678 BEGIN_CASE(JSOP_ARGCNT)
5679 id = ATOM_TO_JSID(rt->atomState.lengthAtom);
5680 if (!js_GetArgsProperty(cx, fp, id, &rval))
5681 goto error;
5682 PUSH_OPND(rval);
5683 END_CASE(JSOP_ARGCNT)
5685 BEGIN_CASE(JSOP_GETARG)
5686 BEGIN_CASE(JSOP_CALLARG)
5687 slot = GET_ARGNO(regs.pc);
5688 JS_ASSERT(slot < fp->fun->nargs);
5689 METER_SLOT_OP(op, slot);
5690 PUSH_OPND(fp->argv[slot]);
5691 if (op == JSOP_CALLARG)
5692 PUSH_OPND(JSVAL_NULL);
5693 END_CASE(JSOP_GETARG)
5695 BEGIN_CASE(JSOP_SETARG)
5696 slot = GET_ARGNO(regs.pc);
5697 JS_ASSERT(slot < fp->fun->nargs);
5698 METER_SLOT_OP(op, slot);
5699 vp = &fp->argv[slot];
5700 *vp = FETCH_OPND(-1);
5701 END_SET_CASE(JSOP_SETARG)
5703 BEGIN_CASE(JSOP_GETLOCAL)
5704 slot = GET_SLOTNO(regs.pc);
5705 JS_ASSERT(slot < script->nslots);
5706 PUSH_OPND(fp->slots[slot]);
5707 END_CASE(JSOP_GETLOCAL)
5709 BEGIN_CASE(JSOP_CALLLOCAL)
5710 slot = GET_SLOTNO(regs.pc);
5711 JS_ASSERT(slot < script->nslots);
5712 PUSH_OPND(fp->slots[slot]);
5713 PUSH_OPND(JSVAL_NULL);
5714 END_CASE(JSOP_CALLLOCAL)
5716 BEGIN_CASE(JSOP_SETLOCAL)
5717 slot = GET_SLOTNO(regs.pc);
5718 JS_ASSERT(slot < script->nslots);
5719 vp = &fp->slots[slot];
5720 *vp = FETCH_OPND(-1);
5721 END_SET_CASE(JSOP_SETLOCAL)
5723 BEGIN_CASE(JSOP_GETUPVAR)
5724 BEGIN_CASE(JSOP_CALLUPVAR)
5726 JSUpvarArray *uva = JS_SCRIPT_UPVARS(script);
5728 index = GET_UINT16(regs.pc);
5729 JS_ASSERT(index < uva->length);
5731 rval = js_GetUpvar(cx, script->staticLevel, uva->vector[index]);
5732 PUSH_OPND(rval);
5734 if (op == JSOP_CALLUPVAR)
5735 PUSH_OPND(JSVAL_NULL);
5737 END_CASE(JSOP_GETUPVAR)
5739 BEGIN_CASE(JSOP_GETUPVAR_DBG)
5740 BEGIN_CASE(JSOP_CALLUPVAR_DBG)
5741 fun = fp->fun;
5742 JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
5743 JS_ASSERT(fun->u.i.wrapper);
5745 /* Scope for tempPool mark and local names allocation in it. */
5747 void *mark = JS_ARENA_MARK(&cx->tempPool);
5748 jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
5749 if (!names)
5750 goto error;
5752 index = fun->countArgsAndVars() + GET_UINT16(regs.pc);
5753 atom = JS_LOCAL_NAME_TO_ATOM(names[index]);
5754 id = ATOM_TO_JSID(atom);
5756 ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
5757 JS_ARENA_RELEASE(&cx->tempPool, mark);
5758 if (!ok)
5759 goto error;
5762 if (!prop)
5763 goto atom_not_defined;
5765 /* Minimize footprint with generic code instead of NATIVE_GET. */
5766 OBJ_DROP_PROPERTY(cx, obj2, prop);
5767 vp = regs.sp;
5768 PUSH_OPND(JSVAL_NULL);
5769 if (!OBJ_GET_PROPERTY(cx, obj, id, vp))
5770 goto error;
5772 if (op == JSOP_CALLUPVAR_DBG)
5773 PUSH_OPND(JSVAL_NULL);
5774 END_CASE(JSOP_GETUPVAR_DBG)
5776 BEGIN_CASE(JSOP_GETDSLOT)
5777 BEGIN_CASE(JSOP_CALLDSLOT)
5778 obj = fp->callee;
5779 JS_ASSERT(obj);
5780 JS_ASSERT(obj->dslots);
5782 index = GET_UINT16(regs.pc);
5783 JS_ASSERT(JS_INITIAL_NSLOTS + index < jsatomid(obj->dslots[-1]));
5784 JS_ASSERT_IF(OBJ_SCOPE(obj)->object == obj,
5785 JS_INITIAL_NSLOTS + index < OBJ_SCOPE(obj)->freeslot);
5787 PUSH_OPND(obj->dslots[index]);
5788 if (op == JSOP_CALLDSLOT)
5789 PUSH_OPND(JSVAL_NULL);
5790 END_CASE(JSOP_GETDSLOT)
5792 BEGIN_CASE(JSOP_GETGVAR)
5793 BEGIN_CASE(JSOP_CALLGVAR)
5794 slot = GET_SLOTNO(regs.pc);
5795 JS_ASSERT(slot < GlobalVarCount(fp));
5796 METER_SLOT_OP(op, slot);
5797 lval = fp->slots[slot];
5798 if (JSVAL_IS_NULL(lval)) {
5799 op = (op == JSOP_GETGVAR) ? JSOP_NAME : JSOP_CALLNAME;
5800 DO_OP();
5802 obj = fp->varobj;
5803 slot = JSVAL_TO_INT(lval);
5804 rval = OBJ_GET_SLOT(cx, obj, slot);
5805 PUSH_OPND(rval);
5806 if (op == JSOP_CALLGVAR)
5807 PUSH_OPND(OBJECT_TO_JSVAL(obj));
5808 END_CASE(JSOP_GETGVAR)
5810 BEGIN_CASE(JSOP_SETGVAR)
5811 slot = GET_SLOTNO(regs.pc);
5812 JS_ASSERT(slot < GlobalVarCount(fp));
5813 METER_SLOT_OP(op, slot);
5814 rval = FETCH_OPND(-1);
5815 obj = fp->varobj;
5816 lval = fp->slots[slot];
5817 if (JSVAL_IS_NULL(lval)) {
5819 * Inline-clone and deoptimize JSOP_SETNAME code here because
5820 * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval]
5821 * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME.
5823 #ifdef JS_TRACER
5824 if (TRACE_RECORDER(cx))
5825 js_AbortRecording(cx, "SETGVAR with NULL slot");
5826 #endif
5827 LOAD_ATOM(0);
5828 id = ATOM_TO_JSID(atom);
5829 if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
5830 goto error;
5831 } else {
5832 slot = JSVAL_TO_INT(lval);
5833 JS_LOCK_OBJ(cx, obj);
5834 LOCKED_OBJ_WRITE_SLOT(cx, obj, slot, rval);
5835 JS_UNLOCK_OBJ(cx, obj);
5837 END_SET_CASE(JSOP_SETGVAR)
5839 BEGIN_CASE(JSOP_DEFCONST)
5840 BEGIN_CASE(JSOP_DEFVAR)
5841 index = GET_INDEX(regs.pc);
5842 atom = atoms[index];
5845 * index is relative to atoms at this point but for global var
5846 * code below we need the absolute value.
5848 index += atoms - script->atomMap.vector;
5849 obj = fp->varobj;
5850 attrs = JSPROP_ENUMERATE;
5851 if (!(fp->flags & JSFRAME_EVAL))
5852 attrs |= JSPROP_PERMANENT;
5853 if (op == JSOP_DEFCONST)
5854 attrs |= JSPROP_READONLY;
5856 /* Lookup id in order to check for redeclaration problems. */
5857 id = ATOM_TO_JSID(atom);
5858 prop = NULL;
5859 if (!js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop))
5860 goto error;
5862 /* Bind a variable only if it's not yet defined. */
5863 if (!prop) {
5864 if (!OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
5865 JS_PropertyStub, JS_PropertyStub,
5866 attrs, &prop)) {
5867 goto error;
5869 JS_ASSERT(prop);
5870 obj2 = obj;
5874 * Try to optimize a property we either just created, or found
5875 * directly in the global object, that is permanent, has a slot,
5876 * and has stub getter and setter, into a "fast global" accessed
5877 * by the JSOP_*GVAR opcodes.
5879 if (!fp->fun &&
5880 index < GlobalVarCount(fp) &&
5881 obj2 == obj &&
5882 OBJ_IS_NATIVE(obj)) {
5883 sprop = (JSScopeProperty *) prop;
5884 if ((sprop->attrs & JSPROP_PERMANENT) &&
5885 SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) &&
5886 SPROP_HAS_STUB_GETTER(sprop) &&
5887 SPROP_HAS_STUB_SETTER(sprop)) {
5889 * Fast globals use frame variables to map the global
5890 * name's atom index to the permanent fp->varobj slot
5891 * number, tagged as a jsval. The atom index for the
5892 * global's name literal is identical to its variable
5893 * index.
5895 fp->slots[index] = INT_TO_JSVAL(sprop->slot);
5899 OBJ_DROP_PROPERTY(cx, obj2, prop);
5900 END_CASE(JSOP_DEFVAR)
5902 BEGIN_CASE(JSOP_DEFFUN)
5904 JSPropertyOp getter, setter;
5905 bool doSet;
5906 JSObject *pobj;
5907 JSProperty *prop;
5908 uint32 old;
5911 * A top-level function defined in Global or Eval code (see
5912 * ECMA-262 Ed. 3), or else a SpiderMonkey extension: a named
5913 * function statement in a compound statement (not at the top
5914 * statement level of global code, or at the top level of a
5915 * function body).
5917 LOAD_FUNCTION(0);
5918 obj = FUN_OBJECT(fun);
5920 if (FUN_NULL_CLOSURE(fun)) {
5922 * Even a null closure needs a parent for principals finding.
5923 * FIXME: bug 476950, although debugger users may also demand
5924 * some kind of scope link for debugger-assisted eval-in-frame.
5926 obj2 = fp->scopeChain;
5927 } else {
5928 JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
5931 * Inline js_GetScopeChain a bit to optimize for the case of a
5932 * top-level function.
5934 if (!fp->blockChain) {
5935 obj2 = fp->scopeChain;
5936 } else {
5937 obj2 = js_GetScopeChain(cx, fp);
5938 if (!obj2)
5939 goto error;
5944 * If static link is not current scope, clone fun's object to link
5945 * to the current scope via parent. We do this to enable sharing of
5946 * compiled functions among multiple equivalent scopes, amortizing
5947 * the cost of compilation over a number of executions. Examples
5948 * include XUL scripts and event handlers shared among Firefox or
5949 * other Mozilla app chrome windows, and user-defined JS functions
5950 * precompiled and then shared among requests in server-side JS.
5952 if (OBJ_GET_PARENT(cx, obj) != obj2) {
5953 obj = js_CloneFunctionObject(cx, fun, obj2);
5954 if (!obj)
5955 goto error;
5959 * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All
5960 * paths from here must flow through the "Restore fp->scopeChain"
5961 * code below the OBJ_DEFINE_PROPERTY call.
5963 MUST_FLOW_THROUGH("restore_scope");
5964 fp->scopeChain = obj;
5966 rval = OBJECT_TO_JSVAL(obj);
5969 * ECMA requires functions defined when entering Eval code to be
5970 * impermanent.
5972 attrs = (fp->flags & JSFRAME_EVAL)
5973 ? JSPROP_ENUMERATE
5974 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
5977 * Load function flags that are also property attributes. Getters
5978 * and setters do not need a slot, their value is stored elsewhere
5979 * in the property itself, not in obj slots.
5981 setter = getter = JS_PropertyStub;
5982 flags = JSFUN_GSFLAG2ATTR(fun->flags);
5983 if (flags) {
5984 /* Function cannot be both getter a setter. */
5985 JS_ASSERT(flags == JSPROP_GETTER || flags == JSPROP_SETTER);
5986 attrs |= flags | JSPROP_SHARED;
5987 rval = JSVAL_VOID;
5988 if (flags == JSPROP_GETTER)
5989 getter = js_CastAsPropertyOp(obj);
5990 else
5991 setter = js_CastAsPropertyOp(obj);
5995 * We define the function as a property of the variable object and
5996 * not the current scope chain even for the case of function
5997 * expression statements and functions defined by eval inside let
5998 * or with blocks.
6000 parent = fp->varobj;
6001 JS_ASSERT(parent);
6004 * Check for a const property of the same name -- or any kind
6005 * of property if executing with the strict option. We check
6006 * here at runtime as well as at compile-time, to handle eval
6007 * as well as multiple HTML script tags.
6009 id = ATOM_TO_JSID(fun->atom);
6010 prop = NULL;
6011 ok = js_CheckRedeclaration(cx, parent, id, attrs, &pobj, &prop);
6012 if (!ok)
6013 goto restore_scope;
6016 * We deviate from 10.1.2 in ECMA 262 v3 and under eval use for
6017 * function declarations OBJ_SET_PROPERTY, not OBJ_DEFINE_PROPERTY,
6018 * to preserve the JSOP_PERMANENT attribute of existing properties
6019 * and make sure that such properties cannot be deleted.
6021 * We also use OBJ_SET_PROPERTY for the existing properties of
6022 * Call objects with matching attributes to preserve the native
6023 * getters and setters that store the value of the property in the
6024 * interpreter frame, see bug 467495.
6026 doSet = (attrs == JSPROP_ENUMERATE);
6027 JS_ASSERT_IF(doSet, fp->flags & JSFRAME_EVAL);
6028 if (prop) {
6029 if (parent == pobj &&
6030 OBJ_GET_CLASS(cx, parent) == &js_CallClass &&
6031 (old = ((JSScopeProperty *) prop)->attrs,
6032 !(old & (JSPROP_GETTER|JSPROP_SETTER)) &&
6033 (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs)) {
6035 * js_CheckRedeclaration must reject attempts to add a
6036 * getter or setter to an existing property without a
6037 * getter or setter.
6039 JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT)));
6040 JS_ASSERT(!(old & JSPROP_READONLY));
6041 doSet = JS_TRUE;
6043 OBJ_DROP_PROPERTY(cx, pobj, prop);
6045 ok = doSet
6046 ? OBJ_SET_PROPERTY(cx, parent, id, &rval)
6047 : OBJ_DEFINE_PROPERTY(cx, parent, id, rval, getter, setter,
6048 attrs, NULL);
6050 restore_scope:
6051 /* Restore fp->scopeChain now that obj is defined in fp->varobj. */
6052 fp->scopeChain = obj2;
6053 if (!ok) {
6054 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
6055 goto error;
6058 END_CASE(JSOP_DEFFUN)
6060 BEGIN_CASE(JSOP_DEFFUN_FC)
6061 BEGIN_CASE(JSOP_DEFFUN_DBGFC)
6062 LOAD_FUNCTION(0);
6064 obj = (op == JSOP_DEFFUN_FC)
6065 ? js_NewFlatClosure(cx, fun)
6066 : js_NewDebuggableFlatClosure(cx, fun);
6067 if (!obj)
6068 goto error;
6069 rval = OBJECT_TO_JSVAL(obj);
6071 attrs = (fp->flags & JSFRAME_EVAL)
6072 ? JSPROP_ENUMERATE
6073 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
6075 flags = JSFUN_GSFLAG2ATTR(fun->flags);
6076 if (flags) {
6077 attrs |= flags | JSPROP_SHARED;
6078 rval = JSVAL_VOID;
6081 parent = fp->varobj;
6082 JS_ASSERT(parent);
6084 id = ATOM_TO_JSID(fun->atom);
6085 ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL);
6086 if (ok) {
6087 if (attrs == JSPROP_ENUMERATE) {
6088 JS_ASSERT(fp->flags & JSFRAME_EVAL);
6089 ok = OBJ_SET_PROPERTY(cx, parent, id, &rval);
6090 } else {
6091 JS_ASSERT(attrs & JSPROP_PERMANENT);
6093 ok = OBJ_DEFINE_PROPERTY(cx, parent, id, rval,
6094 (flags & JSPROP_GETTER)
6095 ? JS_EXTENSION (JSPropertyOp) obj
6096 : JS_PropertyStub,
6097 (flags & JSPROP_SETTER)
6098 ? JS_EXTENSION (JSPropertyOp) obj
6099 : JS_PropertyStub,
6100 attrs,
6101 NULL);
6105 if (!ok) {
6106 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
6107 goto error;
6109 END_CASE(JSOP_DEFFUN_FC)
6111 BEGIN_CASE(JSOP_DEFLOCALFUN)
6113 * Define a local function (i.e., one nested at the top level of
6114 * another function), parented by the current scope chain, stored
6115 * in a local variable slot that the compiler allocated. This is
6116 * an optimization over JSOP_DEFFUN that avoids requiring a call
6117 * object for the outer function's activation.
6119 LOAD_FUNCTION(SLOTNO_LEN);
6120 JS_ASSERT(FUN_INTERPRETED(fun));
6121 JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
6122 obj = FUN_OBJECT(fun);
6124 if (FUN_NULL_CLOSURE(fun)) {
6125 obj = js_CloneFunctionObject(cx, fun, fp->scopeChain);
6126 if (!obj)
6127 goto error;
6128 } else {
6129 parent = js_GetScopeChain(cx, fp);
6130 if (!parent)
6131 goto error;
6133 if (OBJ_GET_PARENT(cx, obj) != parent) {
6134 #ifdef JS_TRACER
6135 if (TRACE_RECORDER(cx))
6136 js_AbortRecording(cx, "DEFLOCALFUN for closure");
6137 #endif
6138 obj = js_CloneFunctionObject(cx, fun, parent);
6139 if (!obj)
6140 goto error;
6144 slot = GET_SLOTNO(regs.pc);
6145 TRACE_2(DefLocalFunSetSlot, slot, obj);
6147 fp->slots[slot] = OBJECT_TO_JSVAL(obj);
6148 END_CASE(JSOP_DEFLOCALFUN)
6150 BEGIN_CASE(JSOP_DEFLOCALFUN_FC)
6151 LOAD_FUNCTION(SLOTNO_LEN);
6153 obj = js_NewFlatClosure(cx, fun);
6154 if (!obj)
6155 goto error;
6157 slot = GET_SLOTNO(regs.pc);
6158 TRACE_2(DefLocalFunSetSlot, slot, obj);
6160 fp->slots[slot] = OBJECT_TO_JSVAL(obj);
6161 END_CASE(JSOP_DEFLOCALFUN_FC)
6163 BEGIN_CASE(JSOP_DEFLOCALFUN_DBGFC)
6164 LOAD_FUNCTION(SLOTNO_LEN);
6166 obj = js_NewDebuggableFlatClosure(cx, fun);
6167 if (!obj)
6168 goto error;
6170 slot = GET_SLOTNO(regs.pc);
6171 fp->slots[slot] = OBJECT_TO_JSVAL(obj);
6172 END_CASE(JSOP_DEFLOCALFUN_DBGFC)
6174 BEGIN_CASE(JSOP_LAMBDA)
6175 /* Load the specified function object literal. */
6176 LOAD_FUNCTION(0);
6177 obj = FUN_OBJECT(fun);
6179 if (FUN_NULL_CLOSURE(fun)) {
6180 obj = js_CloneFunctionObject(cx, fun, fp->scopeChain);
6181 if (!obj)
6182 goto error;
6183 } else {
6184 parent = js_GetScopeChain(cx, fp);
6185 if (!parent)
6186 goto error;
6189 * FIXME: bug 471214, Cloning here even when the compiler saw
6190 * the right parent is wasteful but we don't fully support
6191 * joined function objects, yet.
6193 obj = js_CloneFunctionObject(cx, fun, parent);
6194 if (!obj)
6195 goto error;
6198 PUSH_OPND(OBJECT_TO_JSVAL(obj));
6199 END_CASE(JSOP_LAMBDA)
6201 BEGIN_CASE(JSOP_LAMBDA_FC)
6202 LOAD_FUNCTION(0);
6204 obj = js_NewFlatClosure(cx, fun);
6205 if (!obj)
6206 goto error;
6208 PUSH_OPND(OBJECT_TO_JSVAL(obj));
6209 END_CASE(JSOP_LAMBDA_FC)
6211 BEGIN_CASE(JSOP_LAMBDA_DBGFC)
6212 LOAD_FUNCTION(0);
6214 obj = js_NewDebuggableFlatClosure(cx, fun);
6215 if (!obj)
6216 goto error;
6218 PUSH_OPND(OBJECT_TO_JSVAL(obj));
6219 END_CASE(JSOP_LAMBDA_DBGFC)
6221 BEGIN_CASE(JSOP_CALLEE)
6222 PUSH_OPND(OBJECT_TO_JSVAL(fp->callee));
6223 END_CASE(JSOP_CALLEE)
6225 #if JS_HAS_GETTER_SETTER
6226 BEGIN_CASE(JSOP_GETTER)
6227 BEGIN_CASE(JSOP_SETTER)
6228 do_getter_setter:
6229 op2 = (JSOp) *++regs.pc;
6230 switch (op2) {
6231 case JSOP_INDEXBASE:
6232 atoms += GET_INDEXBASE(regs.pc);
6233 regs.pc += JSOP_INDEXBASE_LENGTH - 1;
6234 goto do_getter_setter;
6235 case JSOP_INDEXBASE1:
6236 case JSOP_INDEXBASE2:
6237 case JSOP_INDEXBASE3:
6238 atoms += (op2 - JSOP_INDEXBASE1 + 1) << 16;
6239 goto do_getter_setter;
6241 case JSOP_SETNAME:
6242 case JSOP_SETPROP:
6243 LOAD_ATOM(0);
6244 id = ATOM_TO_JSID(atom);
6245 rval = FETCH_OPND(-1);
6246 i = -1;
6247 goto gs_pop_lval;
6249 case JSOP_SETELEM:
6250 rval = FETCH_OPND(-1);
6251 id = 0;
6252 i = -2;
6253 gs_pop_lval:
6254 FETCH_OBJECT(cx, i - 1, lval, obj);
6255 break;
6257 case JSOP_INITPROP:
6258 JS_ASSERT(regs.sp - StackBase(fp) >= 2);
6259 rval = FETCH_OPND(-1);
6260 i = -1;
6261 LOAD_ATOM(0);
6262 id = ATOM_TO_JSID(atom);
6263 goto gs_get_lval;
6265 default:
6266 JS_ASSERT(op2 == JSOP_INITELEM);
6268 JS_ASSERT(regs.sp - StackBase(fp) >= 3);
6269 rval = FETCH_OPND(-1);
6270 id = 0;
6271 i = -2;
6272 gs_get_lval:
6273 lval = FETCH_OPND(i-1);
6274 JS_ASSERT(JSVAL_IS_OBJECT(lval));
6275 obj = JSVAL_TO_OBJECT(lval);
6276 break;
6279 /* Ensure that id has a type suitable for use with obj. */
6280 if (id == 0)
6281 FETCH_ELEMENT_ID(obj, i, id);
6283 if (JS_TypeOfValue(cx, rval) != JSTYPE_FUNCTION) {
6284 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6285 JSMSG_BAD_GETTER_OR_SETTER,
6286 (op == JSOP_GETTER)
6287 ? js_getter_str
6288 : js_setter_str);
6289 goto error;
6293 * Getters and setters are just like watchpoints from an access
6294 * control point of view.
6296 if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &rtmp, &attrs))
6297 goto error;
6299 if (op == JSOP_GETTER) {
6300 getter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval);
6301 setter = JS_PropertyStub;
6302 attrs = JSPROP_GETTER;
6303 } else {
6304 getter = JS_PropertyStub;
6305 setter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval);
6306 attrs = JSPROP_SETTER;
6308 attrs |= JSPROP_ENUMERATE | JSPROP_SHARED;
6310 /* Check for a readonly or permanent property of the same name. */
6311 if (!js_CheckRedeclaration(cx, obj, id, attrs, NULL, NULL))
6312 goto error;
6314 if (!OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, getter, setter,
6315 attrs, NULL)) {
6316 goto error;
6319 regs.sp += i;
6320 if (js_CodeSpec[op2].ndefs)
6321 STORE_OPND(-1, rval);
6322 len = js_CodeSpec[op2].length;
6323 DO_NEXT_OP(len);
6324 #endif /* JS_HAS_GETTER_SETTER */
6326 BEGIN_CASE(JSOP_HOLE)
6327 PUSH_OPND(JSVAL_HOLE);
6328 END_CASE(JSOP_HOLE)
6330 BEGIN_CASE(JSOP_NEWARRAY)
6331 len = GET_UINT16(regs.pc);
6332 cx->fp->assertValidStackDepth(len);
6333 obj = js_NewArrayObject(cx, len, regs.sp - len, JS_TRUE);
6334 if (!obj)
6335 goto error;
6336 regs.sp -= len - 1;
6337 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
6338 END_CASE(JSOP_NEWARRAY)
6340 BEGIN_CASE(JSOP_NEWINIT)
6341 i = GET_INT8(regs.pc);
6342 JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
6343 obj = (i == JSProto_Array)
6344 ? js_NewArrayObject(cx, 0, NULL)
6345 : js_NewObject(cx, &js_ObjectClass, NULL, NULL);
6346 if (!obj)
6347 goto error;
6348 PUSH_OPND(OBJECT_TO_JSVAL(obj));
6349 fp->sharpDepth++;
6350 CHECK_INTERRUPT_HANDLER();
6351 END_CASE(JSOP_NEWINIT)
6353 BEGIN_CASE(JSOP_ENDINIT)
6354 if (--fp->sharpDepth == 0)
6355 fp->sharpArray = NULL;
6357 /* Re-set the newborn root to the top of this object tree. */
6358 JS_ASSERT(regs.sp - StackBase(fp) >= 1);
6359 lval = FETCH_OPND(-1);
6360 JS_ASSERT(JSVAL_IS_OBJECT(lval));
6361 cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(lval);
6362 END_CASE(JSOP_ENDINIT)
6364 BEGIN_CASE(JSOP_INITPROP)
6365 /* Load the property's initial value into rval. */
6366 JS_ASSERT(regs.sp - StackBase(fp) >= 2);
6367 rval = FETCH_OPND(-1);
6369 /* Load the object being initialized into lval/obj. */
6370 lval = FETCH_OPND(-2);
6371 obj = JSVAL_TO_OBJECT(lval);
6372 JS_ASSERT(OBJ_IS_NATIVE(obj));
6373 JS_ASSERT(!OBJ_GET_CLASS(cx, obj)->reserveSlots);
6374 JS_ASSERT(!(LOCKED_OBJ_GET_CLASS(obj)->flags &
6375 JSCLASS_SHARE_ALL_PROPERTIES));
6377 do {
6378 JSScope *scope;
6379 uint32 kshape;
6380 JSPropertyCache *cache;
6381 JSPropCacheEntry *entry;
6383 JS_LOCK_OBJ(cx, obj);
6384 scope = OBJ_SCOPE(obj);
6385 JS_ASSERT(!scope->sealed());
6386 kshape = scope->shape;
6387 cache = &JS_PROPERTY_CACHE(cx);
6388 entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)];
6389 PCMETER(cache->pctestentry = entry);
6390 PCMETER(cache->tests++);
6391 PCMETER(cache->initests++);
6393 if (entry->kpc == regs.pc &&
6394 entry->kshape == kshape &&
6395 PCVCAP_SHAPE(entry->vcap) == rt->protoHazardShape) {
6396 JS_ASSERT(PCVCAP_TAG(entry->vcap) == 0);
6398 PCMETER(cache->pchits++);
6399 PCMETER(cache->inipchits++);
6401 JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
6402 sprop = PCVAL_TO_SPROP(entry->vword);
6403 JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
6406 * If this property has a non-stub setter, it must be
6407 * __proto__, __parent__, or another "shared prototype"
6408 * built-in. Force a miss to save code size here and let
6409 * the standard code path take care of business.
6411 if (!SPROP_HAS_STUB_SETTER(sprop))
6412 goto do_initprop_miss;
6414 if (!scope->owned()) {
6415 scope = js_GetMutableScope(cx, obj);
6416 if (!scope) {
6417 JS_UNLOCK_OBJ(cx, obj);
6418 goto error;
6423 * Detect a repeated property name and force a miss to
6424 * share the strict warning code and cope with complexity
6425 * managed by JSScope::add.
6427 if (sprop->parent != scope->lastProp)
6428 goto do_initprop_miss;
6431 * Otherwise this entry must be for a direct property of
6432 * obj, not a proto-property, and there cannot have been
6433 * any deletions of prior properties.
6435 JS_ASSERT(!scope->hadMiddleDelete());
6436 JS_ASSERT_IF(scope->table, !scope->has(sprop));
6438 slot = sprop->slot;
6439 JS_ASSERT(slot == scope->freeslot);
6440 if (slot < STOBJ_NSLOTS(obj)) {
6441 ++scope->freeslot;
6442 } else {
6443 if (!js_AllocSlot(cx, obj, &slot)) {
6444 JS_UNLOCK_SCOPE(cx, scope);
6445 goto error;
6447 JS_ASSERT(slot == sprop->slot);
6450 JS_ASSERT(!scope->lastProp ||
6451 scope->shape == scope->lastProp->shape);
6452 if (scope->table) {
6453 JSScopeProperty *sprop2 =
6454 scope->add(cx, sprop->id,
6455 sprop->getter, sprop->setter,
6456 slot, sprop->attrs,
6457 sprop->flags, sprop->shortid);
6458 if (!sprop2) {
6459 js_FreeSlot(cx, obj, slot);
6460 JS_UNLOCK_SCOPE(cx, scope);
6461 goto error;
6463 JS_ASSERT(sprop2 == sprop);
6464 } else {
6465 JS_ASSERT(scope->owned());
6466 js_LeaveTraceIfGlobalObject(cx, obj);
6467 scope->shape = sprop->shape;
6468 ++scope->entryCount;
6469 scope->lastProp = sprop;
6472 LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, rval);
6473 TRACE_2(SetPropHit, entry, sprop);
6474 LOCKED_OBJ_SET_SLOT(obj, slot, rval);
6475 JS_UNLOCK_SCOPE(cx, scope);
6476 break;
6479 do_initprop_miss:
6480 PCMETER(cache->inipcmisses++);
6481 JS_UNLOCK_SCOPE(cx, scope);
6483 /* Get the immediate property name into id. */
6484 LOAD_ATOM(0);
6485 id = ATOM_TO_JSID(atom);
6487 /* Set the property named by obj[id] to rval. */
6488 if (!js_CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER,
6489 NULL, NULL)) {
6490 goto error;
6493 if (!(JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom)
6494 ? js_SetPropertyHelper(cx, obj, id, true, &rval)
6495 : js_DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
6496 JSPROP_ENUMERATE, 0, 0, NULL,
6497 JSDNP_CACHE_RESULT)))
6498 goto error;
6499 } while (0);
6501 /* Common tail for property cache hit and miss cases. */
6502 regs.sp--;
6503 END_CASE(JSOP_INITPROP);
6505 BEGIN_CASE(JSOP_INITELEM)
6506 /* Pop the element's value into rval. */
6507 JS_ASSERT(regs.sp - StackBase(fp) >= 3);
6508 rval = FETCH_OPND(-1);
6510 /* Find the object being initialized at top of stack. */
6511 lval = FETCH_OPND(-3);
6512 JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval));
6513 obj = JSVAL_TO_OBJECT(lval);
6515 /* Fetch id now that we have obj. */
6516 FETCH_ELEMENT_ID(obj, -2, id);
6519 * Check for property redeclaration strict warning (we may be in
6520 * an object initialiser, not an array initialiser).
6522 if (!js_CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER, NULL, NULL))
6523 goto error;
6526 * If rval is a hole, do not call OBJ_DEFINE_PROPERTY. In this case,
6527 * obj must be an array, so if the current op is the last element
6528 * initialiser, set the array length to one greater than id.
6530 if (rval == JSVAL_HOLE) {
6531 JS_ASSERT(OBJ_IS_ARRAY(cx, obj));
6532 JS_ASSERT(JSID_IS_INT(id));
6533 JS_ASSERT((jsuint) JSID_TO_INT(id) < ARRAY_INIT_LIMIT);
6534 if (js_GetOpcode(cx, script, regs.pc + JSOP_INITELEM_LENGTH) == JSOP_ENDINIT &&
6535 !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1))) {
6536 goto error;
6538 } else {
6539 if (!OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL, JSPROP_ENUMERATE, NULL))
6540 goto error;
6542 regs.sp -= 2;
6543 END_CASE(JSOP_INITELEM)
6545 #if JS_HAS_SHARP_VARS
6546 BEGIN_CASE(JSOP_DEFSHARP)
6547 obj = fp->sharpArray;
6548 if (!obj) {
6549 obj = js_NewArrayObject(cx, 0, NULL);
6550 if (!obj)
6551 goto error;
6552 fp->sharpArray = obj;
6554 i = (jsint) GET_UINT16(regs.pc);
6555 id = INT_TO_JSID(i);
6556 rval = FETCH_OPND(-1);
6557 if (JSVAL_IS_PRIMITIVE(rval)) {
6558 char numBuf[12];
6559 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
6560 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6561 JSMSG_BAD_SHARP_DEF, numBuf);
6562 goto error;
6564 if (!OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL, JSPROP_ENUMERATE, NULL))
6565 goto error;
6566 END_CASE(JSOP_DEFSHARP)
6568 BEGIN_CASE(JSOP_USESHARP)
6569 i = (jsint) GET_UINT16(regs.pc);
6570 id = INT_TO_JSID(i);
6571 obj = fp->sharpArray;
6572 if (!obj) {
6573 rval = JSVAL_VOID;
6574 } else {
6575 if (!OBJ_GET_PROPERTY(cx, obj, id, &rval))
6576 goto error;
6578 if (!JSVAL_IS_OBJECT(rval)) {
6579 char numBuf[12];
6581 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
6582 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6583 JSMSG_BAD_SHARP_USE, numBuf);
6584 goto error;
6586 PUSH_OPND(rval);
6587 END_CASE(JSOP_USESHARP)
6588 #endif /* JS_HAS_SHARP_VARS */
6590 BEGIN_CASE(JSOP_GOSUB)
6591 PUSH(JSVAL_FALSE);
6592 i = (regs.pc - script->main) + JSOP_GOSUB_LENGTH;
6593 PUSH(INT_TO_JSVAL(i));
6594 len = GET_JUMP_OFFSET(regs.pc);
6595 END_VARLEN_CASE
6597 BEGIN_CASE(JSOP_GOSUBX)
6598 PUSH(JSVAL_FALSE);
6599 i = (regs.pc - script->main) + JSOP_GOSUBX_LENGTH;
6600 len = GET_JUMPX_OFFSET(regs.pc);
6601 PUSH(INT_TO_JSVAL(i));
6602 END_VARLEN_CASE
6604 BEGIN_CASE(JSOP_RETSUB)
6605 /* Pop [exception or hole, retsub pc-index]. */
6606 rval = POP();
6607 lval = POP();
6608 JS_ASSERT(JSVAL_IS_BOOLEAN(lval));
6609 if (JSVAL_TO_BOOLEAN(lval)) {
6611 * Exception was pending during finally, throw it *before* we
6612 * adjust pc, because pc indexes into script->trynotes. This
6613 * turns out not to be necessary, but it seems clearer. And
6614 * it points out a FIXME: 350509, due to Igor Bukanov.
6616 cx->throwing = JS_TRUE;
6617 cx->exception = rval;
6618 goto error;
6620 JS_ASSERT(JSVAL_IS_INT(rval));
6621 len = JSVAL_TO_INT(rval);
6622 regs.pc = script->main;
6623 END_VARLEN_CASE
6625 BEGIN_CASE(JSOP_EXCEPTION)
6626 JS_ASSERT(cx->throwing);
6627 PUSH(cx->exception);
6628 cx->throwing = JS_FALSE;
6629 CHECK_BRANCH();
6630 END_CASE(JSOP_EXCEPTION)
6632 BEGIN_CASE(JSOP_FINALLY)
6633 CHECK_BRANCH();
6634 END_CASE(JSOP_FINALLY)
6636 BEGIN_CASE(JSOP_THROWING)
6637 JS_ASSERT(!cx->throwing);
6638 cx->throwing = JS_TRUE;
6639 cx->exception = POP_OPND();
6640 END_CASE(JSOP_THROWING)
6642 BEGIN_CASE(JSOP_THROW)
6643 JS_ASSERT(!cx->throwing);
6644 CHECK_BRANCH();
6645 cx->throwing = JS_TRUE;
6646 cx->exception = POP_OPND();
6647 /* let the code at error try to catch the exception. */
6648 goto error;
6650 BEGIN_CASE(JSOP_SETLOCALPOP)
6652 * The stack must have a block with at least one local slot below
6653 * the exception object.
6655 JS_ASSERT((size_t) (regs.sp - StackBase(fp)) >= 2);
6656 slot = GET_UINT16(regs.pc);
6657 JS_ASSERT(slot + 1 < script->nslots);
6658 fp->slots[slot] = POP_OPND();
6659 END_CASE(JSOP_SETLOCALPOP)
6661 BEGIN_CASE(JSOP_IFPRIMTOP)
6663 * If the top of stack is of primitive type, jump to our target.
6664 * Otherwise advance to the next opcode.
6666 JS_ASSERT(regs.sp > StackBase(fp));
6667 rval = FETCH_OPND(-1);
6668 if (JSVAL_IS_PRIMITIVE(rval)) {
6669 len = GET_JUMP_OFFSET(regs.pc);
6670 BRANCH(len);
6672 END_CASE(JSOP_IFPRIMTOP)
6674 BEGIN_CASE(JSOP_PRIMTOP)
6675 JS_ASSERT(regs.sp > StackBase(fp));
6676 lval = FETCH_OPND(-1);
6677 i = GET_INT8(regs.pc);
6678 if (!JSVAL_IS_PRIMITIVE(lval)) {
6679 lval = FETCH_OPND(-2);
6680 js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO,
6681 -2, lval, NULL,
6682 (i == JSTYPE_VOID)
6683 ? "primitive type"
6684 : JS_TYPE_STR(i));
6685 goto error;
6687 END_CASE(JSOP_PRIMTOP)
6689 BEGIN_CASE(JSOP_OBJTOP)
6690 lval = FETCH_OPND(-1);
6691 if (JSVAL_IS_PRIMITIVE(lval)) {
6692 js_ReportValueError(cx, GET_UINT16(regs.pc), -1, lval, NULL);
6693 goto error;
6695 END_CASE(JSOP_OBJTOP)
6697 BEGIN_CASE(JSOP_INSTANCEOF)
6698 rval = FETCH_OPND(-1);
6699 if (JSVAL_IS_PRIMITIVE(rval) ||
6700 !(obj = JSVAL_TO_OBJECT(rval))->map->ops->hasInstance) {
6701 js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
6702 -1, rval, NULL);
6703 goto error;
6705 lval = FETCH_OPND(-2);
6706 cond = JS_FALSE;
6707 if (!obj->map->ops->hasInstance(cx, obj, lval, &cond))
6708 goto error;
6709 regs.sp--;
6710 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));
6711 END_CASE(JSOP_INSTANCEOF)
6713 #if JS_HAS_DEBUGGER_KEYWORD
6714 BEGIN_CASE(JSOP_DEBUGGER)
6716 JSTrapHandler handler = cx->debugHooks->debuggerHandler;
6717 if (handler) {
6718 switch (handler(cx, script, regs.pc, &rval,
6719 cx->debugHooks->debuggerHandlerData)) {
6720 case JSTRAP_ERROR:
6721 goto error;
6722 case JSTRAP_CONTINUE:
6723 break;
6724 case JSTRAP_RETURN:
6725 fp->rval = rval;
6726 ok = JS_TRUE;
6727 goto forced_return;
6728 case JSTRAP_THROW:
6729 cx->throwing = JS_TRUE;
6730 cx->exception = rval;
6731 goto error;
6732 default:;
6734 CHECK_INTERRUPT_HANDLER();
6737 END_CASE(JSOP_DEBUGGER)
6738 #endif /* JS_HAS_DEBUGGER_KEYWORD */
6740 #if JS_HAS_XML_SUPPORT
6741 BEGIN_CASE(JSOP_DEFXMLNS)
6742 rval = POP();
6743 if (!js_SetDefaultXMLNamespace(cx, rval))
6744 goto error;
6745 END_CASE(JSOP_DEFXMLNS)
6747 BEGIN_CASE(JSOP_ANYNAME)
6748 if (!js_GetAnyName(cx, &rval))
6749 goto error;
6750 PUSH_OPND(rval);
6751 END_CASE(JSOP_ANYNAME)
6753 BEGIN_CASE(JSOP_QNAMEPART)
6754 LOAD_ATOM(0);
6755 PUSH_OPND(ATOM_KEY(atom));
6756 END_CASE(JSOP_QNAMEPART)
6758 BEGIN_CASE(JSOP_QNAMECONST)
6759 LOAD_ATOM(0);
6760 rval = ATOM_KEY(atom);
6761 lval = FETCH_OPND(-1);
6762 obj = js_ConstructXMLQNameObject(cx, lval, rval);
6763 if (!obj)
6764 goto error;
6765 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
6766 END_CASE(JSOP_QNAMECONST)
6768 BEGIN_CASE(JSOP_QNAME)
6769 rval = FETCH_OPND(-1);
6770 lval = FETCH_OPND(-2);
6771 obj = js_ConstructXMLQNameObject(cx, lval, rval);
6772 if (!obj)
6773 goto error;
6774 regs.sp--;
6775 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
6776 END_CASE(JSOP_QNAME)
6778 BEGIN_CASE(JSOP_TOATTRNAME)
6779 rval = FETCH_OPND(-1);
6780 if (!js_ToAttributeName(cx, &rval))
6781 goto error;
6782 STORE_OPND(-1, rval);
6783 END_CASE(JSOP_TOATTRNAME)
6785 BEGIN_CASE(JSOP_TOATTRVAL)
6786 rval = FETCH_OPND(-1);
6787 JS_ASSERT(JSVAL_IS_STRING(rval));
6788 str = js_EscapeAttributeValue(cx, JSVAL_TO_STRING(rval), JS_FALSE);
6789 if (!str)
6790 goto error;
6791 STORE_OPND(-1, STRING_TO_JSVAL(str));
6792 END_CASE(JSOP_TOATTRVAL)
6794 BEGIN_CASE(JSOP_ADDATTRNAME)
6795 BEGIN_CASE(JSOP_ADDATTRVAL)
6796 rval = FETCH_OPND(-1);
6797 lval = FETCH_OPND(-2);
6798 str = JSVAL_TO_STRING(lval);
6799 str2 = JSVAL_TO_STRING(rval);
6800 str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2);
6801 if (!str)
6802 goto error;
6803 regs.sp--;
6804 STORE_OPND(-1, STRING_TO_JSVAL(str));
6805 END_CASE(JSOP_ADDATTRNAME)
6807 BEGIN_CASE(JSOP_BINDXMLNAME)
6808 lval = FETCH_OPND(-1);
6809 if (!js_FindXMLProperty(cx, lval, &obj, &id))
6810 goto error;
6811 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
6812 PUSH_OPND(ID_TO_VALUE(id));
6813 END_CASE(JSOP_BINDXMLNAME)
6815 BEGIN_CASE(JSOP_SETXMLNAME)
6816 obj = JSVAL_TO_OBJECT(FETCH_OPND(-3));
6817 rval = FETCH_OPND(-1);
6818 FETCH_ELEMENT_ID(obj, -2, id);
6819 if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
6820 goto error;
6821 rval = FETCH_OPND(-1);
6822 regs.sp -= 2;
6823 STORE_OPND(-1, rval);
6824 END_CASE(JSOP_SETXMLNAME)
6826 BEGIN_CASE(JSOP_CALLXMLNAME)
6827 BEGIN_CASE(JSOP_XMLNAME)
6828 lval = FETCH_OPND(-1);
6829 if (!js_FindXMLProperty(cx, lval, &obj, &id))
6830 goto error;
6831 if (!OBJ_GET_PROPERTY(cx, obj, id, &rval))
6832 goto error;
6833 STORE_OPND(-1, rval);
6834 if (op == JSOP_CALLXMLNAME)
6835 PUSH_OPND(OBJECT_TO_JSVAL(obj));
6836 END_CASE(JSOP_XMLNAME)
6838 BEGIN_CASE(JSOP_DESCENDANTS)
6839 BEGIN_CASE(JSOP_DELDESC)
6840 FETCH_OBJECT(cx, -2, lval, obj);
6841 rval = FETCH_OPND(-1);
6842 if (!js_GetXMLDescendants(cx, obj, rval, &rval))
6843 goto error;
6845 if (op == JSOP_DELDESC) {
6846 regs.sp[-1] = rval; /* set local root */
6847 if (!js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval)))
6848 goto error;
6849 rval = JSVAL_TRUE; /* always succeed */
6852 regs.sp--;
6853 STORE_OPND(-1, rval);
6854 END_CASE(JSOP_DESCENDANTS)
6856 BEGIN_CASE(JSOP_FILTER)
6858 * We push the hole value before jumping to [enditer] so we can
6859 * detect the first iteration and direct js_StepXMLListFilter to
6860 * initialize filter's state.
6862 PUSH_OPND(JSVAL_HOLE);
6863 len = GET_JUMP_OFFSET(regs.pc);
6864 JS_ASSERT(len > 0);
6865 END_VARLEN_CASE
6867 BEGIN_CASE(JSOP_ENDFILTER)
6868 cond = (regs.sp[-1] != JSVAL_HOLE);
6869 if (cond) {
6870 /* Exit the "with" block left from the previous iteration. */
6871 js_LeaveWith(cx);
6873 if (!js_StepXMLListFilter(cx, cond))
6874 goto error;
6875 if (regs.sp[-1] != JSVAL_NULL) {
6877 * Decrease sp after EnterWith returns as we use sp[-1] there
6878 * to root temporaries.
6880 JS_ASSERT(VALUE_IS_XML(cx, regs.sp[-1]));
6881 if (!js_EnterWith(cx, -2))
6882 goto error;
6883 regs.sp--;
6884 len = GET_JUMP_OFFSET(regs.pc);
6885 JS_ASSERT(len < 0);
6886 BRANCH(len);
6888 regs.sp--;
6889 END_CASE(JSOP_ENDFILTER);
6891 BEGIN_CASE(JSOP_TOXML)
6892 rval = FETCH_OPND(-1);
6893 obj = js_ValueToXMLObject(cx, rval);
6894 if (!obj)
6895 goto error;
6896 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
6897 END_CASE(JSOP_TOXML)
6899 BEGIN_CASE(JSOP_TOXMLLIST)
6900 rval = FETCH_OPND(-1);
6901 obj = js_ValueToXMLListObject(cx, rval);
6902 if (!obj)
6903 goto error;
6904 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
6905 END_CASE(JSOP_TOXMLLIST)
6907 BEGIN_CASE(JSOP_XMLTAGEXPR)
6908 rval = FETCH_OPND(-1);
6909 str = js_ValueToString(cx, rval);
6910 if (!str)
6911 goto error;
6912 STORE_OPND(-1, STRING_TO_JSVAL(str));
6913 END_CASE(JSOP_XMLTAGEXPR)
6915 BEGIN_CASE(JSOP_XMLELTEXPR)
6916 rval = FETCH_OPND(-1);
6917 if (VALUE_IS_XML(cx, rval)) {
6918 str = js_ValueToXMLString(cx, rval);
6919 } else {
6920 str = js_ValueToString(cx, rval);
6921 if (str)
6922 str = js_EscapeElementValue(cx, str);
6924 if (!str)
6925 goto error;
6926 STORE_OPND(-1, STRING_TO_JSVAL(str));
6927 END_CASE(JSOP_XMLELTEXPR)
6929 BEGIN_CASE(JSOP_XMLOBJECT)
6930 LOAD_OBJECT(0);
6931 obj = js_CloneXMLObject(cx, obj);
6932 if (!obj)
6933 goto error;
6934 PUSH_OPND(OBJECT_TO_JSVAL(obj));
6935 END_CASE(JSOP_XMLOBJECT)
6937 BEGIN_CASE(JSOP_XMLCDATA)
6938 LOAD_ATOM(0);
6939 str = ATOM_TO_STRING(atom);
6940 obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_TEXT, NULL, str);
6941 if (!obj)
6942 goto error;
6943 PUSH_OPND(OBJECT_TO_JSVAL(obj));
6944 END_CASE(JSOP_XMLCDATA)
6946 BEGIN_CASE(JSOP_XMLCOMMENT)
6947 LOAD_ATOM(0);
6948 str = ATOM_TO_STRING(atom);
6949 obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_COMMENT, NULL, str);
6950 if (!obj)
6951 goto error;
6952 PUSH_OPND(OBJECT_TO_JSVAL(obj));
6953 END_CASE(JSOP_XMLCOMMENT)
6955 BEGIN_CASE(JSOP_XMLPI)
6956 LOAD_ATOM(0);
6957 str = ATOM_TO_STRING(atom);
6958 rval = FETCH_OPND(-1);
6959 str2 = JSVAL_TO_STRING(rval);
6960 obj = js_NewXMLSpecialObject(cx,
6961 JSXML_CLASS_PROCESSING_INSTRUCTION,
6962 str, str2);
6963 if (!obj)
6964 goto error;
6965 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
6966 END_CASE(JSOP_XMLPI)
6968 BEGIN_CASE(JSOP_GETFUNNS)
6969 if (!js_GetFunctionNamespace(cx, &rval))
6970 goto error;
6971 PUSH_OPND(rval);
6972 END_CASE(JSOP_GETFUNNS)
6973 #endif /* JS_HAS_XML_SUPPORT */
6975 BEGIN_CASE(JSOP_ENTERBLOCK)
6976 LOAD_OBJECT(0);
6977 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj));
6978 JS_ASSERT(StackBase(fp) + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp);
6979 vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj);
6980 JS_ASSERT(regs.sp < vp);
6981 JS_ASSERT(vp <= fp->slots + script->nslots);
6982 while (regs.sp < vp) {
6983 STORE_OPND(0, JSVAL_VOID);
6984 regs.sp++;
6987 #ifdef DEBUG
6988 JS_ASSERT(fp->blockChain == OBJ_GET_PARENT(cx, obj));
6991 * The young end of fp->scopeChain may omit blocks if we
6992 * haven't closed over them, but if there are any closure
6993 * blocks on fp->scopeChain, they'd better be (clones of)
6994 * ancestors of the block we're entering now; anything
6995 * else we should have popped off fp->scopeChain when we
6996 * left its static scope.
6998 obj2 = fp->scopeChain;
6999 while ((clasp = OBJ_GET_CLASS(cx, obj2)) == &js_WithClass)
7000 obj2 = OBJ_GET_PARENT(cx, obj2);
7001 if (clasp == &js_BlockClass &&
7002 obj2->getAssignedPrivate() == fp) {
7003 JSObject *youngestProto = OBJ_GET_PROTO(cx, obj2);
7004 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(youngestProto));
7005 parent = obj;
7006 while ((parent = OBJ_GET_PARENT(cx, parent)) != youngestProto)
7007 JS_ASSERT(parent);
7009 #endif
7011 fp->blockChain = obj;
7012 END_CASE(JSOP_ENTERBLOCK)
7014 BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
7015 BEGIN_CASE(JSOP_LEAVEBLOCK)
7017 #ifdef DEBUG
7018 JS_ASSERT(OBJ_GET_CLASS(cx, fp->blockChain) == &js_BlockClass);
7019 uintN blockDepth = OBJ_BLOCK_DEPTH(cx, fp->blockChain);
7021 JS_ASSERT(blockDepth <= StackDepth(script));
7022 #endif
7024 * If we're about to leave the dynamic scope of a block that has
7025 * been cloned onto fp->scopeChain, clear its private data, move
7026 * its locals from the stack into the clone, and pop it off the
7027 * chain.
7029 obj = fp->scopeChain;
7030 if (OBJ_GET_PROTO(cx, obj) == fp->blockChain) {
7031 JS_ASSERT (OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
7032 if (!js_PutBlockObject(cx, JS_TRUE))
7033 goto error;
7036 /* Pop the block chain, too. */
7037 fp->blockChain = OBJ_GET_PARENT(cx, fp->blockChain);
7040 * We will move the result of the expression to the new topmost
7041 * stack slot.
7043 if (op == JSOP_LEAVEBLOCKEXPR)
7044 rval = FETCH_OPND(-1);
7045 regs.sp -= GET_UINT16(regs.pc);
7046 if (op == JSOP_LEAVEBLOCKEXPR) {
7047 JS_ASSERT(StackBase(fp) + blockDepth == regs.sp - 1);
7048 STORE_OPND(-1, rval);
7049 } else {
7050 JS_ASSERT(StackBase(fp) + blockDepth == regs.sp);
7053 END_CASE(JSOP_LEAVEBLOCK)
7055 BEGIN_CASE(JSOP_CALLBUILTIN)
7056 #ifdef JS_TRACER
7057 obj = js_GetBuiltinFunction(cx, GET_INDEX(regs.pc));
7058 if (!obj)
7059 goto error;
7060 rval = FETCH_OPND(-1);
7061 PUSH_OPND(rval);
7062 STORE_OPND(-2, OBJECT_TO_JSVAL(obj));
7063 #else
7064 goto bad_opcode; /* This is an imacro-only opcode. */
7065 #endif
7066 END_CASE(JSOP_CALLBUILTIN)
7068 #if JS_HAS_GENERATORS
7069 BEGIN_CASE(JSOP_GENERATOR)
7070 ASSERT_NOT_THROWING(cx);
7071 regs.pc += JSOP_GENERATOR_LENGTH;
7072 obj = js_NewGenerator(cx, fp);
7073 if (!obj)
7074 goto error;
7075 JS_ASSERT(!fp->callobj && !fp->argsobj);
7076 fp->rval = OBJECT_TO_JSVAL(obj);
7077 ok = JS_TRUE;
7078 if (inlineCallCount != 0)
7079 goto inline_return;
7080 goto exit;
7082 BEGIN_CASE(JSOP_YIELD)
7083 ASSERT_NOT_THROWING(cx);
7084 if (FRAME_TO_GENERATOR(fp)->state == JSGEN_CLOSING) {
7085 js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD,
7086 JSDVG_SEARCH_STACK, fp->argv[-2], NULL);
7087 goto error;
7089 fp->rval = FETCH_OPND(-1);
7090 fp->flags |= JSFRAME_YIELDING;
7091 regs.pc += JSOP_YIELD_LENGTH;
7092 ok = JS_TRUE;
7093 goto exit;
7095 BEGIN_CASE(JSOP_ARRAYPUSH)
7096 slot = GET_UINT16(regs.pc);
7097 JS_ASSERT(script->nfixed <= slot);
7098 JS_ASSERT(slot < script->nslots);
7099 lval = fp->slots[slot];
7100 obj = JSVAL_TO_OBJECT(lval);
7101 rval = FETCH_OPND(-1);
7102 if (!js_ArrayCompPush(cx, obj, rval))
7103 goto error;
7104 regs.sp--;
7105 END_CASE(JSOP_ARRAYPUSH)
7106 #endif /* JS_HAS_GENERATORS */
7108 BEGIN_CASE(JSOP_LOOP)
7109 END_CASE(JSOP_LOOP)
7111 #if JS_THREADED_INTERP
7112 L_JSOP_BACKPATCH:
7113 L_JSOP_BACKPATCH_POP:
7115 # if !JS_HAS_GENERATORS
7116 L_JSOP_GENERATOR:
7117 L_JSOP_YIELD:
7118 L_JSOP_ARRAYPUSH:
7119 # endif
7121 # if !JS_HAS_SHARP_VARS
7122 L_JSOP_DEFSHARP:
7123 L_JSOP_USESHARP:
7124 # endif
7126 # if !JS_HAS_DESTRUCTURING
7127 L_JSOP_ENUMCONSTELEM:
7128 # endif
7130 # if !JS_HAS_XML_SUPPORT
7131 L_JSOP_CALLXMLNAME:
7132 L_JSOP_STARTXMLEXPR:
7133 L_JSOP_STARTXML:
7134 L_JSOP_DELDESC:
7135 L_JSOP_GETFUNNS:
7136 L_JSOP_XMLPI:
7137 L_JSOP_XMLCOMMENT:
7138 L_JSOP_XMLCDATA:
7139 L_JSOP_XMLOBJECT:
7140 L_JSOP_XMLELTEXPR:
7141 L_JSOP_XMLTAGEXPR:
7142 L_JSOP_TOXMLLIST:
7143 L_JSOP_TOXML:
7144 L_JSOP_ENDFILTER:
7145 L_JSOP_FILTER:
7146 L_JSOP_DESCENDANTS:
7147 L_JSOP_XMLNAME:
7148 L_JSOP_SETXMLNAME:
7149 L_JSOP_BINDXMLNAME:
7150 L_JSOP_ADDATTRVAL:
7151 L_JSOP_ADDATTRNAME:
7152 L_JSOP_TOATTRVAL:
7153 L_JSOP_TOATTRNAME:
7154 L_JSOP_QNAME:
7155 L_JSOP_QNAMECONST:
7156 L_JSOP_QNAMEPART:
7157 L_JSOP_ANYNAME:
7158 L_JSOP_DEFXMLNS:
7159 # endif
7161 #else /* !JS_THREADED_INTERP */
7162 default:
7163 #endif
7164 #ifndef JS_TRACER
7165 bad_opcode:
7166 #endif
7168 char numBuf[12];
7169 JS_snprintf(numBuf, sizeof numBuf, "%d", op);
7170 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
7171 JSMSG_BAD_BYTECODE, numBuf);
7172 goto error;
7175 #if !JS_THREADED_INTERP
7176 } /* switch (op) */
7177 } /* for (;;) */
7178 #endif /* !JS_THREADED_INTERP */
7180 error:
7181 if (fp->imacpc && cx->throwing) {
7182 // To keep things simple, we hard-code imacro exception handlers here.
7183 if (*fp->imacpc == JSOP_NEXTITER && js_ValueIsStopIteration(cx->exception)) {
7184 // pc may point to JSOP_DUP here due to bug 474854.
7185 JS_ASSERT(*regs.pc == JSOP_CALL || *regs.pc == JSOP_DUP || *regs.pc == JSOP_TRUE);
7186 cx->throwing = JS_FALSE;
7187 cx->exception = JSVAL_VOID;
7188 regs.sp[-1] = JSVAL_HOLE;
7189 PUSH(JSVAL_FALSE);
7190 goto end_imacro;
7193 // Handle other exceptions as if they came from the imacro-calling pc.
7194 regs.pc = fp->imacpc;
7195 fp->imacpc = NULL;
7196 atoms = script->atomMap.vector;
7199 JS_ASSERT((size_t)((fp->imacpc ? fp->imacpc : regs.pc) - script->code) < script->length);
7201 #ifdef JS_TRACER
7203 * This abort could be weakened to permit tracing through exceptions that
7204 * are thrown and caught within a loop, with the co-operation of the tracer.
7205 * For now just bail on any sign of trouble.
7207 if (TRACE_RECORDER(cx))
7208 js_AbortRecording(cx, "error or exception while recording");
7209 #endif
7211 if (!cx->throwing) {
7212 /* This is an error, not a catchable exception, quit the frame ASAP. */
7213 ok = JS_FALSE;
7214 } else {
7215 JSTrapHandler handler;
7216 JSTryNote *tn, *tnlimit;
7217 uint32 offset;
7219 /* Call debugger throw hook if set. */
7220 handler = cx->debugHooks->throwHook;
7221 if (handler) {
7222 switch (handler(cx, script, regs.pc, &rval,
7223 cx->debugHooks->throwHookData)) {
7224 case JSTRAP_ERROR:
7225 cx->throwing = JS_FALSE;
7226 goto error;
7227 case JSTRAP_RETURN:
7228 cx->throwing = JS_FALSE;
7229 fp->rval = rval;
7230 ok = JS_TRUE;
7231 goto forced_return;
7232 case JSTRAP_THROW:
7233 cx->exception = rval;
7234 case JSTRAP_CONTINUE:
7235 default:;
7237 CHECK_INTERRUPT_HANDLER();
7241 * Look for a try block in script that can catch this exception.
7243 if (script->trynotesOffset == 0)
7244 goto no_catch;
7246 offset = (uint32)(regs.pc - script->main);
7247 tn = JS_SCRIPT_TRYNOTES(script)->vector;
7248 tnlimit = tn + JS_SCRIPT_TRYNOTES(script)->length;
7249 do {
7250 if (offset - tn->start >= tn->length)
7251 continue;
7254 * We have a note that covers the exception pc but we must check
7255 * whether the interpreter has already executed the corresponding
7256 * handler. This is possible when the executed bytecode
7257 * implements break or return from inside a for-in loop.
7259 * In this case the emitter generates additional [enditer] and
7260 * [gosub] opcodes to close all outstanding iterators and execute
7261 * the finally blocks. If such an [enditer] throws an exception,
7262 * its pc can still be inside several nested for-in loops and
7263 * try-finally statements even if we have already closed the
7264 * corresponding iterators and invoked the finally blocks.
7266 * To address this, we make [enditer] always decrease the stack
7267 * even when its implementation throws an exception. Thus already
7268 * executed [enditer] and [gosub] opcodes will have try notes
7269 * with the stack depth exceeding the current one and this
7270 * condition is what we use to filter them out.
7272 if (tn->stackDepth > regs.sp - StackBase(fp))
7273 continue;
7276 * Set pc to the first bytecode after the the try note to point
7277 * to the beginning of catch or finally or to [enditer] closing
7278 * the for-in loop.
7280 regs.pc = (script)->main + tn->start + tn->length;
7282 ok = js_UnwindScope(cx, fp, tn->stackDepth, JS_TRUE);
7283 JS_ASSERT(fp->regs->sp == StackBase(fp) + tn->stackDepth);
7284 if (!ok) {
7286 * Restart the handler search with updated pc and stack depth
7287 * to properly notify the debugger.
7289 goto error;
7292 switch (tn->kind) {
7293 case JSTRY_CATCH:
7294 JS_ASSERT(js_GetOpcode(cx, fp->script, regs.pc) == JSOP_ENTERBLOCK);
7296 #if JS_HAS_GENERATORS
7297 /* Catch cannot intercept the closing of a generator. */
7298 if (JS_UNLIKELY(cx->exception == JSVAL_ARETURN))
7299 break;
7300 #endif
7303 * Don't clear cx->throwing to save cx->exception from GC
7304 * until it is pushed to the stack via [exception] in the
7305 * catch block.
7307 len = 0;
7308 DO_NEXT_OP(len);
7310 case JSTRY_FINALLY:
7312 * Push (true, exception) pair for finally to indicate that
7313 * [retsub] should rethrow the exception.
7315 PUSH(JSVAL_TRUE);
7316 PUSH(cx->exception);
7317 cx->throwing = JS_FALSE;
7318 len = 0;
7319 DO_NEXT_OP(len);
7321 case JSTRY_ITER:
7323 * This is similar to JSOP_ENDITER in the interpreter loop,
7324 * except the code now uses the stack slot normally used by
7325 * JSOP_NEXTITER, namely regs.sp[-1] before the regs.sp -= 2
7326 * adjustment and regs.sp[1] after, to save and restore the
7327 * pending exception.
7329 JS_ASSERT(js_GetOpcode(cx, fp->script, regs.pc) == JSOP_ENDITER);
7330 regs.sp[-1] = cx->exception;
7331 cx->throwing = JS_FALSE;
7332 ok = js_CloseIterator(cx, regs.sp[-2]);
7333 regs.sp -= 2;
7334 if (!ok)
7335 goto error;
7336 cx->throwing = JS_TRUE;
7337 cx->exception = regs.sp[1];
7339 } while (++tn != tnlimit);
7341 no_catch:
7343 * Propagate the exception or error to the caller unless the exception
7344 * is an asynchronous return from a generator.
7346 ok = JS_FALSE;
7347 #if JS_HAS_GENERATORS
7348 if (JS_UNLIKELY(cx->throwing && cx->exception == JSVAL_ARETURN)) {
7349 cx->throwing = JS_FALSE;
7350 ok = JS_TRUE;
7351 fp->rval = JSVAL_VOID;
7353 #endif
7356 forced_return:
7358 * Unwind the scope making sure that ok stays false even when UnwindScope
7359 * returns true.
7361 * When a trap handler returns JSTRAP_RETURN, we jump here with ok set to
7362 * true bypassing any finally blocks.
7364 ok &= js_UnwindScope(cx, fp, 0, ok || cx->throwing);
7365 JS_ASSERT(regs.sp == StackBase(fp));
7367 #ifdef DEBUG
7368 cx->tracePrevPc = NULL;
7369 #endif
7371 if (inlineCallCount)
7372 goto inline_return;
7374 exit:
7376 * At this point we are inevitably leaving an interpreted function or a
7377 * top-level script, and returning to one of:
7378 * (a) an "out of line" call made through js_Invoke;
7379 * (b) a js_Execute activation;
7380 * (c) a generator (SendToGenerator, jsiter.c).
7382 * We must not be in an inline frame. The check above ensures that for the
7383 * error case and for a normal return, the code jumps directly to parent's
7384 * frame pc.
7386 JS_ASSERT(inlineCallCount == 0);
7387 JS_ASSERT(fp->regs == &regs);
7388 #ifdef JS_TRACER
7389 if (TRACE_RECORDER(cx))
7390 js_AbortRecording(cx, "recording out of js_Interpret");
7391 #endif
7392 #if JS_HAS_GENERATORS
7393 if (JS_UNLIKELY(fp->flags & JSFRAME_YIELDING)) {
7394 JSGenerator *gen;
7396 gen = FRAME_TO_GENERATOR(fp);
7397 gen->savedRegs = regs;
7398 gen->frame.regs = &gen->savedRegs;
7399 } else
7400 #endif /* JS_HAS_GENERATORS */
7402 JS_ASSERT(!fp->blockChain);
7403 JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
7404 fp->regs = NULL;
7407 /* Undo the remaining effects committed on entry to js_Interpret. */
7408 if (script->staticLevel < JS_DISPLAY_SIZE)
7409 cx->display[script->staticLevel] = fp->displaySave;
7410 if (cx->version == currentVersion && currentVersion != originalVersion)
7411 js_SetVersion(cx, originalVersion);
7412 --cx->interpLevel;
7414 #ifdef JS_TRACER
7415 if (tr) {
7416 SET_TRACE_RECORDER(cx, tr);
7417 if (!tr->wasDeepAborted()) {
7418 tr->popAbortStack();
7419 tr->deepAbort();
7422 #endif
7423 return ok;
7425 atom_not_defined:
7427 const char *printable;
7429 printable = js_AtomToPrintableString(cx, atom);
7430 if (printable)
7431 js_ReportIsNotDefined(cx, printable);
7432 goto error;
7436 #endif /* !defined jsinvoke_cpp___ */