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
17 * The Original Code is Mozilla Communicator client code, released
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.
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.
49 #include "jsarena.h" /* Added by JSIFY */
50 #include "jsutil.h" /* Added by JSIFY */
58 #include "jsversion.h"
72 #include "jsstaticcheck.h"
74 #include "jslibmath.h"
76 #ifdef INCLUDE_MOZILLA_DTRACE
77 #include "jsdtracef.h"
80 #if JS_HAS_XML_SUPPORT
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
;
99 jsuword kshape
, vshape
, khash
;
101 const JSCodeSpec
*cs
;
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) {
144 for (uintN i
= 0; i
!= scopeIndex
; i
++)
145 tmp
= OBJ_GET_PARENT(cx
, tmp
);
146 JS_ASSERT(tmp
!= pobj
);
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
;
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
];
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
)) {
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
++);
208 "branding %p (%s) for funobj %p (%s), shape %lu\n",
209 pobj
, LOCKED_OBJ_GET_CLASS(pobj
)->name
,
211 JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx
, JSVAL_TO_OBJECT(v
))),
214 scope
->brandingShapeChange(cx
, sprop
->slot
, v
);
215 if (js_IsPropertyCacheDisabled(cx
)) /* check for rt->shapeGen overflow */
216 return JS_NO_PROP_CACHE_FILL
;
219 vword
= JSVAL_OBJECT_TO_PCVAL(v
);
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
);
231 /* Best we can do is to cache sprop (still a nice speedup). */
232 vword
= SPROP_TO_PCVAL(sprop
);
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
263 JS_ASSERT(scope
->owned());
265 kshape
= sprop
->parent
->shape
;
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
),
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
;
295 kshape
= OBJ_SHAPE(obj
);
296 vshape
= scope
->shape
;
299 khash
= PROPERTY_CACHE_HASH_PC(pc
, kshape
);
301 JS_ASSERT(scopeIndex
== 0 && protoIndex
== 0);
303 if (op
== JSOP_LENGTH
) {
304 atom
= cx
->runtime
->atomState
.lengthAtom
;
306 pcoff
= (JOF_TYPE(cs
->format
) == JOF_SLOTATOM
) ? SLOTNO_LEN
: 0;
307 GET_ATOM_FROM_BYTECODE(cx
->fp
->script
, pc
, pcoff
, atom
);
311 if (scopeIndex
== 0) {
312 JS_ASSERT(protoIndex
!= 0);
313 JS_ASSERT((protoIndex
== 1) == (OBJ_GET_PROTO(cx
, obj
) == pobj
));
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
++);
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
);
357 JS_REQUIRES_STACK JSAtom
*
358 js_FullTestPropertyCache(JSContext
*cx
, jsbytecode
*pc
,
359 JSObject
**objp
, JSObject
**pobjp
,
360 JSPropCacheEntry
**entryp
)
363 const JSCodeSpec
*cs
;
366 JSObject
*obj
, *pobj
, *tmp
;
367 JSPropCacheEntry
*entry
;
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
;
378 pcoff
= (JOF_TYPE(cs
->format
) == JOF_SLOTATOM
) ? SLOTNO_LEN
: 0;
379 GET_ATOM_FROM_BYTECODE(cx
->fp
->script
, pc
, pcoff
, atom
);
383 JS_ASSERT(OBJ_IS_NATIVE(obj
));
384 entry
= &JS_PROPERTY_CACHE(cx
).table
[PROPERTY_CACHE_HASH_ATOM(atom
, obj
)];
388 if (entry
->kpc
!= (jsbytecode
*) atom
) {
389 PCMETER(JS_PROPERTY_CACHE(cx
).idmisses
++);
392 entry
= &JS_PROPERTY_CACHE(cx
).table
[PROPERTY_CACHE_HASH_PC(pc
, OBJ_SHAPE(obj
))];
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
,
403 js_Disassemble1(cx
, cx
->fp
->script
, pc
,
404 pc
- cx
->fp
->script
->code
,
411 if (entry
->kshape
!= (jsuword
) obj
) {
412 PCMETER(JS_PROPERTY_CACHE(cx
).komisses
++);
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
))
424 vcap
-= PCVCAP_PROTOSIZE
;
430 while (vcap
& PCVCAP_PROTOMASK
) {
431 tmp
= OBJ_GET_PROTO(cx
, pobj
);
432 if (!tmp
|| !OBJ_IS_NATIVE(tmp
))
438 if (JS_LOCK_OBJ_IF_SHAPE(cx
, pobj
, PCVCAP_SHAPE(vcap
))) {
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
);
450 PCMETER(JS_PROPERTY_CACHE(cx
).vcmisses
++);
455 #define ASSERT_CACHE_IS_EMPTY(cache) \
457 JSPropertyCache *cache_ = (cache); \
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); \
468 #define ASSERT_CACHE_IS_EMPTY(cache) ((void)0)
471 JS_STATIC_ASSERT(PCVAL_NULL
== 0);
474 js_PurgePropertyCache(JSContext
*cx
, JSPropertyCache
*cache
)
477 ASSERT_CACHE_IS_EMPTY(cache
);
481 memset(cache
->table
, 0, sizeof cache
->table
);
482 cache
->empty
= JS_TRUE
;
484 #ifdef JS_PROPERTY_CACHE_METERING
487 fp
= fopen("/tmp/propcache.stats", "w");
489 fputs("Property cache stats for ", fp
);
491 fprintf(fp
, "thread %lu, ", (unsigned long) cx
->thread
->id
);
493 fprintf(fp
, "GC %u\n", cx
->runtime
->gcNumber
);
495 # define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)cache->mem)
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
))
532 (100. * cache
->inipchits
) / cache
->initests
,
533 (100. * (cache
->tests
- cache
->misses
)) / cache
->tests
);
539 PCMETER(cache
->flushes
++);
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
;
551 if (JS_UPTRDIFF(entry
->kpc
, script
->code
) < script
->length
) {
555 entry
->vcap
= entry
->vword
= 0;
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
)
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
)
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
))
584 JS_ARENA_ALLOCATE_CAST(sp2
, jsval
*, &cx
->stackPool
,
585 (nslots
- surplus
) * sizeof(jsval
));
586 JS_ASSERT(sp2
== sp
+ surplus
);
590 JS_STATIC_INTERPRET JS_REQUIRES_STACK jsval
*
591 js_AllocRawStack(JSContext
*cx
, uintN nslots
, void **markp
)
595 JS_ASSERT(nslots
!= 0);
596 JS_ASSERT_NOT_ON_TRACE(cx
);
598 if (!cx
->stackPool
.first
.next
) {
601 JS_ARENA_ALLOCATE_CAST(timestamp
, int64
*,
602 &cx
->stackPool
, sizeof *timestamp
);
604 js_ReportOutOfScriptQuota(cx
);
607 *timestamp
= JS_Now();
611 *markp
= JS_ARENA_MARK(&cx
->stackPool
);
612 JS_ARENA_ALLOCATE_CAST(sp
, jsval
*, &cx
->stackPool
, nslots
* sizeof(jsval
));
614 js_ReportOutOfScriptQuota(cx
);
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
)
631 /* Callers don't check for zero nslots: we do to avoid empty segments. */
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
);
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
);
651 * Need a new stack segment, so allocate and push a stack segment
652 * header from the 2 extra slots.
654 sh
= (JSStackHeader
*)sp
;
656 sh
->down
= cx
->stackHeaders
;
657 cx
->stackHeaders
= sh
;
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
));
670 JS_REQUIRES_STACK
JS_FRIEND_API(void)
671 js_FreeStack(JSContext
*cx
, void *mark
)
676 /* Check for zero nslots allocation special case. */
680 /* We can assert because js_FreeStack always balances js_AllocStack. */
681 sh
= cx
->stackHeaders
;
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
;
689 cx
->stackHeaders
= sh
->down
;
691 /* Release the stackPool space allocated since mark was set. */
692 JS_ARENA_RELEASE(&cx
->stackPool
, mark
);
696 js_GetScopeChain(JSContext
*cx
, JSStackFrame
*fp
)
698 JSObject
*sharedBlock
= fp
->blockChain
;
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
) ||
708 JS_ASSERT(fp
->scopeChain
);
709 return fp
->scopeChain
;
712 /* We don't handle cloning blocks on trace. */
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
))
729 /* We know we must clone everything on blockChain. */
730 limitBlock
= limitClone
= NULL
;
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
775 JSObject
*innermostNewChild
= js_CloneBlockObject(cx
, sharedBlock
, fp
);
776 if (!innermostNewChild
)
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
;
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
)
793 /* As in the call above, we don't know the real parent yet. */
795 = js_CloneBlockObject(cx
, sharedBlock
, fp
);
800 * Avoid OBJ_SET_PARENT overhead as newChild cannot escape to
803 STOBJ_SET_PARENT(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
,
818 /* Place our newly cloned blocks at the head of the scope chain. */
819 fp
->scopeChain
= innermostNewChild
;
820 return fp
->scopeChain
;
824 js_GetPrimitiveThis(JSContext
*cx
, jsval
*vp
, JSClass
*clasp
, jsval
*thisvp
)
830 if (JSVAL_IS_OBJECT(v
)) {
831 obj
= JS_THIS_OBJECT(cx
, vp
);
832 if (!JS_InstanceOf(cx
, obj
, clasp
, vp
+ 2))
834 v
= obj
->fslots
[JSSLOT_PRIVATE
];
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:
846 * function f() { return this }
847 * function g() { return f }
853 * The alert should display "true".
855 JS_STATIC_INTERPRET JSObject
*
856 js_ComputeGlobalThis(JSContext
*cx
, JSBool lazy
, jsval
*argv
)
860 if (JSVAL_IS_PRIMITIVE(argv
[-2]) ||
861 !OBJ_GET_PARENT(cx
, JSVAL_TO_OBJECT(argv
[-2]))) {
862 thisp
= cx
->globalObject
;
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 */
883 JS_ASSERT(fp
->argv
== argv
);
884 fp
->dormantNext
= cx
->dormantFrameChain
;
885 cx
->dormantFrameChain
= fp
;
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
);
894 cx
->dormantFrameChain
= fp
->dormantNext
;
895 fp
->dormantNext
= 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
)
909 /* Some objects (e.g., With) delegate 'this' to another object. */
910 thisp
= OBJ_THIS_OBJECT(cx
, thisp
);
913 argv
[-1] = OBJECT_TO_JSVAL(thisp
);
918 ComputeThis(JSContext
*cx
, JSBool lazy
, jsval
*argv
)
922 JS_ASSERT(!JSVAL_IS_NULL(argv
[-1]));
923 if (!JSVAL_IS_OBJECT(argv
[-1])) {
924 if (!js_PrimitiveToObject(cx
, &argv
[-1]))
926 thisp
= JSVAL_TO_OBJECT(argv
[-1]);
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
);
938 argv
[-1] = OBJECT_TO_JSVAL(thisp
);
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
= {
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
978 JS_STATIC_INTERPRET JSBool
979 js_OnUnknownMethod(JSContext
*cx
, jsval
*vp
)
983 JSTempValueRooter tvr
;
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
);
995 if (JSVAL_IS_PRIMITIVE(tvr
.u
.value
)) {
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
);
1006 vp
[0] = ID_TO_VALUE(id
);
1009 obj
= js_NewObjectWithGivenProto(cx
, &js_NoSuchMethodClass
,
1015 obj
->fslots
[JSSLOT_FOUND_FUNCTION
] = tvr
.u
.value
;
1016 obj
->fslots
[JSSLOT_SAVED_ID
] = vp
[0];
1017 vp
[0] = OBJECT_TO_JSVAL(obj
);
1022 JS_POP_TEMP_ROOT(cx
, &tvr
);
1026 static JS_REQUIRES_STACK JSBool
1027 NoSuchMethod(JSContext
*cx
, uintN argc
, jsval
*vp
, uint32 flags
)
1032 JSObject
*obj
, *argsobj
;
1034 invokevp
= js_AllocStack(cx
, 2 + 2, &mark
);
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);
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
);
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
)
1092 jsval
*sp
, *argv
, *newvp
;
1094 JSObject
*funobj
, *parent
;
1102 uint32 rootedArgsFlag
;
1103 JSInterpreterHook hook
;
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");
1115 if (JSVAL_IS_PRIMITIVE(v
))
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
);
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
);
1144 if (VALUE_IS_FUNCTION(cx
, v
)) {
1145 /* Make vp refer to funobj to keep it available as argv[-2]. */
1147 funobj
= JSVAL_TO_OBJECT(v
);
1148 parent
= OBJ_GET_PARENT(cx
, funobj
);
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]);
1163 native
= ops
->construct
;
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
)) {
1177 script
= fun
->u
.i
.script
;
1180 native
= fun
->u
.n
.native
;
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]))
1195 if (flags
& JSINVOKE_CONSTRUCT
) {
1196 JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp
[1]));
1199 * We must call js_ComputeThis in case we are not called from the
1200 * interpreter, where a prior bytecode has computed an appropriate
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)) {
1213 flags
|= JSFRAME_COMPUTED_THIS
;
1218 if (native
&& fun
&& (fun
->flags
& JSFUN_FAST_NATIVE
)) {
1219 #ifdef DEBUG_NOT_THROWING
1220 JSBool alreadyThrowing
= cx
->throwing
;
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
;
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
);
1239 rootedArgsFlag
= JSFRAME_ROOTED_ARGV
;
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
)) {
1249 newvp
= js_AllocRawStack(cx
, 2 + argc
+ nslots
, NULL
);
1254 memcpy(newvp
, vp
, (2 + argc
) * sizeof(jsval
));
1259 /* Push void to initialize missing args. */
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
);
1277 /* Push void to initialize local variables. */
1278 for (jsval
*end
= sp
+ fun
->u
.i
.nvars
; sp
!= end
; ++sp
)
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
;
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
;
1306 frame
.imacpc
= 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");
1318 /* Init these now in case we goto out before first hook call. */
1319 hook
= cx
->debugHooks
->callHook
;
1323 /* If native, use caller varobj and scopeChain for eval. */
1324 JS_ASSERT(!frame
.varobj
);
1325 JS_ASSERT(!frame
.scopeChain
);
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
;
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
)) {
1344 frame
.slots
= sp
- fun
->u
.i
.nvars
;
1347 /* Call the hook if present after we fully initialized the frame. */
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
);
1361 /* Call the function, either a native method or an interpreted script. */
1363 #ifdef DEBUG_NOT_THROWING
1364 JSBool alreadyThrowing
= cx
->throwing
;
1367 #if JS_HAS_LVALUE_RETURN
1368 /* Set by JS_SetCallReturnValue2, used to return reference types. */
1369 cx
->rval2set
= JS_FALSE
;
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
);
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
);
1392 hook
= cx
->debugHooks
->callHook
;
1394 hook(cx
, &frame
, JS_FALSE
, &ok
, hookData
);
1397 ok
&= frame
.putActivationObjects(cx
);
1401 /* Restore cx->fp now that we're done releasing frame objects. */
1402 cx
->fp
= frame
.down
;
1405 /* Pop everything we may have allocated off the stack. */
1406 JS_ARENA_RELEASE(&cx
->stackPool
, mark
);
1412 js_ReportIsNotFunction(cx
, vp
, flags
& JSINVOKE_FUNFLAGS
);
1418 js_InternalInvoke(JSContext
*cx
, JSObject
*obj
, jsval fval
, uintN flags
,
1419 uintN argc
, jsval
*argv
, jsval
*rval
)
1426 invokevp
= js_AllocStack(cx
, 2 + argc
, &mark
);
1431 invokevp
[1] = OBJECT_TO_JSVAL(obj
);
1432 memcpy(invokevp
+ 2, argv
, argc
* sizeof *argv
);
1434 ok
= js_Invoke(cx
, argc
, invokevp
, flags
);
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.
1444 if (JSVAL_IS_GCTHING(*rval
) && *rval
!= JSVAL_NULL
) {
1445 if (cx
->localRootStack
) {
1446 if (js_PushLocalRoot(cx
, cx
->localRootStack
, *rval
) < 0)
1449 cx
->weakRoots
.lastInternalResult
= *rval
;
1454 js_FreeStack(cx
, mark
);
1459 js_InternalGetOrSet(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval fval
,
1460 JSAccessMode mode
, uintN argc
, jsval
*argv
, jsval
*rval
)
1462 JSSecurityCallbacks
*callbacks
;
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
);
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
)) {
1497 return js_InternalCall(cx
, obj
, fval
, argc
, argv
, rval
);
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
;
1512 #ifdef INCLUDE_MOZILLA_DTRACE
1513 if (JAVASCRIPT_EXECUTE_START_ENABLED())
1514 jsdtrace_execute_start(script
);
1517 hook
= cx
->debugHooks
->executeHook
;
1518 hookData
= mark
= NULL
;
1519 oldfp
= js_GetTopStackFrame(cx
);
1520 frame
.script
= script
;
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);
1537 frame
.callobj
= NULL
;
1538 frame
.argsobj
= NULL
;
1540 if (cx
->options
& JSOPTION_VAROBJFIX
) {
1541 while ((tmp
= OBJ_GET_PARENT(cx
, obj
)) != NULL
)
1545 frame
.callee
= NULL
;
1547 frame
.thisp
= chain
;
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
);
1561 memset(frame
.slots
, 0, script
->nfixed
* sizeof(jsval
));
1566 frame
.rval
= JSVAL_VOID
;
1568 frame
.scopeChain
= chain
;
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
;
1597 frame
.thisp
= OBJ_THIS_OBJECT(cx
, frame
.thisp
);
1602 frame
.flags
|= JSFRAME_COMPUTED_THIS
;
1606 hookData
= hook(cx
, &frame
, JS_TRUE
, 0,
1607 cx
->debugHooks
->executeHookData
);
1610 ok
= js_Interpret(cx
);
1612 *result
= frame
.rval
;
1615 hook
= cx
->debugHooks
->executeHook
;
1617 hook(cx
, &frame
, JS_FALSE
, &ok
, hookData
);
1622 js_FreeRawStack(cx
, mark
);
1625 if (oldfp
&& oldfp
!= down
) {
1626 JS_ASSERT(cx
->dormantFrameChain
== oldfp
);
1627 cx
->dormantFrameChain
= oldfp
->dormantNext
;
1628 oldfp
->dormantNext
= NULL
;
1632 #ifdef INCLUDE_MOZILLA_DTRACE
1633 if (JAVASCRIPT_EXECUTE_DONE_ENABLED())
1634 jsdtrace_execute_done(script
);
1640 js_CheckRedeclaration(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN attrs
,
1641 JSObject
**objp
, JSProperty
**propp
)
1645 uintN oldAttrs
, report
;
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
))
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
);
1677 * If our caller doesn't want prop, drop it (we don't need it any longer).
1680 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
1687 if (attrs
== JSPROP_INITIALIZER
) {
1688 /* Allow the new object to override properties. */
1692 /* The property must be dropped already. */
1694 report
= JSREPORT_WARNING
| JSREPORT_STRICT
;
1697 isFunction
= false; /* suppress bogus gcc warnings */
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
)))
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)
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
))
1725 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
1727 report
= JSREPORT_ERROR
;
1728 isFunction
= (oldAttrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) != 0;
1730 if (!OBJ_GET_PROPERTY(cx
, obj
, id
, &value
))
1732 isFunction
= VALUE_IS_FUNCTION(cx
, value
);
1736 type
= (attrs
== JSPROP_INITIALIZER
)
1738 : (oldAttrs
& attrs
& JSPROP_GETTER
)
1740 : (oldAttrs
& attrs
& JSPROP_SETTER
)
1742 : (oldAttrs
& JSPROP_READONLY
)
1747 name
= js_ValueToPrintableString(cx
, ID_TO_VALUE(id
));
1750 return JS_ReportErrorFlagsAndNumber(cx
, report
,
1751 js_GetErrorMessage
, NULL
,
1752 JSMSG_REDECLARED_VAR
,
1757 js_StrictlyEqual(JSContext
*cx
, jsval lval
, jsval rval
)
1759 jsval ltag
= JSVAL_TAG(lval
), rtag
= JSVAL_TAG(rval
);
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
&&
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
;
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
);
1821 clasp
= &js_ObjectClass
;
1823 proto
= parent
= NULL
;
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
1832 if (!OBJ_GET_PROPERTY(cx
, obj2
,
1833 ATOM_TO_JSID(cx
->runtime
->atomState
1834 .classPrototypeAtom
),
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
);
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
;
1859 /* Check the return value and if it's primitive, force it to be obj. */
1861 if (clampReturn
&& JSVAL_IS_PRIMITIVE(rval
)) {
1863 /* native [[Construct]] returning primitive is error */
1864 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1865 JSMSG_BAD_NEW_RESULT
,
1866 js_ValueToPrintableString(cx
, rval
));
1869 *vp
= OBJECT_TO_JSVAL(obj
);
1872 JS_RUNTIME_METER(cx
->runtime
, constructs
);
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
);
1887 if (!js_IsFunctionQName(cx
, JSVAL_TO_OBJECT(idval
), idp
))
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
)
1906 JSObject
*obj
, *parent
, *withobj
;
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]);
1916 obj
= js_ValueToNonNullObject(cx
, sp
[-1]);
1919 sp
[-1] = OBJECT_TO_JSVAL(obj
);
1922 parent
= js_GetScopeChain(cx
, fp
);
1926 OBJ_TO_INNER_OBJECT(cx
, obj
);
1930 withobj
= js_NewWithObject(cx
, obj
, parent
,
1931 sp
+ stackIndex
- StackBase(fp
));
1935 fp
->scopeChain
= withobj
;
1939 JS_STATIC_INTERPRET JS_REQUIRES_STACK
void
1940 js_LeaveWith(JSContext
*cx
)
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
)
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
) {
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
)
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
)
1985 fp
->blockChain
= obj
;
1988 obj
= fp
->scopeChain
;
1989 clasp
= js_IsActiveWithOrBlock(cx
, obj
, stackDepth
);
1992 if (clasp
== &js_BlockClass
) {
1993 /* Don't fail until after we've updated all stacks. */
1994 normalUnwind
&= js_PutBlockObject(cx
, normalUnwind
);
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
)
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
);
2016 d
= js_ValueToNumber(cx
, vp
);
2017 if (JSVAL_IS_NULL(*vp
))
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
) &&
2024 && !js_NewNumberInRootedValue(cx
, d
, vp
)) {
2029 (cs
->format
& JOF_INC
) ? d
++ : d
--;
2030 if (!js_NewNumberInRootedValue(cx
, d
, vp2
))
2033 if (!(cs
->format
& JOF_POST
))
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
);
2051 vp
= fp
->slots
+ fp
->script
->nfixed
;
2052 } else if (slot
< fp
->fun
->nargs
) {
2054 } else if (slot
== CALLEE_UPVAR_SLOT
) {
2058 slot
-= fp
->fun
->nargs
;
2059 JS_ASSERT(slot
< fp
->script
->nslots
);
2068 JS_STATIC_INTERPRET JS_REQUIRES_STACK
void
2069 js_TraceOpcode(JSContext
*cx
)
2074 intN ndefs
, n
, nuses
;
2079 tracefp
= (FILE *) cx
->tracefp
;
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.
2099 ndefs
< regs
->sp
- fp
->slots
) {
2100 for (n
= -ndefs
; n
< 0; n
++) {
2101 char *bytes
= js_DecompileValueGenerator(cx
, n
, regs
->sp
[n
],
2104 fprintf(tracefp
, "%s %s",
2105 (n
== -ndefs
) ? " output:" : ",",
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
);
2116 fputs("<null>", tracefp
);
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
,
2129 op
= (JSOp
) *regs
->pc
;
2130 nuses
= js_GetStackUses(&js_CodeSpec
[op
], op
, regs
->pc
);
2132 for (n
= -nuses
; n
< 0; n
++) {
2133 char *bytes
= js_DecompileValueGenerator(cx
, n
, regs
->sp
[n
],
2136 fprintf(tracefp
, "%s %s",
2137 (n
== -nuses
) ? " inputs:" : ",",
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. */
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
{
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
;
2198 const char *name
, *from
, *style
;
2200 uint32 total
, count
;
2201 uint32 i
, j
, nedges
;
2204 name
= getenv("JS_OPMETER_FILE");
2206 name
= "/tmp/ops.dot";
2207 fp
= fopen(name
, "w");
2214 for (i
= 0; i
< JSOP_LIMIT
; i
++) {
2215 for (j
= 0; j
< JSOP_LIMIT
; j
++) {
2216 count
= succeeds
[i
][j
];
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
;
2239 qsort(graph
, nedges
, sizeof(Edge
), compare_edges
);
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
);
2259 name
= getenv("JS_OPMETER_HIST");
2261 name
= "/tmp/ops.hist";
2262 fp
= fopen(name
, "w");
2267 fputs("bytecode", fp
);
2268 for (j
= 0; j
< HIST_NSLOTS
; j
++)
2269 fprintf(fp
, " slot %1u", (unsigned)j
);
2271 fputs("========", fp
);
2272 for (j
= 0; j
< HIST_NSLOTS
; j
++)
2273 fputs(" =======", 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
]);
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
2308 #define STORE_NUMBER(cx, n, d) \
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, ®s.sp[n])) \
2318 #define STORE_INT(cx, n, i) \
2320 if (INT_FITS_IN_JSVAL(i)) \
2321 regs.sp[n] = INT_TO_JSVAL(i); \
2322 else if (!js_NewDoubleInRootedValue(cx, (jsdouble) (i), ®s.sp[n])) \
2326 #define STORE_UINT(cx, n, u) \
2328 if ((u) <= JSVAL_INT_MAX) \
2329 regs.sp[n] = INT_TO_JSVAL(u); \
2330 else if (!js_NewDoubleInRootedValue(cx, (jsdouble) (u), ®s.sp[n])) \
2334 #define FETCH_NUMBER(cx, n, d) \
2338 v_ = FETCH_OPND(n); \
2339 VALUE_TO_NUMBER(cx, n, v_, d); \
2342 #define FETCH_INT(cx, n, i) \
2346 v_= FETCH_OPND(n); \
2347 if (JSVAL_IS_INT(v_)) { \
2348 i = JSVAL_TO_INT(v_); \
2350 i = js_ValueToECMAInt32(cx, ®s.sp[n]); \
2351 if (JSVAL_IS_NULL(regs.sp[n])) \
2356 #define FETCH_UINT(cx, n, ui) \
2360 v_= FETCH_OPND(n); \
2361 if (JSVAL_IS_INT(v_)) { \
2362 ui = (uint32) JSVAL_TO_INT(v_); \
2364 ui = js_ValueToECMAUint32(cx, ®s.sp[n]); \
2365 if (JSVAL_IS_NULL(regs.sp[n])) \
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) \
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); \
2382 d = js_ValueToNumber(cx, ®s.sp[n]); \
2383 if (JSVAL_IS_NULL(regs.sp[n])) \
2385 JS_ASSERT(JSVAL_IS_NUMBER(regs.sp[n]) || \
2386 regs.sp[n] == JSVAL_TRUE); \
2390 #define POP_BOOLEAN(cx, v, b) \
2392 v = FETCH_OPND(-1); \
2393 if (v == JSVAL_NULL) { \
2395 } else if (JSVAL_IS_BOOLEAN(v)) { \
2396 b = JSVAL_TO_BOOLEAN(v); \
2398 b = js_ValueToBoolean(v); \
2403 #define VALUE_TO_OBJECT(cx, n, v, obj) \
2405 if (!JSVAL_IS_PRIMITIVE(v)) { \
2406 obj = JSVAL_TO_OBJECT(v); \
2408 obj = js_ValueToNonNullObject(cx, v); \
2411 STORE_OPND(n, OBJECT_TO_JSVAL(obj)); \
2415 #define FETCH_OBJECT(cx, n, v, obj) \
2417 v = FETCH_OPND(n); \
2418 VALUE_TO_OBJECT(cx, n, v, obj); \
2421 #define DEFAULT_VALUE(cx, n, hint, v) \
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, ®s.sp[n])) \
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
2450 #ifdef DEBUG_NOT_THROWING
2451 # define ASSERT_NOT_THROWING(cx) JS_ASSERT(!(cx)->throwing)
2453 # define ASSERT_NOT_THROWING(cx) /* nothing */
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.
2467 # define METER_OP_INIT(op) /* nothing */
2468 # define METER_OP_PAIR(op1,op2) /* nothing */
2469 # define METER_SLOT_OP(op,slot) /* nothing */
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))
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 && ( \
2495 (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \
2496 __SUNPRO_C >= 0x570)
2497 # define JS_THREADED_INTERP 1
2499 # define JS_THREADED_INTERP 0
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) \
2511 if (!AssertValidPropertyCacheHit(cx, script, regs, pcoff, obj, pobj, \
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
;
2526 GET_ATOM_FROM_BYTECODE(script
, regs
.pc
, pcoff
, atom
);
2528 atom
= cx
->runtime
->atomState
.lengthAtom
;
2530 JSObject
*obj
, *pobj
;
2534 if (JOF_OPMODE(*regs
.pc
) == JOF_NAME
) {
2535 ok
= js_FindProperty(cx
, ATOM_TO_JSID(atom
), &obj
, &pobj
, &prop
);
2538 ok
= js_LookupProperty(cx
, obj
, ATOM_TO_JSID(atom
), &pobj
, &prop
);
2544 if (cx
->runtime
->gcNumber
!= sample
||
2545 PCVCAP_SHAPE(entry
->vcap
) != OBJ_SHAPE(pobj
)) {
2546 OBJ_DROP_PROPERTY(cx
, pobj
, 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
);
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
);
2574 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0)
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
);
2613 # define ABORT_RECORDING(cx, reason) \
2615 if (TRACE_RECORDER(cx)) \
2616 js_AbortRecording(cx, reason); \
2619 # define ABORT_RECORDING(cx, reason) ((void) 0)
2622 JS_REQUIRES_STACK JSBool
2623 js_Interpret(JSContext
*cx
)
2626 TraceVisStateObj
tvso(cx
, S_INTERP
);
2632 uintN inlineCallCount
;
2634 JSVersion currentVersion
, originalVersion
;
2636 JSObject
*obj
, *obj2
, *parent
;
2639 jsbytecode
*endpc
, *pc2
;
2643 uintN argc
, attrs
, flags
;
2645 jsval
*vp
, lval
, rval
, ltmp
, rtmp
;
2648 JSScopeProperty
*sprop
;
2649 JSString
*str
, *str2
;
2655 jsint low
, high
, off
, npairs
;
2657 #if JS_HAS_GETTER_SETTER
2658 JSPropertyOp getter
, setter
;
2660 JSAutoResolveFlags
rf(cx
, JSRESOLVE_INFER
);
2663 # define JS_EXTENSION __extension__
2664 # define JS_EXTENSION_(s) __extension__ ({ s; })
2666 # define JS_EXTENSION
2667 # define JS_EXTENSION_(s) s
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) && \
2688 js_TraceOpcode(cx); \
2691 # define TRACE_OPCODE(OP) ((void) 0)
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"
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"
2709 register void * const *jumpTable
= normalJumpTable
;
2711 METER_OP_INIT(op
); /* to nullify first METER_OP_PAIR */
2713 # define ENABLE_INTERRUPTS() ((void) (jumpTable = interruptJumpTable))
2716 # define CHECK_RECORDER() \
2717 JS_ASSERT_IF(TRACE_RECORDER(cx), jumpTable == interruptJumpTable)
2719 # define CHECK_RECORDER() ((void)0)
2722 # define DO_OP() JS_BEGIN_MACRO \
2724 JS_EXTENSION_(goto *jumpTable[op]); \
2726 # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
2727 METER_OP_PAIR(op, regs.pc[n]); \
2728 op = (JSOp) *(regs.pc += (n)); \
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; \
2740 # define END_EMPTY_CASES
2742 #else /* !JS_THREADED_INTERP */
2744 register intN switchMask
= 0;
2747 # define ENABLE_INTERRUPTS() ((void) (switchMask = -1))
2750 # define CHECK_RECORDER() \
2751 JS_ASSERT_IF(TRACE_RECORDER(cx), switchMask == -1)
2753 # define CHECK_RECORDER() ((void)0)
2756 # define DO_OP() goto do_op
2757 # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
2758 JS_ASSERT((n) == len); \
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 */
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. */
2790 if (tr
->wasDeepAborted())
2791 tr
->removeFragmentoReferences();
2793 tr
->pushAbortStack();
2797 /* Check for too deep of a native thread stack. */
2798 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
2802 /* Set registerized frame pointer and derived script pointer. */
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) \
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)]; \
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)
2842 #if JS_THREADED_INTERP
2843 #define MONITOR_BRANCH_TRACEVIS \
2845 if (jumpTable != interruptJumpTable) \
2846 js_EnterTraceVisState(cx, S_RECORD, R_NONE); \
2848 #else /* !JS_THREADED_INTERP */
2849 #define MONITOR_BRANCH_TRACEVIS \
2851 js_EnterTraceVisState(cx, S_RECORD, R_NONE); \
2855 #define MONITOR_BRANCH_TRACEVIS
2858 #define MONITOR_BRANCH() \
2860 if (TRACING_ENABLED(cx)) { \
2861 if (js_MonitorLoopEdge(cx, inlineCallCount)) { \
2862 JS_ASSERT(TRACE_RECORDER(cx)); \
2863 MONITOR_BRANCH_TRACEVIS; \
2864 ENABLE_INTERRUPTS(); \
2867 script = fp->script; \
2868 atoms = FrameAtomBase(cx, fp); \
2869 currentVersion = (JSVersion) script->version; \
2870 JS_ASSERT(fp->regs == ®s); \
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() \
2888 if (!JS_CHECK_OPERATION_LIMIT(cx)) \
2892 #ifndef TRACE_RECORDER
2893 #define TRACE_RECORDER(cx) (false)
2899 op = (JSOp) *regs.pc; \
2902 if (op == JSOP_NOP) { \
2903 if (TRACE_RECORDER(cx)) { \
2905 op = (JSOp) *regs.pc; \
2907 op = (JSOp) *++regs.pc; \
2909 } else if (op == JSOP_LOOP) { \
2911 op = (JSOp) *regs.pc; \
2917 MUST_FLOW_THROUGH("exit");
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
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
;
2941 # define CHECK_INTERRUPT_HANDLER() \
2943 if (cx->debugHooks->interruptHandler) \
2944 ENABLE_INTERRUPTS(); \
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
);
2958 /* Initialize the pc and sp registers unless we're resuming a generator. */
2959 if (JS_LIKELY(!fp
->regs
)) {
2961 ASSERT_NOT_THROWING(cx
);
2962 regs
.pc
= script
->code
;
2963 regs
.sp
= StackBase(fp
);
2965 #if JS_HAS_GENERATORS
2969 JS_ASSERT(fp
->flags
& JSFRAME_GENERATOR
);
2970 gen
= FRAME_TO_GENERATOR(fp
);
2971 JS_ASSERT(fp
->regs
== &gen
->savedRegs
);
2972 regs
= gen
->savedRegs
;
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.
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
);
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.
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.
3012 #else /* !JS_THREADED_INTERP */
3015 JS_ASSERT(js_CodeSpec
[op
].length
== 1);
3019 op
= (JSOp
) *regs
.pc
;
3024 switchOp
= intN(op
) | switchMask
;
3028 JS_ASSERT(switchMask
== -1);
3029 #endif /* !JS_THREADED_INTERP */
3031 bool moreInterrupts
= false;
3032 JSTrapHandler handler
= cx
->debugHooks
->interruptHandler
;
3035 if (TRACE_RECORDER(cx
))
3036 js_AbortRecording(cx
, "interrupt handler");
3038 switch (handler(cx
, script
, regs
.pc
, &rval
,
3039 cx
->debugHooks
->interruptHandlerData
)) {
3042 case JSTRAP_CONTINUE
:
3049 cx
->throwing
= JS_TRUE
;
3050 cx
->exception
= rval
;
3054 moreInterrupts
= true;
3058 TraceRecorder
* tr
= TRACE_RECORDER(cx
);
3060 JSRecordingStatus status
= TraceRecorder::monitorRecording(cx
, tr
, op
);
3063 moreInterrupts
= true;
3066 atoms
= COMMON_ATOMS_START(&rt
->atomState
);
3067 op
= JSOp(*regs
.pc
);
3068 DO_OP(); /* keep interrupting for op. */
3071 // The code at 'error:' aborts the recording.
3076 JS_NOT_REACHED("Bad recording status");
3079 #endif /* !JS_TRACER */
3081 #if JS_THREADED_INTERP
3083 if (!moreInterrupts
)
3084 js_ExitTraceVisState(cx
, R_ABORT
);
3086 jumpTable
= moreInterrupts
? interruptJumpTable
: normalJumpTable
;
3087 JS_EXTENSION_(goto *normalJumpTable
[op
]);
3089 switchMask
= moreInterrupts
? -1 : 0;
3090 switchOp
= intN(op
);
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
)
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
);
3113 BEGIN_CASE(JSOP_POP
)
3117 BEGIN_CASE(JSOP_POPN
)
3118 regs
.sp
-= GET_UINT16(regs
.pc
);
3120 JS_ASSERT(StackBase(fp
) <= regs
.sp
);
3121 obj
= fp
->blockChain
;
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
)
3129 if (obj
->getAssignedPrivate() != fp
)
3131 JS_ASSERT(StackBase(fp
) + OBJ_BLOCK_DEPTH(cx
, obj
)
3132 + ((clasp
== &js_BlockClass
)
3133 ? OBJ_BLOCK_COUNT(cx
, obj
)
3140 BEGIN_CASE(JSOP_SETRVAL
)
3141 BEGIN_CASE(JSOP_POPV
)
3142 ASSERT_NOT_THROWING(cx
);
3143 fp
->rval
= POP_OPND();
3146 BEGIN_CASE(JSOP_ENTERWITH
)
3147 if (!js_EnterWith(cx
, -1))
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
));
3166 END_CASE(JSOP_LEAVEWITH
)
3168 BEGIN_CASE(JSOP_RETURN
)
3169 fp
->rval
= POP_OPND();
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
);
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
);
3189 JS_ASSERT((uintN
)(regs
.sp
- fp
->slots
) <= script
->nslots
);
3190 regs
.pc
= fp
->imacpc
+ js_CodeSpec
[*fp
->imacpc
].length
;
3192 atoms
= script
->atomMap
.vector
;
3193 op
= JSOp(*regs
.pc
);
3197 JS_ASSERT(regs
.sp
== StackBase(fp
));
3198 if ((fp
->flags
& JSFRAME_CONSTRUCTING
) &&
3199 JSVAL_IS_PRIMITIVE(fp
->rval
)) {
3201 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3202 JSMSG_BAD_NEW_RESULT
,
3203 js_ValueToPrintableString(cx
, rval
));
3206 fp
->rval
= OBJECT_TO_JSVAL(fp
->thisp
);
3209 if (inlineCallCount
)
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
;
3222 JSInterpreterHook hook
;
3225 hook
= cx
->debugHooks
->callHook
;
3228 * Do not pass &ok directly as exposing the address
3229 * inhibits optimizations and uninitialised warnings.
3232 hook(cx
, fp
, JS_FALSE
, &status
, hookData
);
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
);
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
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
);
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. */
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
;
3302 BEGIN_CASE(JSOP_DEFAULT
)
3305 BEGIN_CASE(JSOP_GOTO
)
3306 len
= GET_JUMP_OFFSET(regs
.pc
);
3310 BEGIN_CASE(JSOP_IFEQ
)
3311 POP_BOOLEAN(cx
, rval
, cond
);
3312 if (cond
== JS_FALSE
) {
3313 len
= GET_JUMP_OFFSET(regs
.pc
);
3318 BEGIN_CASE(JSOP_IFNE
)
3319 POP_BOOLEAN(cx
, rval
, cond
);
3320 if (cond
!= JS_FALSE
) {
3321 len
= GET_JUMP_OFFSET(regs
.pc
);
3327 POP_BOOLEAN(cx
, rval
, cond
);
3328 if (cond
== JS_TRUE
) {
3329 len
= GET_JUMP_OFFSET(regs
.pc
);
3335 BEGIN_CASE(JSOP_AND
)
3336 POP_BOOLEAN(cx
, rval
, cond
);
3337 if (cond
== JS_FALSE
) {
3338 len
= GET_JUMP_OFFSET(regs
.pc
);
3344 BEGIN_CASE(JSOP_DEFAULTX
)
3347 BEGIN_CASE(JSOP_GOTOX
)
3348 len
= GET_JUMPX_OFFSET(regs
.pc
);
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
);
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
);
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
);
3377 BEGIN_CASE(JSOP_ANDX
)
3378 POP_BOOLEAN(cx
, rval
, cond
);
3379 if (cond
== JS_FALSE
) {
3380 len
= GET_JUMPX_OFFSET(regs
.pc
);
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
3392 #define FETCH_ELEMENT_ID(obj, n, id) \
3394 jsval idval_ = FETCH_OPND(n); \
3395 if (JSVAL_IS_INT(idval_)) { \
3396 id = INT_JSVAL_TO_JSID(idval_); \
3398 if (!js_InternNonIntElementId(cx, obj, idval_, &id)) \
3400 regs.sp[n] = ID_TO_VALUE(id); \
3404 #define TRY_BRANCH_AFTER_COND(cond,spdec) \
3407 JS_ASSERT(js_CodeSpec[op].length == 1); \
3408 diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ; \
3411 if (cond == (diff_ != 0)) { \
3413 len = GET_JUMP_OFFSET(regs.pc); \
3416 len = 1 + JSOP_IFEQ_LENGTH; \
3422 rval
= FETCH_OPND(-1);
3423 if (JSVAL_IS_PRIMITIVE(rval
)) {
3424 js_ReportValueError(cx
, JSMSG_IN_NOT_OBJECT
, -1, rval
, NULL
);
3427 obj
= JSVAL_TO_OBJECT(rval
);
3428 FETCH_ELEMENT_ID(obj
, -2, id
);
3429 if (!OBJ_LOOKUP_PROPERTY(cx
, obj
, id
, &obj2
, &prop
))
3431 cond
= prop
!= NULL
;
3433 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
3434 TRY_BRANCH_AFTER_COND(cond
, 2);
3436 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond
));
3439 BEGIN_CASE(JSOP_ITER
)
3440 JS_ASSERT(regs
.sp
> StackBase(fp
));
3442 if (!js_ValueToIterator(cx
, flags
, ®s
.sp
[-1]))
3444 CHECK_INTERRUPT_HANDLER();
3445 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs
.sp
[-1]));
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]), ®s
.sp
[-1]))
3454 CHECK_INTERRUPT_HANDLER();
3455 rval
= BOOLEAN_TO_JSVAL(regs
.sp
[-1] != JSVAL_HOLE
);
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]);
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
));
3488 id
= ATOM_TO_JSID(atom
);
3489 if (!js_FindProperty(cx
, id
, &obj
, &obj2
, &prop
))
3492 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
3493 ok
= OBJ_SET_PROPERTY(cx
, obj
, id
, ®s
.sp
[-1]);
3496 END_CASE(JSOP_FORNAME
)
3498 BEGIN_CASE(JSOP_FORPROP
)
3499 JS_ASSERT(regs
.sp
- 2 >= StackBase(fp
));
3501 id
= ATOM_TO_JSID(atom
);
3502 FETCH_OBJECT(cx
, -1, lval
, obj
);
3503 ok
= OBJ_SET_PROPERTY(cx
, obj
, id
, ®s
.sp
[-2]);
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);
3519 END_CASE(JSOP_FORELEM
)
3521 BEGIN_CASE(JSOP_DUP
)
3522 JS_ASSERT(regs
.sp
> StackBase(fp
));
3523 rval
= FETCH_OPND(-1);
3527 BEGIN_CASE(JSOP_DUP2
)
3528 JS_ASSERT(regs
.sp
- 2 >= StackBase(fp
));
3529 lval
= FETCH_OPND(-2);
3530 rval
= FETCH_OPND(-1);
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
);
3543 BEGIN_CASE(JSOP_PICK
)
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
);
3551 #define PROPERTY_OP(n, call) \
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. */ \
3561 #define ELEMENT_OP(n, call) \
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. */ \
3574 #define NATIVE_GET(cx,obj,pobj,sprop,vp) \
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) \
3584 if (!js_NativeGet(cx, obj, pobj, sprop, vp)) \
3589 #define NATIVE_SET(cx,obj,sprop,entry,vp) \
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); \
3597 if (!js_NativeSet(cx, obj, sprop, vp)) \
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) { \
3616 regs.pc += oplen + JSOP_POP_LENGTH; \
3617 op = (JSOp) *regs.pc; \
3621 #define END_SET_CASE(OP) \
3622 SKIP_POP_AFTER_SET(OP##_LENGTH, 1); \
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); \
3632 BEGIN_CASE(JSOP_SETCONST
)
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
|
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
|
3658 END_CASE(JSOP_ENUMCONSTELEM
)
3661 BEGIN_CASE(JSOP_BINDNAME
)
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
))
3684 if (JS_LIKELY(OBJ_IS_NATIVE(obj
))) {
3685 PROPERTY_CACHE_TEST(cx
, regs
.pc
, obj
, obj2
, entry
, atom
);
3687 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj
, obj2
, entry
);
3688 JS_UNLOCK_OBJ(cx
, obj2
);
3695 id
= ATOM_TO_JSID(atom
);
3696 obj
= js_FindIdentifierBase(cx
, fp
->scopeChain
, id
);
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
);
3708 #define BITWISE_OP(OP) \
3710 FETCH_INT(cx, -2, i); \
3711 FETCH_INT(cx, -1, j); \
3714 STORE_INT(cx, -1, i); \
3717 BEGIN_CASE(JSOP_BITOR
)
3719 END_CASE(JSOP_BITOR
)
3721 BEGIN_CASE(JSOP_BITXOR
)
3723 END_CASE(JSOP_BITXOR
)
3725 BEGIN_CASE(JSOP_BITAND
)
3727 END_CASE(JSOP_BITAND
)
3729 #define RELATIONAL_OP(OP) \
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); \
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; \
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); \
3753 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
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)) \
3771 if (!js_TestXMLEquality(cx, obj2, rval, &cond)) \
3773 cond = cond OP JS_TRUE; \
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)) \
3785 cond = cond OP JS_TRUE; \
3788 #define XML_EQUALITY_OP(OP) /* nothing */
3789 #define EXTENDED_EQUALITY_OP(OP) /* nothing */
3792 #define EQUALITY_OP(OP, IFNAN) \
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); \
3809 EXTENDED_EQUALITY_OP(OP) \
3810 /* Handle all undefined (=>NaN) and int combinations. */ \
3811 cond = lval OP rval; \
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)) { \
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; \
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); \
3839 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
3843 EQUALITY_OP(==, JS_FALSE
);
3847 EQUALITY_OP(!=, JS_TRUE
);
3850 #define STRICT_EQUALITY_OP(OP) \
3852 rval = FETCH_OPND(-1); \
3853 lval = FETCH_OPND(-2); \
3854 cond = js_StrictlyEqual(cx, lval, rval) OP JS_TRUE; \
3856 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
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(==);
3871 len
= GET_JUMP_OFFSET(regs
.pc
);
3877 BEGIN_CASE(JSOP_CASEX
)
3878 STRICT_EQUALITY_OP(==);
3881 len
= GET_JUMPX_OFFSET(regs
.pc
);
3885 END_CASE(JSOP_CASEX
)
3904 #undef RELATIONAL_OP
3906 #define SIGNED_SHIFT_OP(OP) \
3908 FETCH_INT(cx, -2, i); \
3909 FETCH_INT(cx, -1, j); \
3910 i = i OP (j & 31); \
3912 STORE_INT(cx, -1, i); \
3915 BEGIN_CASE(JSOP_LSH
)
3916 SIGNED_SHIFT_OP(<<);
3919 BEGIN_CASE(JSOP_RSH
)
3920 SIGNED_SHIFT_OP(>>);
3923 BEGIN_CASE(JSOP_URSH
)
3927 FETCH_UINT(cx
, -2, u
);
3928 FETCH_INT(cx
, -1, j
);
3931 STORE_UINT(cx
, -1, u
);
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
))
3948 STORE_OPND(-1, rval
);
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
)) {
3958 str
= JSVAL_TO_STRING(lval
);
3959 str2
= js_ValueToString(cx
, rval
);
3962 regs
.sp
[-1] = STRING_TO_JSVAL(str2
);
3964 str2
= JSVAL_TO_STRING(rval
);
3965 str
= js_ValueToString(cx
, lval
);
3968 regs
.sp
[-2] = STRING_TO_JSVAL(str
);
3970 str
= js_ConcatStrings(cx
, str
, str2
);
3974 STORE_OPND(-1, STRING_TO_JSVAL(str
));
3976 VALUE_TO_NUMBER(cx
, -2, lval
, d
);
3977 VALUE_TO_NUMBER(cx
, -1, rval
, d2
);
3980 STORE_NUMBER(cx
, -1, d
);
3985 #define BINARY_OP(OP) \
3987 FETCH_NUMBER(cx, -2, d); \
3988 FETCH_NUMBER(cx, -1, d2); \
3991 STORE_NUMBER(cx, -1, d); \
3994 BEGIN_CASE(JSOP_SUB
)
3998 BEGIN_CASE(JSOP_MUL
)
4002 BEGIN_CASE(JSOP_DIV
)
4003 FETCH_NUMBER(cx
, -1, d2
);
4004 FETCH_NUMBER(cx
, -2, d
);
4008 /* XXX MSVC miscompiles such that (NaN == 0) */
4009 if (JSDOUBLE_IS_NaN(d2
))
4010 rval
= DOUBLE_TO_JSVAL(rt
->jsNaN
);
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
);
4018 rval
= DOUBLE_TO_JSVAL(rt
->jsPositiveInfinity
);
4019 STORE_OPND(-1, rval
);
4022 STORE_NUMBER(cx
, -1, d
);
4026 BEGIN_CASE(JSOP_MOD
)
4027 FETCH_NUMBER(cx
, -1, d2
);
4028 FETCH_NUMBER(cx
, -2, d
);
4031 STORE_OPND(-1, DOUBLE_TO_JSVAL(rt
->jsNaN
));
4034 STORE_NUMBER(cx
, -1, d
);
4038 BEGIN_CASE(JSOP_NOT
)
4039 POP_BOOLEAN(cx
, rval
, cond
);
4040 PUSH_OPND(BOOLEAN_TO_JSVAL(!cond
));
4043 BEGIN_CASE(JSOP_BITNOT
)
4044 FETCH_INT(cx
, -1, 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
));
4061 JS_ASSERT(INT_FITS_IN_JSVAL(i
));
4062 regs
.sp
[-1] = INT_TO_JSVAL(i
);
4064 if (JSVAL_IS_DOUBLE(rval
)) {
4065 d
= *JSVAL_TO_DOUBLE(rval
);
4067 d
= js_ValueToNumber(cx
, ®s
.sp
[-1]);
4068 if (JSVAL_IS_NULL(regs
.sp
[-1]))
4070 JS_ASSERT(JSVAL_IS_NUMBER(regs
.sp
[-1]) ||
4071 regs
.sp
[-1] == JSVAL_TRUE
);
4075 * Negation of a zero doesn't produce a negative
4076 * zero on HPUX. Perform the operation by bit
4079 JSDOUBLE_HI32(d
) ^= JSDOUBLE_HI32_SIGNBIT
;
4083 if (!js_NewNumberInRootedValue(cx
, d
, ®s
.sp
[-1]))
4088 BEGIN_CASE(JSOP_POS
)
4089 rval
= FETCH_OPND(-1);
4090 if (!JSVAL_IS_NUMBER(rval
)) {
4091 d
= js_ValueToNumber(cx
, ®s
.sp
[-1]);
4093 if (JSVAL_IS_NULL(rval
))
4095 if (rval
== JSVAL_TRUE
) {
4096 if (!js_NewNumberInRootedValue(cx
, d
, ®s
.sp
[-1]))
4099 JS_ASSERT(JSVAL_IS_NUMBER(rval
));
4104 BEGIN_CASE(JSOP_DELNAME
)
4106 id
= ATOM_TO_JSID(atom
);
4107 if (!js_FindProperty(cx
, id
, &obj
, &obj2
, &prop
))
4110 /* ECMA says to return true if name is undefined or inherited. */
4111 PUSH_OPND(JSVAL_TRUE
);
4113 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
4114 if (!OBJ_DELETE_PROPERTY(cx
, obj
, id
, ®s
.sp
[-1]))
4117 END_CASE(JSOP_DELNAME
)
4119 BEGIN_CASE(JSOP_DELPROP
)
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
));
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
);
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.
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
)
4161 id
= ATOM_TO_JSID(atom
);
4165 FETCH_OBJECT(cx
, i
, lval
, obj
);
4167 FETCH_ELEMENT_ID(obj
, -1, id
);
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
);
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
))) {
4188 rval
+= (js_CodeSpec
[op
].format
& JOF_INC
) ? 2 : -2;
4189 if (!(js_CodeSpec
[op
].format
& JOF_POST
))
4191 LOCKED_OBJ_SET_SLOT(obj
, slot
, rval
);
4192 JS_UNLOCK_OBJ(cx
, obj
);
4194 len
= JSOP_INCNAME_LENGTH
;
4198 JS_UNLOCK_OBJ(cx
, obj2
);
4204 id
= ATOM_TO_JSID(atom
);
4205 if (!js_FindPropertyHelper(cx
, id
, true, &obj
, &obj2
, &prop
))
4208 goto atom_not_defined
;
4209 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
4214 const JSCodeSpec
*cs
;
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
, ®s
.sp
[-1]))
4225 cs
= &js_CodeSpec
[op
];
4226 JS_ASSERT(cs
->ndefs
== 1);
4227 JS_ASSERT((cs
->format
& JOF_TMPSLOT_MASK
) == JOF_TMPSLOT2
);
4229 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(v
))) {
4232 incr
= (cs
->format
& JOF_INC
) ? 2 : -2;
4233 if (cs
->format
& JOF_POST
) {
4234 regs
.sp
[-1] = v
+ incr
;
4239 fp
->flags
|= JSFRAME_ASSIGNING
;
4240 ok
= OBJ_SET_PROPERTY(cx
, obj
, id
, ®s
.sp
[-1]);
4241 fp
->flags
&= ~JSFRAME_ASSIGNING
;
4246 * We must set regs.sp[-1] to v for both post and pre increments
4247 * as the setter overwrites regs.sp[-1].
4251 /* We need an extra root for the result. */
4252 PUSH_OPND(JSVAL_NULL
);
4253 if (!js_DoIncDec(cx
, cs
, ®s
.sp
[-2], ®s
.sp
[-1]))
4255 fp
->flags
|= JSFRAME_ASSIGNING
;
4256 ok
= OBJ_SET_PROPERTY(cx
, obj
, id
, ®s
.sp
[-1]);
4257 fp
->flags
&= ~JSFRAME_ASSIGNING
;
4263 if (cs
->nuses
== 0) {
4264 /* regs.sp[-1] already contains the result of name increment. */
4267 regs
.sp
-= cs
->nuses
;
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;
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
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
;
4317 if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval
))) {
4319 JS_ASSERT(JSOP_INCARG_LENGTH
== js_CodeSpec
[op
].length
);
4320 SKIP_POP_AFTER_SET(JSOP_INCARG_LENGTH
, 0);
4321 PUSH_OPND(rval
+ incr2
);
4324 if (!js_DoIncDec(cx
, &js_CodeSpec
[op
], ®s
.sp
[-1], vp
))
4327 len
= JSOP_INCARG_LENGTH
;
4328 JS_ASSERT(len
== js_CodeSpec
[op
].length
);
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) \
4337 goto do_global_incop
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
4354 JS_ASSERT((js_CodeSpec
[op
].format
& JOF_TMPSLOT_MASK
) ==
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
)) {
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
);
4371 PUSH_OPND(JSVAL_NULL
); /* Extra root */
4372 if (!js_DoIncDec(cx
, &js_CodeSpec
[op
], ®s
.sp
[-2], ®s
.sp
[-1]))
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
);
4383 #define COMPUTE_THIS(cx, fp, obj) \
4385 if (!(obj = js_ComputeThisForFrame(cx, fp))) \
4389 BEGIN_CASE(JSOP_THIS
)
4390 COMPUTE_THIS(cx
, fp
, obj
);
4391 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
4394 BEGIN_CASE(JSOP_GETTHISPROP
)
4396 COMPUTE_THIS(cx
, fp
, obj
);
4398 goto do_getprop_with_obj
;
4402 BEGIN_CASE(JSOP_GETARGPROP
)
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
)
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
)
4421 lval
= FETCH_OPND(-1);
4423 do_getprop_with_lval
:
4424 VALUE_TO_OBJECT(cx
, -1, lval
, obj
);
4426 do_getprop_with_obj
:
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
);
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
);
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
);
4453 atom
= rt
->atomState
.lengthAtom
;
4457 id
= ATOM_TO_JSID(atom
);
4459 ? !js_GetPropertyHelper(cx
, obj
, id
, true, &rval
)
4460 : !OBJ_GET_PROPERTY(cx
, obj
, id
, &rval
)) {
4465 STORE_OPND(-1, rval
);
4466 JS_ASSERT(JSOP_GETPROP_LENGTH
+ i
== js_CodeSpec
[op
].length
);
4467 len
= JSOP_GETPROP_LENGTH
+ i
;
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
))) {
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
,
4493 goto do_getprop_with_lval
;
4495 END_CASE(JSOP_LENGTH
)
4497 BEGIN_CASE(JSOP_CALLPROP
)
4500 JSPropCacheEntry
*entry
;
4502 lval
= FETCH_OPND(-1);
4503 if (!JSVAL_IS_PRIMITIVE(lval
)) {
4504 obj
= JSVAL_TO_OBJECT(lval
);
4506 if (JSVAL_IS_STRING(lval
)) {
4508 } else if (JSVAL_IS_NUMBER(lval
)) {
4510 } else if (JSVAL_IS_BOOLEAN(lval
)) {
4511 i
= JSProto_Boolean
;
4513 JS_ASSERT(JSVAL_IS_NULL(lval
) || JSVAL_IS_VOID(lval
));
4514 js_ReportIsNullOrUndefined(cx
, -1, lval
, NULL
);
4518 if (!js_GetClassPrototype(cx
, NULL
, INT_TO_JSID(i
), &obj
))
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
);
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
);
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
);
4549 * Cache miss: use the immediate atom that was loaded for us under
4550 * PROPERTY_CACHE_TEST.
4552 id
= ATOM_TO_JSID(atom
);
4554 if (!JSVAL_IS_PRIMITIVE(lval
)) {
4555 if (!js_GetMethod(cx
, obj
, id
, !!entry
, &rval
))
4557 STORE_OPND(-1, OBJECT_TO_JSVAL(obj
));
4558 STORE_OPND(-2, rval
);
4560 JS_ASSERT(obj
->map
->ops
->getProperty
== js_GetProperty
);
4561 if (!js_GetPropertyHelper(cx
, obj
, id
, true, &rval
))
4563 STORE_OPND(-1, lval
);
4564 STORE_OPND(-2, rval
);
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
, ®s
.sp
[-1]))
4579 #if JS_HAS_NO_SUCH_METHOD
4580 if (JS_UNLIKELY(JSVAL_IS_VOID(rval
))) {
4582 regs
.sp
[-2] = ATOM_KEY(atom
);
4583 if (!js_OnUnknownMethod(cx
, regs
.sp
- 2))
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
);
4598 JSPropCacheEntry
*entry
;
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};
4615 * or similar real-world cases, which evolve a newborn
4616 * native object predicatably through some bounded number
4617 * of property additions. And second:
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
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
);
4669 !(sprop
->attrs
& JSPROP_SHARED
) &&
4670 sprop
->parent
== scope
->lastProp
&&
4671 !scope
->hadMiddleDelete();
4673 scope
= js_GetMutableScope(cx
, obj
);
4675 JS_UNLOCK_OBJ(cx
, obj
);
4678 checkForAdd
= !sprop
->parent
;
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
) {
4710 if (!js_AllocSlot(cx
, obj
, &slot
)) {
4711 JS_UNLOCK_SCOPE(cx
, scope
);
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
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
,
4732 sprop
->flags
, sprop
->shortid
);
4734 js_FreeSlot(cx
, obj
, slot
);
4735 JS_UNLOCK_SCOPE(cx
, scope
);
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
);
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
);
4764 JS_UNLOCK_SCOPE(cx
, scope
);
4765 PCMETER(cache
->setpcmisses
++);
4769 atom
= js_FullTestPropertyCache(cx
, regs
.pc
, &obj
, &obj2
,
4772 PCMETER(cache
->misses
++);
4773 PCMETER(cache
->setmisses
++);
4775 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj
, obj2
, entry
);
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
);
4792 id
= ATOM_TO_JSID(atom
);
4794 if (!js_SetPropertyHelper(cx
, obj
, id
, true, &rval
))
4797 if (!OBJ_SET_PROPERTY(cx
, obj
, id
, &rval
))
4799 ABORT_RECORDING(cx
, "Non-native set");
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
);
4815 rval
= STRING_TO_JSVAL(str
);
4820 VALUE_TO_OBJECT(cx
, -2, lval
, obj
);
4821 if (JSVAL_IS_INT(rval
)) {
4822 if (OBJ_IS_DENSE_ARRAY(cx
, obj
)) {
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
)
4833 /* Reload rval from the stack in the rare hole case. */
4834 rval
= FETCH_OPND(-1);
4837 id
= INT_JSVAL_TO_JSID(rval
);
4839 if (!js_InternNonIntElementId(cx
, obj
, rval
, &id
))
4843 if (!OBJ_GET_PROPERTY(cx
, obj
, id
, &rval
))
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))
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
);
4871 if (OBJ_IS_DENSE_ARRAY(cx
, obj
) && JSID_IS_INT(id
)) {
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
))
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
;
4889 if (!OBJ_SET_PROPERTY(cx
, obj
, id
, &rval
))
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
))
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.
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
),
4928 obj2
= js_NewObject(cx
, &js_ObjectClass
,
4929 JSVAL_IS_OBJECT(rval
)
4930 ? JSVAL_TO_OBJECT(rval
)
4932 OBJ_GET_PARENT(cx
, obj
));
4935 vp
[1] = OBJECT_TO_JSVAL(obj2
);
4936 flags
= JSFRAME_CONSTRUCTING
;
4941 if (!js_InvokeConstructor(cx
, argc
, JS_FALSE
, vp
))
4944 CHECK_INTERRUPT_HANDLER();
4945 TRACE_0(NativeCallComplete
);
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);
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. */
4961 if (FUN_INTERPRETED(fun
))
4964 uintN nframeslots
, nvars
, missing
;
4969 JSInlineFrame
*newifp
;
4970 JSInterpreterHook hook
;
4972 /* Restrict recursion of lightweight functions. */
4973 if (inlineCallCount
== MAX_INLINE_CALL_COUNT
) {
4974 js_ReportOverRecursed(cx
);
4978 /* Compute the total number of stack slots needed by fun. */
4979 nframeslots
= JS_HOWMANY(sizeof(JSInlineFrame
),
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
) {
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
;
4998 *--argsp
= JSVAL_VOID
;
4999 } while (argsp
!= regs
.sp
);
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
;
5011 JS_ASSERT(missing
== 0);
5013 JS_ARENA_ALLOCATE_CAST(newsp
, jsval
*, &cx
->stackPool
,
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.
5025 memcpy(newsp
, vp
, (2 + argc
) * sizeof(jsval
));
5027 newsp
= vp
+ 2 + argc
;
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
;
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
;
5094 regs
.pc
= script
->code
;
5095 newifp
->frame
.regs
= ®s
;
5096 cx
->fp
= fp
= &newifp
->frame
;
5098 /* Call the debugger hook if present. */
5099 hook
= cx
->debugHooks
->callHook
;
5101 newifp
->hookData
= hook(cx
, &newifp
->frame
, JS_TRUE
, 0,
5102 cx
->debugHooks
->callHookData
);
5103 CHECK_INTERRUPT_HANDLER();
5105 newifp
->hookData
= NULL
;
5108 TRACE_0(EnterFrame
);
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
);
5123 /* Load first op and dispatch it (safe since JSOP_STOP). */
5124 op
= (JSOp
) *regs
.pc
;
5128 JS_ASSERT(fp
->regs
== ®s
);
5129 script
= fp
->script
;
5130 atoms
= script
->atomMap
.vector
;
5131 js_FreeRawStack(cx
, newmark
);
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);
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
);
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
;
5178 TRACE_0(NativeCallComplete
);
5183 ok
= js_Invoke(cx
, argc
, vp
, 0);
5185 CHECK_INTERRUPT_HANDLER();
5188 JS_RUNTIME_METER(rt
, nonInlineCalls
);
5189 TRACE_0(NativeCallComplete
);
5192 #if JS_HAS_LVALUE_RETURN
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
));
5212 STORE_OPND(-1, rval
);
5213 cx
->rval2set
= JS_FALSE
;
5215 #endif /* JS_HAS_LVALUE_RETURN */
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);
5224 CHECK_INTERRUPT_HANDLER();
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
);
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.
5241 regs
.pc
+= JSOP_SETCALL_LENGTH
+ JSOP_DELELEM_LENGTH
;
5242 op
= (JSOp
) *regs
.pc
;
5245 PUSH_OPND(cx
->rval2
);
5246 cx
->rval2set
= JS_FALSE
;
5247 END_CASE(JSOP_SETCALL
)
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
);
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
);
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
);
5274 JS_ASSERT(PCVAL_IS_SPROP(entry
->vword
));
5275 sprop
= PCVAL_TO_SPROP(entry
->vword
);
5282 id
= ATOM_TO_JSID(atom
);
5283 if (!js_FindPropertyHelper(cx
, id
, true, &obj
, &obj2
, &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
;
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
))
5303 sprop
= (JSScopeProperty
*)prop
;
5305 NATIVE_GET(cx
, obj
, obj2
, sprop
, &rval
);
5306 OBJ_DROP_PROPERTY(cx
, obj2
, (JSProperty
*) sprop
);
5311 if (op
== JSOP_CALLNAME
)
5312 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
5316 BEGIN_CASE(JSOP_UINT16
)
5317 i
= (jsint
) GET_UINT16(regs
.pc
);
5318 rval
= INT_TO_JSVAL(i
);
5320 END_CASE(JSOP_UINT16
)
5322 BEGIN_CASE(JSOP_UINT24
)
5323 i
= (jsint
) GET_UINT24(regs
.pc
);
5324 rval
= INT_TO_JSVAL(i
);
5326 END_CASE(JSOP_UINT24
)
5328 BEGIN_CASE(JSOP_INT8
)
5329 i
= GET_INT8(regs
.pc
);
5330 rval
= INT_TO_JSVAL(i
);
5334 BEGIN_CASE(JSOP_INT32
)
5335 i
= GET_INT32(regs
.pc
);
5336 rval
= INT_TO_JSVAL(i
);
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
)
5362 PUSH_OPND(ATOM_KEY(atom
));
5363 END_CASE(JSOP_DOUBLE
)
5365 BEGIN_CASE(JSOP_OBJECT
)
5367 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
5368 END_CASE(JSOP_OBJECT
)
5370 BEGIN_CASE(JSOP_REGEXP
)
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
);
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
))
5415 if (JSVAL_IS_VOID(rval
))
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
];
5429 funobj
= NULL
; /* suppress bogus gcc warnings */
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
)
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
);
5469 rval
= OBJECT_TO_JSVAL(obj
);
5471 /* Store the regexp object value in its cloneIndex slot. */
5473 if (!JS_SetReservedSlot(cx
, funobj
, slot
, rval
))
5476 fp
->slots
[slot
] = rval
;
5482 END_CASE(JSOP_REGEXP
)
5484 BEGIN_CASE(JSOP_ZERO
)
5485 PUSH_OPND(JSVAL_ZERO
);
5488 BEGIN_CASE(JSOP_ONE
)
5489 PUSH_OPND(JSVAL_ONE
);
5492 BEGIN_CASE(JSOP_NULL
)
5493 PUSH_OPND(JSVAL_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
);
5504 BEGIN_CASE(JSOP_TABLESWITCH
)
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.)
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. */
5523 pc2
+= JUMP_OFFSET_LEN
;
5524 low
= GET_JUMP_OFFSET(pc2
);
5525 pc2
+= JUMP_OFFSET_LEN
;
5526 high
= GET_JUMP_OFFSET(pc2
);
5529 if ((jsuint
)i
< (jsuint
)(high
- low
+ 1)) {
5530 pc2
+= JUMP_OFFSET_LEN
+ JUMP_OFFSET_LEN
* i
;
5531 off
= (jsint
) GET_JUMP_OFFSET(pc2
);
5537 BEGIN_CASE(JSOP_TABLESWITCHX
)
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.)
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. */
5556 pc2
+= JUMPX_OFFSET_LEN
;
5557 low
= GET_JUMP_OFFSET(pc2
);
5558 pc2
+= JUMP_OFFSET_LEN
;
5559 high
= GET_JUMP_OFFSET(pc2
);
5562 if ((jsuint
)i
< (jsuint
)(high
- low
+ 1)) {
5563 pc2
+= JUMP_OFFSET_LEN
+ JUMPX_OFFSET_LEN
* i
;
5564 off
= (jsint
) GET_JUMPX_OFFSET(pc2
);
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
;
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
);
5586 if (!JSVAL_IS_NUMBER(lval
) &&
5587 !JSVAL_IS_STRING(lval
) &&
5588 !JSVAL_IS_BOOLEAN(lval
)) {
5589 goto end_lookup_switch
;
5593 npairs
= (jsint
) GET_UINT16(pc2
);
5595 JS_ASSERT(npairs
); /* empty switch uses JSOP_TABLESWITCH */
5597 #define SEARCH_PAIRS(MATCH_CODE) \
5599 JS_ASSERT(GET_INDEX(pc2) < script->atomMap.length); \
5600 atom = atoms[GET_INDEX(pc2)]; \
5601 rval = ATOM_KEY(atom); \
5607 if (--npairs == 0) { \
5612 if (JSVAL_IS_STRING(lval
)) {
5613 str
= JSVAL_TO_STRING(lval
);
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
);
5622 match
= (JSVAL_IS_DOUBLE(rval
) &&
5623 *JSVAL_TO_DOUBLE(rval
) == d
);
5627 match
= (lval
== rval
);
5633 len
= (op
== JSOP_LOOKUPSWITCH
)
5634 ? GET_JUMP_OFFSET(pc2
)
5635 : GET_JUMPX_OFFSET(pc2
);
5638 BEGIN_CASE(JSOP_TRAP
)
5640 JSTrapStatus status
;
5642 status
= JS_HandleTrap(cx
, script
, regs
.pc
, &rval
);
5651 cx
->throwing
= JS_TRUE
;
5652 cx
->exception
= rval
;
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
);
5665 BEGIN_CASE(JSOP_ARGUMENTS
)
5666 if (!js_GetArgsValue(cx
, fp
, &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
))
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
))
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
]);
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
)
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
);
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
);
5763 goto atom_not_defined
;
5765 /* Minimize footprint with generic code instead of NATIVE_GET. */
5766 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
5768 PUSH_OPND(JSVAL_NULL
);
5769 if (!OBJ_GET_PROPERTY(cx
, obj
, id
, vp
))
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
)
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
;
5803 slot
= JSVAL_TO_INT(lval
);
5804 rval
= OBJ_GET_SLOT(cx
, obj
, slot
);
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);
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.
5824 if (TRACE_RECORDER(cx
))
5825 js_AbortRecording(cx
, "SETGVAR with NULL slot");
5828 id
= ATOM_TO_JSID(atom
);
5829 if (!OBJ_SET_PROPERTY(cx
, obj
, id
, &rval
))
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
;
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
);
5859 if (!js_CheckRedeclaration(cx
, obj
, id
, attrs
, &obj2
, &prop
))
5862 /* Bind a variable only if it's not yet defined. */
5864 if (!OBJ_DEFINE_PROPERTY(cx
, obj
, id
, JSVAL_VOID
,
5865 JS_PropertyStub
, JS_PropertyStub
,
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.
5880 index
< GlobalVarCount(fp
) &&
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
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
;
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
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
;
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
;
5937 obj2
= js_GetScopeChain(cx
, fp
);
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
);
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
5972 attrs
= (fp
->flags
& JSFRAME_EVAL
)
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
);
5984 /* Function cannot be both getter a setter. */
5985 JS_ASSERT(flags
== JSPROP_GETTER
|| flags
== JSPROP_SETTER
);
5986 attrs
|= flags
| JSPROP_SHARED
;
5988 if (flags
== JSPROP_GETTER
)
5989 getter
= js_CastAsPropertyOp(obj
);
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
6000 parent
= fp
->varobj
;
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
);
6011 ok
= js_CheckRedeclaration(cx
, parent
, id
, attrs
, &pobj
, &prop
);
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
);
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
6039 JS_ASSERT(!(attrs
& ~(JSPROP_ENUMERATE
|JSPROP_PERMANENT
)));
6040 JS_ASSERT(!(old
& JSPROP_READONLY
));
6043 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
6046 ? OBJ_SET_PROPERTY(cx
, parent
, id
, &rval
)
6047 : OBJ_DEFINE_PROPERTY(cx
, parent
, id
, rval
, getter
, setter
,
6051 /* Restore fp->scopeChain now that obj is defined in fp->varobj. */
6052 fp
->scopeChain
= obj2
;
6054 cx
->weakRoots
.newborn
[GCX_OBJECT
] = NULL
;
6058 END_CASE(JSOP_DEFFUN
)
6060 BEGIN_CASE(JSOP_DEFFUN_FC
)
6061 BEGIN_CASE(JSOP_DEFFUN_DBGFC
)
6064 obj
= (op
== JSOP_DEFFUN_FC
)
6065 ? js_NewFlatClosure(cx
, fun
)
6066 : js_NewDebuggableFlatClosure(cx
, fun
);
6069 rval
= OBJECT_TO_JSVAL(obj
);
6071 attrs
= (fp
->flags
& JSFRAME_EVAL
)
6073 : JSPROP_ENUMERATE
| JSPROP_PERMANENT
;
6075 flags
= JSFUN_GSFLAG2ATTR(fun
->flags
);
6077 attrs
|= flags
| JSPROP_SHARED
;
6081 parent
= fp
->varobj
;
6084 id
= ATOM_TO_JSID(fun
->atom
);
6085 ok
= js_CheckRedeclaration(cx
, parent
, id
, attrs
, NULL
, NULL
);
6087 if (attrs
== JSPROP_ENUMERATE
) {
6088 JS_ASSERT(fp
->flags
& JSFRAME_EVAL
);
6089 ok
= OBJ_SET_PROPERTY(cx
, parent
, id
, &rval
);
6091 JS_ASSERT(attrs
& JSPROP_PERMANENT
);
6093 ok
= OBJ_DEFINE_PROPERTY(cx
, parent
, id
, rval
,
6094 (flags
& JSPROP_GETTER
)
6095 ? JS_EXTENSION (JSPropertyOp
) obj
6097 (flags
& JSPROP_SETTER
)
6098 ? JS_EXTENSION (JSPropertyOp
) obj
6106 cx
->weakRoots
.newborn
[GCX_OBJECT
] = NULL
;
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
);
6129 parent
= js_GetScopeChain(cx
, fp
);
6133 if (OBJ_GET_PARENT(cx
, obj
) != parent
) {
6135 if (TRACE_RECORDER(cx
))
6136 js_AbortRecording(cx
, "DEFLOCALFUN for closure");
6138 obj
= js_CloneFunctionObject(cx
, fun
, parent
);
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
);
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
);
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. */
6177 obj
= FUN_OBJECT(fun
);
6179 if (FUN_NULL_CLOSURE(fun
)) {
6180 obj
= js_CloneFunctionObject(cx
, fun
, fp
->scopeChain
);
6184 parent
= js_GetScopeChain(cx
, fp
);
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
);
6198 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
6199 END_CASE(JSOP_LAMBDA
)
6201 BEGIN_CASE(JSOP_LAMBDA_FC
)
6204 obj
= js_NewFlatClosure(cx
, fun
);
6208 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
6209 END_CASE(JSOP_LAMBDA_FC
)
6211 BEGIN_CASE(JSOP_LAMBDA_DBGFC
)
6214 obj
= js_NewDebuggableFlatClosure(cx
, fun
);
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
)
6229 op2
= (JSOp
) *++regs
.pc
;
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
;
6244 id
= ATOM_TO_JSID(atom
);
6245 rval
= FETCH_OPND(-1);
6250 rval
= FETCH_OPND(-1);
6254 FETCH_OBJECT(cx
, i
- 1, lval
, obj
);
6258 JS_ASSERT(regs
.sp
- StackBase(fp
) >= 2);
6259 rval
= FETCH_OPND(-1);
6262 id
= ATOM_TO_JSID(atom
);
6266 JS_ASSERT(op2
== JSOP_INITELEM
);
6268 JS_ASSERT(regs
.sp
- StackBase(fp
) >= 3);
6269 rval
= FETCH_OPND(-1);
6273 lval
= FETCH_OPND(i
-1);
6274 JS_ASSERT(JSVAL_IS_OBJECT(lval
));
6275 obj
= JSVAL_TO_OBJECT(lval
);
6279 /* Ensure that id has a type suitable for use with obj. */
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
,
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
))
6299 if (op
== JSOP_GETTER
) {
6300 getter
= JS_EXTENSION (JSPropertyOp
) JSVAL_TO_OBJECT(rval
);
6301 setter
= JS_PropertyStub
;
6302 attrs
= JSPROP_GETTER
;
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
))
6314 if (!OBJ_DEFINE_PROPERTY(cx
, obj
, id
, JSVAL_VOID
, getter
, setter
,
6320 if (js_CodeSpec
[op2
].ndefs
)
6321 STORE_OPND(-1, rval
);
6322 len
= js_CodeSpec
[op2
].length
;
6324 #endif /* JS_HAS_GETTER_SETTER */
6326 BEGIN_CASE(JSOP_HOLE
)
6327 PUSH_OPND(JSVAL_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
);
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
);
6348 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
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
));
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
);
6417 JS_UNLOCK_OBJ(cx
, obj
);
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
));
6439 JS_ASSERT(slot
== scope
->freeslot
);
6440 if (slot
< STOBJ_NSLOTS(obj
)) {
6443 if (!js_AllocSlot(cx
, obj
, &slot
)) {
6444 JS_UNLOCK_SCOPE(cx
, scope
);
6447 JS_ASSERT(slot
== sprop
->slot
);
6450 JS_ASSERT(!scope
->lastProp
||
6451 scope
->shape
== scope
->lastProp
->shape
);
6453 JSScopeProperty
*sprop2
=
6454 scope
->add(cx
, sprop
->id
,
6455 sprop
->getter
, sprop
->setter
,
6457 sprop
->flags
, sprop
->shortid
);
6459 js_FreeSlot(cx
, obj
, slot
);
6460 JS_UNLOCK_SCOPE(cx
, scope
);
6463 JS_ASSERT(sprop2
== sprop
);
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
);
6480 PCMETER(cache
->inipcmisses
++);
6481 JS_UNLOCK_SCOPE(cx
, scope
);
6483 /* Get the immediate property name into id. */
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
,
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
)))
6501 /* Common tail for property cache hit and miss cases. */
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
))
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))) {
6539 if (!OBJ_DEFINE_PROPERTY(cx
, obj
, id
, rval
, NULL
, NULL
, JSPROP_ENUMERATE
, NULL
))
6543 END_CASE(JSOP_INITELEM
)
6545 #if JS_HAS_SHARP_VARS
6546 BEGIN_CASE(JSOP_DEFSHARP
)
6547 obj
= fp
->sharpArray
;
6549 obj
= js_NewArrayObject(cx
, 0, NULL
);
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
)) {
6559 JS_snprintf(numBuf
, sizeof numBuf
, "%u", (unsigned) i
);
6560 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6561 JSMSG_BAD_SHARP_DEF
, numBuf
);
6564 if (!OBJ_DEFINE_PROPERTY(cx
, obj
, id
, rval
, NULL
, NULL
, JSPROP_ENUMERATE
, NULL
))
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
;
6575 if (!OBJ_GET_PROPERTY(cx
, obj
, id
, &rval
))
6578 if (!JSVAL_IS_OBJECT(rval
)) {
6581 JS_snprintf(numBuf
, sizeof numBuf
, "%u", (unsigned) i
);
6582 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6583 JSMSG_BAD_SHARP_USE
, numBuf
);
6587 END_CASE(JSOP_USESHARP
)
6588 #endif /* JS_HAS_SHARP_VARS */
6590 BEGIN_CASE(JSOP_GOSUB
)
6592 i
= (regs
.pc
- script
->main
) + JSOP_GOSUB_LENGTH
;
6593 PUSH(INT_TO_JSVAL(i
));
6594 len
= GET_JUMP_OFFSET(regs
.pc
);
6597 BEGIN_CASE(JSOP_GOSUBX
)
6599 i
= (regs
.pc
- script
->main
) + JSOP_GOSUBX_LENGTH
;
6600 len
= GET_JUMPX_OFFSET(regs
.pc
);
6601 PUSH(INT_TO_JSVAL(i
));
6604 BEGIN_CASE(JSOP_RETSUB
)
6605 /* Pop [exception or hole, retsub pc-index]. */
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
;
6620 JS_ASSERT(JSVAL_IS_INT(rval
));
6621 len
= JSVAL_TO_INT(rval
);
6622 regs
.pc
= script
->main
;
6625 BEGIN_CASE(JSOP_EXCEPTION
)
6626 JS_ASSERT(cx
->throwing
);
6627 PUSH(cx
->exception
);
6628 cx
->throwing
= JS_FALSE
;
6630 END_CASE(JSOP_EXCEPTION
)
6632 BEGIN_CASE(JSOP_FINALLY
)
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
);
6645 cx
->throwing
= JS_TRUE
;
6646 cx
->exception
= POP_OPND();
6647 /* let the code at error try to catch the exception. */
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
);
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
,
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
);
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
,
6705 lval
= FETCH_OPND(-2);
6707 if (!obj
->map
->ops
->hasInstance(cx
, obj
, lval
, &cond
))
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
;
6718 switch (handler(cx
, script
, regs
.pc
, &rval
,
6719 cx
->debugHooks
->debuggerHandlerData
)) {
6722 case JSTRAP_CONTINUE
:
6729 cx
->throwing
= JS_TRUE
;
6730 cx
->exception
= rval
;
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
)
6743 if (!js_SetDefaultXMLNamespace(cx
, rval
))
6745 END_CASE(JSOP_DEFXMLNS
)
6747 BEGIN_CASE(JSOP_ANYNAME
)
6748 if (!js_GetAnyName(cx
, &rval
))
6751 END_CASE(JSOP_ANYNAME
)
6753 BEGIN_CASE(JSOP_QNAMEPART
)
6755 PUSH_OPND(ATOM_KEY(atom
));
6756 END_CASE(JSOP_QNAMEPART
)
6758 BEGIN_CASE(JSOP_QNAMECONST
)
6760 rval
= ATOM_KEY(atom
);
6761 lval
= FETCH_OPND(-1);
6762 obj
= js_ConstructXMLQNameObject(cx
, lval
, rval
);
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
);
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
))
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
);
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
);
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
))
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
))
6821 rval
= FETCH_OPND(-1);
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
))
6831 if (!OBJ_GET_PROPERTY(cx
, obj
, id
, &rval
))
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
))
6845 if (op
== JSOP_DELDESC
) {
6846 regs
.sp
[-1] = rval
; /* set local root */
6847 if (!js_DeleteXMLListElements(cx
, JSVAL_TO_OBJECT(rval
)))
6849 rval
= JSVAL_TRUE
; /* always succeed */
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
);
6867 BEGIN_CASE(JSOP_ENDFILTER
)
6868 cond
= (regs
.sp
[-1] != JSVAL_HOLE
);
6870 /* Exit the "with" block left from the previous iteration. */
6873 if (!js_StepXMLListFilter(cx
, cond
))
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))
6884 len
= GET_JUMP_OFFSET(regs
.pc
);
6889 END_CASE(JSOP_ENDFILTER
);
6891 BEGIN_CASE(JSOP_TOXML
)
6892 rval
= FETCH_OPND(-1);
6893 obj
= js_ValueToXMLObject(cx
, rval
);
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
);
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
);
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
);
6920 str
= js_ValueToString(cx
, rval
);
6922 str
= js_EscapeElementValue(cx
, str
);
6926 STORE_OPND(-1, STRING_TO_JSVAL(str
));
6927 END_CASE(JSOP_XMLELTEXPR
)
6929 BEGIN_CASE(JSOP_XMLOBJECT
)
6931 obj
= js_CloneXMLObject(cx
, obj
);
6934 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
6935 END_CASE(JSOP_XMLOBJECT
)
6937 BEGIN_CASE(JSOP_XMLCDATA
)
6939 str
= ATOM_TO_STRING(atom
);
6940 obj
= js_NewXMLSpecialObject(cx
, JSXML_CLASS_TEXT
, NULL
, str
);
6943 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
6944 END_CASE(JSOP_XMLCDATA
)
6946 BEGIN_CASE(JSOP_XMLCOMMENT
)
6948 str
= ATOM_TO_STRING(atom
);
6949 obj
= js_NewXMLSpecialObject(cx
, JSXML_CLASS_COMMENT
, NULL
, str
);
6952 PUSH_OPND(OBJECT_TO_JSVAL(obj
));
6953 END_CASE(JSOP_XMLCOMMENT
)
6955 BEGIN_CASE(JSOP_XMLPI
)
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
,
6965 STORE_OPND(-1, OBJECT_TO_JSVAL(obj
));
6966 END_CASE(JSOP_XMLPI
)
6968 BEGIN_CASE(JSOP_GETFUNNS
)
6969 if (!js_GetFunctionNamespace(cx
, &rval
))
6972 END_CASE(JSOP_GETFUNNS
)
6973 #endif /* JS_HAS_XML_SUPPORT */
6975 BEGIN_CASE(JSOP_ENTERBLOCK
)
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
);
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
));
7006 while ((parent
= OBJ_GET_PARENT(cx
, parent
)) != youngestProto
)
7011 fp
->blockChain
= obj
;
7012 END_CASE(JSOP_ENTERBLOCK
)
7014 BEGIN_CASE(JSOP_LEAVEBLOCKEXPR
)
7015 BEGIN_CASE(JSOP_LEAVEBLOCK
)
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
));
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
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
))
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
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
);
7050 JS_ASSERT(StackBase(fp
) + blockDepth
== regs
.sp
);
7053 END_CASE(JSOP_LEAVEBLOCK
)
7055 BEGIN_CASE(JSOP_CALLBUILTIN
)
7057 obj
= js_GetBuiltinFunction(cx
, GET_INDEX(regs
.pc
));
7060 rval
= FETCH_OPND(-1);
7062 STORE_OPND(-2, OBJECT_TO_JSVAL(obj
));
7064 goto bad_opcode
; /* This is an imacro-only opcode. */
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
);
7075 JS_ASSERT(!fp
->callobj
&& !fp
->argsobj
);
7076 fp
->rval
= OBJECT_TO_JSVAL(obj
);
7078 if (inlineCallCount
!= 0)
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
);
7089 fp
->rval
= FETCH_OPND(-1);
7090 fp
->flags
|= JSFRAME_YIELDING
;
7091 regs
.pc
+= JSOP_YIELD_LENGTH
;
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
))
7105 END_CASE(JSOP_ARRAYPUSH
)
7106 #endif /* JS_HAS_GENERATORS */
7108 BEGIN_CASE(JSOP_LOOP
)
7111 #if JS_THREADED_INTERP
7113 L_JSOP_BACKPATCH_POP
:
7115 # if !JS_HAS_GENERATORS
7121 # if !JS_HAS_SHARP_VARS
7126 # if !JS_HAS_DESTRUCTURING
7127 L_JSOP_ENUMCONSTELEM
:
7130 # if !JS_HAS_XML_SUPPORT
7132 L_JSOP_STARTXMLEXPR
:
7161 #else /* !JS_THREADED_INTERP */
7169 JS_snprintf(numBuf
, sizeof numBuf
, "%d", op
);
7170 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
7171 JSMSG_BAD_BYTECODE
, numBuf
);
7175 #if !JS_THREADED_INTERP
7178 #endif /* !JS_THREADED_INTERP */
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
;
7193 // Handle other exceptions as if they came from the imacro-calling pc.
7194 regs
.pc
= fp
->imacpc
;
7196 atoms
= script
->atomMap
.vector
;
7199 JS_ASSERT((size_t)((fp
->imacpc
? fp
->imacpc
: regs
.pc
) - script
->code
) < script
->length
);
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");
7211 if (!cx
->throwing
) {
7212 /* This is an error, not a catchable exception, quit the frame ASAP. */
7215 JSTrapHandler handler
;
7216 JSTryNote
*tn
, *tnlimit
;
7219 /* Call debugger throw hook if set. */
7220 handler
= cx
->debugHooks
->throwHook
;
7222 switch (handler(cx
, script
, regs
.pc
, &rval
,
7223 cx
->debugHooks
->throwHookData
)) {
7225 cx
->throwing
= JS_FALSE
;
7228 cx
->throwing
= JS_FALSE
;
7233 cx
->exception
= rval
;
7234 case JSTRAP_CONTINUE
:
7237 CHECK_INTERRUPT_HANDLER();
7241 * Look for a try block in script that can catch this exception.
7243 if (script
->trynotesOffset
== 0)
7246 offset
= (uint32
)(regs
.pc
- script
->main
);
7247 tn
= JS_SCRIPT_TRYNOTES(script
)->vector
;
7248 tnlimit
= tn
+ JS_SCRIPT_TRYNOTES(script
)->length
;
7250 if (offset
- tn
->start
>= tn
->length
)
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
))
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
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
);
7286 * Restart the handler search with updated pc and stack depth
7287 * to properly notify the debugger.
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
))
7303 * Don't clear cx->throwing to save cx->exception from GC
7304 * until it is pushed to the stack via [exception] in the
7312 * Push (true, exception) pair for finally to indicate that
7313 * [retsub] should rethrow the exception.
7316 PUSH(cx
->exception
);
7317 cx
->throwing
= JS_FALSE
;
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]);
7336 cx
->throwing
= JS_TRUE
;
7337 cx
->exception
= regs
.sp
[1];
7339 } while (++tn
!= tnlimit
);
7343 * Propagate the exception or error to the caller unless the exception
7344 * is an asynchronous return from a generator.
7347 #if JS_HAS_GENERATORS
7348 if (JS_UNLIKELY(cx
->throwing
&& cx
->exception
== JSVAL_ARETURN
)) {
7349 cx
->throwing
= JS_FALSE
;
7351 fp
->rval
= JSVAL_VOID
;
7358 * Unwind the scope making sure that ok stays false even when UnwindScope
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
));
7368 cx
->tracePrevPc
= NULL
;
7371 if (inlineCallCount
)
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
7386 JS_ASSERT(inlineCallCount
== 0);
7387 JS_ASSERT(fp
->regs
== ®s
);
7389 if (TRACE_RECORDER(cx
))
7390 js_AbortRecording(cx
, "recording out of js_Interpret");
7392 #if JS_HAS_GENERATORS
7393 if (JS_UNLIKELY(fp
->flags
& JSFRAME_YIELDING
)) {
7396 gen
= FRAME_TO_GENERATOR(fp
);
7397 gen
->savedRegs
= regs
;
7398 gen
->frame
.regs
= &gen
->savedRegs
;
7400 #endif /* JS_HAS_GENERATORS */
7402 JS_ASSERT(!fp
->blockChain
);
7403 JS_ASSERT(!js_IsActiveWithOrBlock(cx
, fp
->scopeChain
, 0));
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
);
7416 SET_TRACE_RECORDER(cx
, tr
);
7417 if (!tr
->wasDeepAborted()) {
7418 tr
->popAbortStack();
7427 const char *printable
;
7429 printable
= js_AtomToPrintableString(cx
, atom
);
7431 js_ReportIsNotDefined(cx
, printable
);
7436 #endif /* !defined jsinvoke_cpp___ */