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

Annotation of /trunk/js/jsinterp.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (hide annotations)
Thu Oct 23 19:03:33 2008 UTC (10 years, 9 months ago) by siliconforks
File size: 265435 byte(s)
Add SpiderMonkey from Firefox 3.1b1.

The following directories and files were removed:
correct/, correct.js
liveconnect/
nanojit/
t/
v8/
vprof/
xpconnect/
all JavaScript files (Y.js, call.js, if.js, math-partial-sums.js, md5.js, perfect.js, trace-test.js, trace.js)


1 siliconforks 332 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2     * vim: set ts=8 sw=4 et tw=79:
3     *
4     * ***** BEGIN LICENSE BLOCK *****
5     * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6     *
7     * The contents of this file are subject to the Mozilla Public License Version
8     * 1.1 (the "License"); you may not use this file except in compliance with
9     * the License. You may obtain a copy of the License at
10     * http://www.mozilla.org/MPL/
11     *
12     * Software distributed under the License is distributed on an "AS IS" basis,
13     * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14     * for the specific language governing rights and limitations under the
15     * License.
16     *
17     * The Original Code is Mozilla Communicator client code, released
18     * March 31, 1998.
19     *
20     * The Initial Developer of the Original Code is
21     * Netscape Communications Corporation.
22     * Portions created by the Initial Developer are Copyright (C) 1998
23     * the Initial Developer. All Rights Reserved.
24     *
25     * Contributor(s):
26     *
27     * Alternatively, the contents of this file may be used under the terms of
28     * either of the GNU General Public License Version 2 or later (the "GPL"),
29     * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30     * in which case the provisions of the GPL or the LGPL are applicable instead
31     * of those above. If you wish to allow use of your version of this file only
32     * under the terms of either the GPL or the LGPL, and not to allow others to
33     * use your version of this file under the terms of the MPL, indicate your
34     * decision by deleting the provisions above and replace them with the notice
35     * and other provisions required by the GPL or the LGPL. If you do not delete
36     * the provisions above, a recipient may use your version of this file under
37     * the terms of any one of the MPL, the GPL or the LGPL.
38     *
39     * ***** END LICENSE BLOCK ***** */
40    
41     /*
42     * JavaScript bytecode interpreter.
43     */
44     #include "jsstddef.h"
45     #include <stdio.h>
46     #include <string.h>
47     #include <math.h>
48     #include "jstypes.h"
49     #include "jsarena.h" /* Added by JSIFY */
50     #include "jsutil.h" /* Added by JSIFY */
51     #include "jsprf.h"
52     #include "jsapi.h"
53     #include "jsarray.h"
54     #include "jsatom.h"
55     #include "jsbool.h"
56     #include "jscntxt.h"
57     #include "jsversion.h"
58     #include "jsdbgapi.h"
59     #include "jsfun.h"
60     #include "jsgc.h"
61     #include "jsinterp.h"
62     #include "jsiter.h"
63     #include "jslock.h"
64     #include "jsnum.h"
65     #include "jsobj.h"
66     #include "jsopcode.h"
67     #include "jsscan.h"
68     #include "jsscope.h"
69     #include "jsscript.h"
70     #include "jsstr.h"
71     #include "jsstaticcheck.h"
72     #include "jstracer.h"
73    
74     #ifdef INCLUDE_MOZILLA_DTRACE
75     #include "jsdtracef.h"
76     #endif
77    
78     #if JS_HAS_XML_SUPPORT
79     #include "jsxml.h"
80     #endif
81    
82     #include "jsautooplen.h"
83    
84     /* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */
85     #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___
86    
87     uint32
88     js_GenerateShape(JSContext *cx, JSBool gcLocked)
89     {
90     JSRuntime *rt;
91     uint32 shape;
92    
93     rt = cx->runtime;
94     shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);
95     JS_ASSERT(shape != 0);
96     if (shape & SHAPE_OVERFLOW_BIT) {
97     rt->gcPoke = JS_TRUE;
98     js_GC(cx, gcLocked ? GC_LOCK_HELD : GC_NORMAL);
99     shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);
100     JS_ASSERT(shape != 0);
101     JS_ASSERT_IF(shape & SHAPE_OVERFLOW_BIT,
102     JS_PROPERTY_CACHE(cx).disabled);
103     }
104     return shape;
105     }
106    
107     void
108     js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
109     uintN scopeIndex, uintN protoIndex,
110     JSObject *pobj, JSScopeProperty *sprop,
111     JSPropCacheEntry **entryp)
112     {
113     JSPropertyCache *cache;
114     jsbytecode *pc;
115     JSScope *scope;
116     JSOp op;
117     const JSCodeSpec *cs;
118     jsuword vword;
119     ptrdiff_t pcoff;
120     jsuword khash;
121     JSAtom *atom;
122     JSPropCacheEntry *entry;
123    
124     JS_ASSERT(!cx->runtime->gcRunning);
125     cache = &JS_PROPERTY_CACHE(cx);
126     pc = cx->fp->regs->pc;
127     if (cache->disabled || (cx->fp->flags & JSFRAME_EVAL)) {
128     PCMETER(cache->disfills++);
129     *entryp = NULL;
130     return;
131     }
132    
133     /*
134     * Check for fill from js_SetPropertyHelper where the setter removed sprop
135     * from pobj's scope (via unwatch or delete, e.g.).
136     */
137     scope = OBJ_SCOPE(pobj);
138     JS_ASSERT(scope->object == pobj);
139     if (!SCOPE_HAS_PROPERTY(scope, sprop)) {
140     PCMETER(cache->oddfills++);
141     *entryp = NULL;
142     return;
143     }
144    
145     /*
146     * Check for overdeep scope and prototype chain. Because resolve, getter,
147     * and setter hooks can change the prototype chain using JS_SetPrototype
148     * after js_LookupPropertyWithFlags has returned the nominal protoIndex,
149     * we have to validate protoIndex if it is non-zero. If it is zero, then
150     * we know thanks to the SCOPE_HAS_PROPERTY test above, and from the fact
151     * that obj == pobj, that protoIndex is invariant.
152     *
153     * The scopeIndex can't be wrong. We require JS_SetParent calls to happen
154     * before any running script might consult a parent-linked scope chain. If
155     * this requirement is not satisfied, the fill in progress will never hit,
156     * but vcap vs. scope shape tests ensure nothing malfunctions.
157     */
158     JS_ASSERT_IF(scopeIndex == 0 && protoIndex == 0, obj == pobj);
159     if (protoIndex != 0) {
160     JSObject *tmp;
161    
162     JS_ASSERT(pobj != obj);
163     protoIndex = 1;
164     tmp = obj;
165     for (;;) {
166     tmp = OBJ_GET_PROTO(cx, tmp);
167     if (!tmp) {
168     PCMETER(cache->noprotos++);
169     *entryp = NULL;
170     return;
171     }
172     if (tmp == pobj)
173     break;
174     ++protoIndex;
175     }
176     }
177     if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) {
178     PCMETER(cache->longchains++);
179     *entryp = NULL;
180     return;
181     }
182    
183     /*
184     * Optimize the cached vword based on our parameters and the current pc's
185     * opcode format flags.
186     */
187     op = (JSOp) *pc;
188     cs = &js_CodeSpec[op];
189    
190     do {
191     /*
192     * Check for a prototype "plain old method" callee computation. What
193     * is a plain old method? It's a function-valued property with stub
194     * getter and setter, so get of a function is idempotent and set is
195     * transparent.
196     */
197     if (cs->format & JOF_CALLOP) {
198     if (SPROP_HAS_STUB_GETTER(sprop) &&
199     SPROP_HAS_VALID_SLOT(sprop, scope)) {
200     jsval v;
201    
202     v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
203     if (VALUE_IS_FUNCTION(cx, v)) {
204     /*
205     * Great, we have a function-valued prototype property
206     * where the getter is JS_PropertyStub. The type id in
207     * pobj's scope does not evolve with changes to property
208     * values, however.
209     *
210     * So here, on first cache fill for this method, we brand
211     * the scope with a new shape and set the SCOPE_BRANDED
212     * flag. Once this scope flag is set, any write that adds
213     * or deletes a function-valued plain old property in
214     * scope->object will result in shape being regenerated.
215     */
216     if (!SCOPE_IS_BRANDED(scope)) {
217     PCMETER(cache->brandfills++);
218     #ifdef DEBUG_notme
219     fprintf(stderr,
220     "branding %p (%s) for funobj %p (%s), kshape %lu\n",
221     pobj, LOCKED_OBJ_GET_CLASS(pobj)->name,
222     JSVAL_TO_OBJECT(v),
223     JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx,
224     JSVAL_TO_OBJECT(v))),
225     kshape);
226     #endif
227     SCOPE_MAKE_UNIQUE_SHAPE(cx, scope);
228     SCOPE_SET_BRANDED(scope);
229     kshape = scope->shape;
230     }
231     vword = JSVAL_OBJECT_TO_PCVAL(v);
232     break;
233     }
234     }
235     }
236    
237     /* If getting a value via a stub getter, we can cache the slot. */
238     if (!(cs->format & JOF_SET) &&
239     SPROP_HAS_STUB_GETTER(sprop) &&
240     SPROP_HAS_VALID_SLOT(sprop, scope)) {
241     /* Great, let's cache sprop's slot and use it on cache hit. */
242     vword = SLOT_TO_PCVAL(sprop->slot);
243     } else {
244     /* Best we can do is to cache sprop (still a nice speedup). */
245     vword = SPROP_TO_PCVAL(sprop);
246     }
247     } while (0);
248    
249     /*
250     * Our caller preserved the scope shape prior to the js_GetPropertyHelper
251     * or similar call out of the interpreter. We want to cache under that
252     * shape if op is overtly mutating, to bias for the case where the mutator
253     * udpates shape predictably.
254     *
255     * Note that an apparently non-mutating op such as JSOP_NAME may still
256     * mutate the base object via, e.g., lazy standard class initialization,
257     * but that is a one-time event and we'll have to miss the old shape and
258     * re-fill under the new one.
259     */
260     if (!(cs->format & (JOF_SET | JOF_INCDEC)) && obj == pobj)
261     kshape = scope->shape;
262    
263     khash = PROPERTY_CACHE_HASH_PC(pc, kshape);
264     if (obj == pobj) {
265     JS_ASSERT(kshape != 0 || scope->shape != 0);
266     JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
267     JS_ASSERT(OBJ_SCOPE(obj)->object == obj);
268     } else {
269     if (op == JSOP_LENGTH) {
270     atom = cx->runtime->atomState.lengthAtom;
271     } else {
272     pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? 2 : 0;
273     GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
274     }
275     JS_ASSERT_IF(scopeIndex == 0,
276     protoIndex != 1 || OBJ_GET_PROTO(cx, obj) == pobj);
277     if (scopeIndex != 0 || protoIndex != 1) {
278     khash = PROPERTY_CACHE_HASH_ATOM(atom, obj, pobj);
279     PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1)
280     cache->pcrecycles++);
281     pc = (jsbytecode *) atom;
282     kshape = (jsuword) obj;
283     }
284     }
285    
286     entry = &cache->table[khash];
287     PCMETER(if (entry != *entryp) cache->modfills++);
288     PCMETER(if (!PCVAL_IS_NULL(entry->vword)) cache->recycles++);
289     entry->kpc = pc;
290     entry->kshape = kshape;
291     entry->vcap = PCVCAP_MAKE(scope->shape, scopeIndex, protoIndex);
292     entry->vword = vword;
293     *entryp = entry;
294    
295     cache->empty = JS_FALSE;
296     PCMETER(cache->fills++);
297     }
298    
299     JSAtom *
300     js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
301     JSObject **objp, JSObject **pobjp,
302     JSPropCacheEntry **entryp)
303     {
304     JSOp op;
305     const JSCodeSpec *cs;
306     ptrdiff_t pcoff;
307     JSAtom *atom;
308     JSObject *obj, *pobj, *tmp;
309     JSPropCacheEntry *entry;
310     uint32 vcap;
311    
312     JS_ASSERT(JS_UPTRDIFF(pc, cx->fp->script->code) < cx->fp->script->length);
313    
314     op = (JSOp) *pc;
315     cs = &js_CodeSpec[op];
316     if (op == JSOP_LENGTH) {
317     atom = cx->runtime->atomState.lengthAtom;
318     } else {
319     pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? 2 : 0;
320     GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
321     }
322    
323     obj = *objp;
324     JS_ASSERT(OBJ_IS_NATIVE(obj));
325     entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_ATOM(atom, obj, NULL)];
326     *entryp = entry;
327     vcap = entry->vcap;
328    
329     if (entry->kpc != (jsbytecode *) atom) {
330     PCMETER(JS_PROPERTY_CACHE(cx).idmisses++);
331    
332     #ifdef DEBUG_notme
333     entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_PC(pc, OBJ_SHAPE(obj))];
334     fprintf(stderr,
335     "id miss for %s from %s:%u"
336     " (pc %u, kpc %u, kshape %u, shape %u)\n",
337     js_AtomToPrintableString(cx, atom),
338     cx->fp->script->filename,
339     js_PCToLineNumber(cx, cx->fp->script, pc),
340     pc - cx->fp->script->code,
341     entry->kpc - cx->fp->script->code,
342     entry->kshape,
343     OBJ_SHAPE(obj));
344     js_Disassemble1(cx, cx->fp->script, pc,
345     PTRDIFF(pc, cx->fp->script->code, jsbytecode),
346     JS_FALSE, stderr);
347     #endif
348    
349     return atom;
350     }
351    
352     if (entry->kshape != (jsuword) obj) {
353     PCMETER(JS_PROPERTY_CACHE(cx).komisses++);
354     return atom;
355     }
356    
357     pobj = obj;
358     JS_LOCK_OBJ(cx, pobj);
359    
360     if (JOF_MODE(cs->format) == JOF_NAME) {
361     while (vcap & (PCVCAP_SCOPEMASK << PCVCAP_PROTOBITS)) {
362     tmp = LOCKED_OBJ_GET_PARENT(pobj);
363     if (!tmp || !OBJ_IS_NATIVE(tmp))
364     break;
365     JS_UNLOCK_OBJ(cx, pobj);
366     pobj = tmp;
367     JS_LOCK_OBJ(cx, pobj);
368     vcap -= PCVCAP_PROTOSIZE;
369     }
370    
371     *objp = pobj;
372     }
373    
374     while (vcap & PCVCAP_PROTOMASK) {
375     tmp = LOCKED_OBJ_GET_PROTO(pobj);
376     if (!tmp || !OBJ_IS_NATIVE(tmp))
377     break;
378     JS_UNLOCK_OBJ(cx, pobj);
379     pobj = tmp;
380     JS_LOCK_OBJ(cx, pobj);
381     --vcap;
382     }
383    
384     if (PCVCAP_SHAPE(vcap) == OBJ_SHAPE(pobj)) {
385     #ifdef DEBUG
386     jsid id = ATOM_TO_JSID(atom);
387    
388     CHECK_FOR_STRING_INDEX(id);
389     JS_ASSERT(SCOPE_GET_PROPERTY(OBJ_SCOPE(pobj), id));
390     JS_ASSERT(OBJ_SCOPE(pobj)->object == pobj);
391     #endif
392     *pobjp = pobj;
393     return NULL;
394     }
395    
396     PCMETER(JS_PROPERTY_CACHE(cx).vcmisses++);
397     JS_UNLOCK_OBJ(cx, pobj);
398     return atom;
399     }
400    
401     #ifdef DEBUG
402     #define ASSERT_CACHE_IS_EMPTY(cache) \
403     JS_BEGIN_MACRO \
404     JSPropertyCache *cache_ = (cache); \
405     uintN i_; \
406     JS_ASSERT(cache_->empty); \
407     for (i_ = 0; i_ < PROPERTY_CACHE_SIZE; i_++) { \
408     JS_ASSERT(!cache_->table[i_].kpc); \
409     JS_ASSERT(!cache_->table[i_].kshape); \
410     JS_ASSERT(!cache_->table[i_].vcap); \
411     JS_ASSERT(!cache_->table[i_].vword); \
412     } \
413     JS_END_MACRO
414     #else
415     #define ASSERT_CACHE_IS_EMPTY(cache) ((void)0)
416     #endif
417    
418     JS_STATIC_ASSERT(PCVAL_NULL == 0);
419    
420     void
421     js_FlushPropertyCache(JSContext *cx)
422     {
423     JSPropertyCache *cache;
424    
425     cache = &JS_PROPERTY_CACHE(cx);
426     if (cache->empty) {
427     ASSERT_CACHE_IS_EMPTY(cache);
428     return;
429     }
430    
431     memset(cache->table, 0, sizeof cache->table);
432     cache->empty = JS_TRUE;
433    
434     #ifdef JS_PROPERTY_CACHE_METERING
435     { static FILE *fp;
436     if (!fp)
437     fp = fopen("/tmp/propcache.stats", "w");
438     if (fp) {
439     fputs("Property cache stats for ", fp);
440     #ifdef JS_THREADSAFE
441     fprintf(fp, "thread %lu, ", (unsigned long) cx->thread->id);
442     #endif
443     fprintf(fp, "GC %u\n", cx->runtime->gcNumber);
444    
445     # define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)cache->mem)
446     P(fills);
447     P(nofills);
448     P(rofills);
449     P(disfills);
450     P(oddfills);
451     P(modfills);
452     P(brandfills);
453     P(noprotos);
454     P(longchains);
455     P(recycles);
456     P(pcrecycles);
457     P(tests);
458     P(pchits);
459     P(protopchits);
460     P(initests);
461     P(inipchits);
462     P(inipcmisses);
463     P(settests);
464     P(addpchits);
465     P(setpchits);
466     P(setpcmisses);
467     P(slotchanges);
468     P(setmisses);
469     P(idmisses);
470     P(komisses);
471     P(vcmisses);
472     P(misses);
473     P(flushes);
474     P(pcpurges);
475     # undef P
476    
477     fprintf(fp, "hit rates: pc %g%% (proto %g%%), set %g%%, ini %g%%, full %g%%\n",
478     (100. * cache->pchits) / cache->tests,
479     (100. * cache->protopchits) / cache->tests,
480     (100. * (cache->addpchits + cache->setpchits))
481     / cache->settests,
482     (100. * cache->inipchits) / cache->initests,
483     (100. * (cache->tests - cache->misses)) / cache->tests);
484     fflush(fp);
485     }
486     }
487     #endif
488    
489     PCMETER(cache->flushes++);
490     }
491    
492     void
493     js_FlushPropertyCacheForScript(JSContext *cx, JSScript *script)
494     {
495     JSPropertyCache *cache;
496     JSPropCacheEntry *entry;
497    
498     cache = &JS_PROPERTY_CACHE(cx);
499     for (entry = cache->table; entry < cache->table + PROPERTY_CACHE_SIZE;
500     entry++) {
501     if (JS_UPTRDIFF(entry->kpc, script->code) < script->length) {
502     entry->kpc = NULL;
503     entry->kshape = 0;
504     #ifdef DEBUG
505     entry->vcap = entry->vword = 0;
506     #endif
507     }
508     }
509     }
510    
511     void
512     js_DisablePropertyCache(JSContext *cx)
513     {
514     JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
515     ++JS_PROPERTY_CACHE(cx).disabled;
516     }
517    
518     void
519     js_EnablePropertyCache(JSContext *cx)
520     {
521     --JS_PROPERTY_CACHE(cx).disabled;
522     JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
523     }
524    
525     /*
526     * Check if the current arena has enough space to fit nslots after sp and, if
527     * so, reserve the necessary space.
528     */
529     static JSBool
530     AllocateAfterSP(JSContext *cx, jsval *sp, uintN nslots)
531     {
532     uintN surplus;
533     jsval *sp2;
534    
535     JS_ASSERT((jsval *) cx->stackPool.current->base <= sp);
536     JS_ASSERT(sp <= (jsval *) cx->stackPool.current->avail);
537     surplus = (jsval *) cx->stackPool.current->avail - sp;
538     if (nslots <= surplus)
539     return JS_TRUE;
540    
541     /*
542     * No room before current->avail, check if the arena has enough space to
543     * fit the missing slots before the limit.
544     */
545     if (nslots > (size_t) ((jsval *) cx->stackPool.current->limit - sp))
546     return JS_FALSE;
547    
548     JS_ARENA_ALLOCATE_CAST(sp2, jsval *, &cx->stackPool,
549     (nslots - surplus) * sizeof(jsval));
550     JS_ASSERT(sp2 == sp + surplus);
551     return JS_TRUE;
552     }
553    
554     JS_STATIC_INTERPRET jsval *
555     js_AllocRawStack(JSContext *cx, uintN nslots, void **markp)
556     {
557     jsval *sp;
558    
559     if (!cx->stackPool.first.next) {
560     int64 *timestamp;
561    
562     JS_ARENA_ALLOCATE_CAST(timestamp, int64 *,
563     &cx->stackPool, sizeof *timestamp);
564     if (!timestamp) {
565     js_ReportOutOfScriptQuota(cx);
566     return NULL;
567     }
568     *timestamp = JS_Now();
569     }
570    
571     if (markp)
572     *markp = JS_ARENA_MARK(&cx->stackPool);
573     JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval));
574     if (!sp)
575     js_ReportOutOfScriptQuota(cx);
576     return sp;
577     }
578    
579     JS_STATIC_INTERPRET void
580     js_FreeRawStack(JSContext *cx, void *mark)
581     {
582     JS_ARENA_RELEASE(&cx->stackPool, mark);
583     }
584    
585     JS_FRIEND_API(jsval *)
586     js_AllocStack(JSContext *cx, uintN nslots, void **markp)
587     {
588     jsval *sp;
589     JSArena *a;
590     JSStackHeader *sh;
591    
592     /* Callers don't check for zero nslots: we do to avoid empty segments. */
593     if (nslots == 0) {
594     *markp = NULL;
595     return (jsval *) JS_ARENA_MARK(&cx->stackPool);
596     }
597    
598     /* Allocate 2 extra slots for the stack segment header we'll likely need. */
599     sp = js_AllocRawStack(cx, 2 + nslots, markp);
600     if (!sp)
601     return NULL;
602    
603     /* Try to avoid another header if we can piggyback on the last segment. */
604     a = cx->stackPool.current;
605     sh = cx->stackHeaders;
606     if (sh && JS_STACK_SEGMENT(sh) + sh->nslots == sp) {
607     /* Extend the last stack segment, give back the 2 header slots. */
608     sh->nslots += nslots;
609     a->avail -= 2 * sizeof(jsval);
610     } else {
611     /*
612     * Need a new stack segment, so allocate and push a stack segment
613     * header from the 2 extra slots.
614     */
615     sh = (JSStackHeader *)sp;
616     sh->nslots = nslots;
617     sh->down = cx->stackHeaders;
618     cx->stackHeaders = sh;
619     sp += 2;
620     }
621    
622     /*
623     * Store JSVAL_NULL using memset, to let compilers optimize as they see
624     * fit, in case a caller allocates and pushes GC-things one by one, which
625     * could nest a last-ditch GC that will scan this segment.
626     */
627     memset(sp, 0, nslots * sizeof(jsval));
628     return sp;
629     }
630    
631     JS_FRIEND_API(void)
632     js_FreeStack(JSContext *cx, void *mark)
633     {
634     JSStackHeader *sh;
635     jsuword slotdiff;
636    
637     /* Check for zero nslots allocation special case. */
638     if (!mark)
639     return;
640    
641     /* We can assert because js_FreeStack always balances js_AllocStack. */
642     sh = cx->stackHeaders;
643     JS_ASSERT(sh);
644    
645     /* If mark is in the current segment, reduce sh->nslots, else pop sh. */
646     slotdiff = JS_UPTRDIFF(mark, JS_STACK_SEGMENT(sh)) / sizeof(jsval);
647     if (slotdiff < (jsuword)sh->nslots)
648     sh->nslots = slotdiff;
649     else
650     cx->stackHeaders = sh->down;
651    
652     /* Release the stackPool space allocated since mark was set. */
653     JS_ARENA_RELEASE(&cx->stackPool, mark);
654     }
655    
656     JSObject *
657     js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
658     {
659     JSObject *obj, *cursor, *clonedChild, *parent;
660     JSTempValueRooter tvr;
661    
662     obj = fp->blockChain;
663     if (!obj) {
664     /*
665     * Don't force a call object for a lightweight function call, but do
666     * insist that there is a call object for a heavyweight function call.
667     */
668     JS_ASSERT(!fp->fun ||
669     !(fp->fun->flags & JSFUN_HEAVYWEIGHT) ||
670     fp->callobj);
671     JS_ASSERT(fp->scopeChain);
672     return fp->scopeChain;
673     }
674    
675     /*
676     * We have one or more lexical scopes to reflect into fp->scopeChain, so
677     * make sure there's a call object at the current head of the scope chain,
678     * if this frame is a call frame.
679     */
680     if (fp->fun && !fp->callobj) {
681     JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass ||
682     OBJ_GET_PRIVATE(cx, fp->scopeChain) != fp);
683     if (!js_GetCallObject(cx, fp, fp->scopeChain))
684     return NULL;
685     }
686    
687     /*
688     * Clone the block chain. To avoid recursive cloning we set the parent of
689     * the cloned child after we clone the parent. In the following loop when
690     * clonedChild is null it indicates the first iteration when no special GC
691     * rooting is necessary. On the second and the following iterations we
692     * have to protect cloned so far chain against the GC during cloning of
693     * the cursor object.
694     */
695     cursor = obj;
696     clonedChild = NULL;
697     for (;;) {
698     parent = OBJ_GET_PARENT(cx, cursor);
699    
700     /*
701     * We pass fp->scopeChain and not null even if we override the parent
702     * slot later as null triggers useless calculations of slot's value in
703     * js_NewObject that js_CloneBlockObject calls.
704     */
705     cursor = js_CloneBlockObject(cx, cursor, fp->scopeChain, fp);
706     if (!cursor) {
707     if (clonedChild)
708     JS_POP_TEMP_ROOT(cx, &tvr);
709     return NULL;
710     }
711     if (!clonedChild) {
712     /*
713     * The first iteration. Check if other follow and root obj if so
714     * to protect the whole cloned chain against GC.
715     */
716     obj = cursor;
717     if (!parent)
718     break;
719     JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
720     } else {
721     /*
722     * Avoid OBJ_SET_PARENT overhead as clonedChild cannot escape to
723     * other threads.
724     */
725     STOBJ_SET_PARENT(clonedChild, cursor);
726     if (!parent) {
727     JS_ASSERT(tvr.u.value == OBJECT_TO_JSVAL(obj));
728     JS_POP_TEMP_ROOT(cx, &tvr);
729     break;
730     }
731     }
732     clonedChild = cursor;
733     cursor = parent;
734     }
735     fp->flags |= JSFRAME_POP_BLOCKS;
736     fp->scopeChain = obj;
737     fp->blockChain = NULL;
738     return obj;
739     }
740    
741     JSBool
742     js_GetPrimitiveThis(JSContext *cx, jsval *vp, JSClass *clasp, jsval *thisvp)
743     {
744     jsval v;
745     JSObject *obj;
746    
747     v = vp[1];
748     if (JSVAL_IS_OBJECT(v)) {
749     obj = JS_THIS_OBJECT(cx, vp);
750     if (!JS_InstanceOf(cx, obj, clasp, vp + 2))
751     return JS_FALSE;
752     v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
753     }
754     *thisvp = v;
755     return JS_TRUE;
756     }
757    
758     /*
759     * ECMA requires "the global object", but in embeddings such as the browser,
760     * which have multiple top-level objects (windows, frames, etc. in the DOM),
761     * we prefer fun's parent. An example that causes this code to run:
762     *
763     * // in window w1
764     * function f() { return this }
765     * function g() { return f }
766     *
767     * // in window w2
768     * var h = w1.g()
769     * alert(h() == w1)
770     *
771     * The alert should display "true".
772     */
773     JS_STATIC_INTERPRET JSObject *
774     js_ComputeGlobalThis(JSContext *cx, JSBool lazy, jsval *argv)
775     {
776     JSObject *thisp;
777    
778     if (JSVAL_IS_PRIMITIVE(argv[-2]) ||
779     !OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]))) {
780     thisp = cx->globalObject;
781     } else {
782     JSStackFrame *fp;
783     jsid id;
784     jsval v;
785     uintN attrs;
786     JSBool ok;
787     JSObject *parent;
788    
789     /*
790     * Walk up the parent chain, first checking that the running script
791     * has access to the callee's parent object. Note that if lazy, the
792     * running script whose principals we want to check is the script
793     * associated with fp->down, not with fp.
794     *
795     * FIXME: 417851 -- this access check should not be required, as it
796     * imposes a performance penalty on all js_ComputeGlobalThis calls,
797     * and it represents a maintenance hazard.
798     */
799     fp = cx->fp; /* quell GCC overwarning */
800     if (lazy) {
801     JS_ASSERT(fp->argv == argv);
802     fp->dormantNext = cx->dormantFrameChain;
803     cx->dormantFrameChain = fp;
804     cx->fp = fp->down;
805     fp->down = NULL;
806     }
807     thisp = JSVAL_TO_OBJECT(argv[-2]);
808     id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
809    
810     ok = OBJ_CHECK_ACCESS(cx, thisp, id, JSACC_PARENT, &v, &attrs);
811     if (lazy) {
812     cx->dormantFrameChain = fp->dormantNext;
813     fp->dormantNext = NULL;
814     fp->down = cx->fp;
815     cx->fp = fp;
816     }
817     if (!ok)
818     return NULL;
819    
820     thisp = JSVAL_IS_VOID(v)
821     ? OBJ_GET_PARENT(cx, thisp)
822     : JSVAL_TO_OBJECT(v);
823     while ((parent = OBJ_GET_PARENT(cx, thisp)) != NULL)
824     thisp = parent;
825     }
826    
827     OBJ_TO_OUTER_OBJECT(cx, thisp);
828     if (!thisp)
829     return NULL;
830     argv[-1] = OBJECT_TO_JSVAL(thisp);
831     return thisp;
832     }
833    
834     static JSObject *
835     ComputeThis(JSContext *cx, JSBool lazy, jsval *argv)
836     {
837     JSObject *thisp;
838    
839     JS_ASSERT(!JSVAL_IS_NULL(argv[-1]));
840     if (!JSVAL_IS_OBJECT(argv[-1])) {
841     if (!js_PrimitiveToObject(cx, &argv[-1]))
842     return NULL;
843     thisp = JSVAL_TO_OBJECT(argv[-1]);
844     } else {
845     thisp = JSVAL_TO_OBJECT(argv[-1]);
846     if (OBJ_GET_CLASS(cx, thisp) == &js_CallClass)
847     return js_ComputeGlobalThis(cx, lazy, argv);
848    
849     if (thisp->map->ops->thisObject) {
850     /* Some objects (e.g., With) delegate 'this' to another object. */
851     thisp = thisp->map->ops->thisObject(cx, thisp);
852     if (!thisp)
853     return NULL;
854     }
855     OBJ_TO_OUTER_OBJECT(cx, thisp);
856     if (!thisp)
857     return NULL;
858     argv[-1] = OBJECT_TO_JSVAL(thisp);
859     }
860     return thisp;
861     }
862    
863     JSObject *
864     js_ComputeThis(JSContext *cx, JSBool lazy, jsval *argv)
865     {
866     if (JSVAL_IS_NULL(argv[-1]))
867     return js_ComputeGlobalThis(cx, lazy, argv);
868     return ComputeThis(cx, lazy, argv);
869     }
870    
871     #if JS_HAS_NO_SUCH_METHOD
872    
873     #define JSSLOT_FOUND_FUNCTION JSSLOT_PRIVATE
874     #define JSSLOT_SAVED_ID (JSSLOT_PRIVATE + 1)
875    
876     JSClass js_NoSuchMethodClass = {
877     "NoSuchMethod",
878     JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS |
879     JSCLASS_HAS_CACHED_PROTO(JSProto_NoSuchMethod),
880     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
881     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
882     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
883     };
884    
885     JS_BEGIN_EXTERN_C
886    
887     JSObject*
888     js_InitNoSuchMethodClass(JSContext *cx, JSObject* obj);
889    
890     JS_END_EXTERN_C
891    
892     JSObject*
893     js_InitNoSuchMethodClass(JSContext *cx, JSObject* obj)
894     {
895     JSObject *proto;
896    
897     proto = JS_InitClass(cx, obj, NULL, &js_NoSuchMethodClass, NULL, 0, NULL,
898     NULL, NULL, NULL);
899     if (!proto)
900     return NULL;
901    
902     OBJ_CLEAR_PROTO(cx, proto);
903     return proto;
904     }
905    
906     /*
907     * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of
908     * the base object, we search for the __noSuchMethod__ method in the base.
909     * If it exists, we store the method and the property's id into an object of
910     * NoSuchMethod class and store this object into the callee's stack slot.
911     * Later, js_Invoke will recognise such an object and transfer control to
912     * NoSuchMethod that invokes the method like:
913     *
914     * this.__noSuchMethod__(id, args)
915     *
916     * where id is the name of the method that this invocation attempted to
917     * call by name, and args is an Array containing this invocation's actual
918     * parameters.
919     */
920     JS_STATIC_INTERPRET JSBool
921     js_OnUnknownMethod(JSContext *cx, jsval *vp)
922     {
923     JSObject *obj;
924     jsid id;
925     JSTempValueRooter tvr;
926     JSBool ok;
927    
928     JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
929     obj = JSVAL_TO_OBJECT(vp[1]);
930     JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
931    
932     MUST_FLOW_THROUGH("out");
933     id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
934     #if JS_HAS_XML_SUPPORT
935     if (OBJECT_IS_XML(cx, obj)) {
936     JSXMLObjectOps *ops;
937    
938     ops = (JSXMLObjectOps *) obj->map->ops;
939     obj = ops->getMethod(cx, obj, id, &tvr.u.value);
940     if (!obj) {
941     ok = JS_FALSE;
942     goto out;
943     }
944     vp[1] = OBJECT_TO_JSVAL(obj);
945     } else
946     #endif
947     {
948     ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value);
949     if (!ok)
950     goto out;
951     }
952     if (JSVAL_IS_PRIMITIVE(tvr.u.value)) {
953     vp[0] = tvr.u.value;
954     } else {
955     #if JS_HAS_XML_SUPPORT
956     /* Extract the function name from function::name qname. */
957     if (!JSVAL_IS_PRIMITIVE(vp[0])) {
958     obj = JSVAL_TO_OBJECT(vp[0]);
959     ok = js_IsFunctionQName(cx, obj, &id);
960     if (!ok)
961     goto out;
962     if (id != 0)
963     vp[0] = ID_TO_VALUE(id);
964     }
965     #endif
966     obj = js_NewObject(cx, &js_NoSuchMethodClass, NULL, NULL, 0);
967     if (!obj) {
968     ok = JS_FALSE;
969     goto out;
970     }
971     obj->fslots[JSSLOT_FOUND_FUNCTION] = tvr.u.value;
972     obj->fslots[JSSLOT_SAVED_ID] = vp[0];
973     vp[0] = OBJECT_TO_JSVAL(obj);
974     }
975     ok = JS_TRUE;
976    
977     out:
978     JS_POP_TEMP_ROOT(cx, &tvr);
979     return ok;
980     }
981    
982     static JSBool
983     NoSuchMethod(JSContext *cx, uintN argc, jsval *vp, uint32 flags)
984     {
985     jsval *invokevp;
986     void *mark;
987     JSBool ok;
988     JSObject *obj, *argsobj;
989    
990     invokevp = js_AllocStack(cx, 2 + 2, &mark);
991     if (!invokevp)
992     return JS_FALSE;
993    
994     JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[0]));
995     JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
996     obj = JSVAL_TO_OBJECT(vp[0]);
997     JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NoSuchMethodClass);
998    
999     invokevp[0] = obj->fslots[JSSLOT_FOUND_FUNCTION];
1000     invokevp[1] = vp[1];
1001     invokevp[2] = obj->fslots[JSSLOT_SAVED_ID];
1002     argsobj = js_NewArrayObject(cx, argc, vp + 2);
1003     if (!argsobj) {
1004     ok = JS_FALSE;
1005     } else {
1006     invokevp[3] = OBJECT_TO_JSVAL(argsobj);
1007     ok = (flags & JSINVOKE_CONSTRUCT)
1008     ? js_InvokeConstructor(cx, 2, JS_TRUE, invokevp)
1009     : js_Invoke(cx, 2, invokevp, flags);
1010     vp[0] = invokevp[0];
1011     }
1012     js_FreeStack(cx, mark);
1013     return ok;
1014     }
1015    
1016     #endif /* JS_HAS_NO_SUCH_METHOD */
1017    
1018     /*
1019     * We check if the function accepts a primitive value as |this|. For that we
1020     * use a table that maps value's tag into the corresponding function flag.
1021     */
1022     JS_STATIC_ASSERT(JSVAL_INT == 1);
1023     JS_STATIC_ASSERT(JSVAL_DOUBLE == 2);
1024     JS_STATIC_ASSERT(JSVAL_STRING == 4);
1025     JS_STATIC_ASSERT(JSVAL_BOOLEAN == 6);
1026    
1027     const uint16 js_PrimitiveTestFlags[] = {
1028     JSFUN_THISP_NUMBER, /* INT */
1029     JSFUN_THISP_NUMBER, /* DOUBLE */
1030     JSFUN_THISP_NUMBER, /* INT */
1031     JSFUN_THISP_STRING, /* STRING */
1032     JSFUN_THISP_NUMBER, /* INT */
1033     JSFUN_THISP_BOOLEAN, /* BOOLEAN */
1034     JSFUN_THISP_NUMBER /* INT */
1035     };
1036    
1037     /*
1038     * Find a function reference and its 'this' object implicit first parameter
1039     * under argc arguments on cx's stack, and call the function. Push missing
1040     * required arguments, allocate declared local variables, and pop everything
1041     * when done. Then push the return value.
1042     */
1043     JS_FRIEND_API(JSBool)
1044     js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags)
1045     {
1046     void *mark;
1047     JSStackFrame frame;
1048     jsval *sp, *argv, *newvp;
1049     jsval v;
1050     JSObject *funobj, *parent;
1051     JSBool ok;
1052     JSClass *clasp;
1053     JSObjectOps *ops;
1054     JSNative native;
1055     JSFunction *fun;
1056     JSScript *script;
1057     uintN nslots, i;
1058     uint32 rootedArgsFlag;
1059     JSInterpreterHook hook;
1060     void *hookData;
1061    
1062     /* [vp .. vp + 2 + argc) must belong to the last JS stack arena. */
1063     JS_ASSERT((jsval *) cx->stackPool.current->base <= vp);
1064     JS_ASSERT(vp + 2 + argc <= (jsval *) cx->stackPool.current->avail);
1065    
1066     /*
1067     * Mark the top of stack and load frequently-used registers. After this
1068     * point the control should flow through label out2: to return.
1069     */
1070     mark = JS_ARENA_MARK(&cx->stackPool);
1071     v = *vp;
1072    
1073     if (JSVAL_IS_PRIMITIVE(v))
1074     goto bad;
1075    
1076     funobj = JSVAL_TO_OBJECT(v);
1077     parent = OBJ_GET_PARENT(cx, funobj);
1078     clasp = OBJ_GET_CLASS(cx, funobj);
1079     if (clasp != &js_FunctionClass) {
1080     #if JS_HAS_NO_SUCH_METHOD
1081     if (clasp == &js_NoSuchMethodClass) {
1082     ok = NoSuchMethod(cx, argc, vp, flags);
1083     goto out2;
1084     }
1085     #endif
1086    
1087     /* Function is inlined, all other classes use object ops. */
1088     ops = funobj->map->ops;
1089    
1090     /*
1091     * XXX this makes no sense -- why convert to function if clasp->call?
1092     * XXX better to call that hook without converting
1093     * XXX the only thing that needs fixing is liveconnect
1094     *
1095     * Try converting to function, for closure and API compatibility.
1096     * We attempt the conversion under all circumstances for 1.2, but
1097     * only if there is a call op defined otherwise.
1098     */
1099     if ((ops == &js_ObjectOps) ? clasp->call : ops->call) {
1100     ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v);
1101     if (!ok)
1102     goto out2;
1103    
1104     if (VALUE_IS_FUNCTION(cx, v)) {
1105     /* Make vp refer to funobj to keep it available as argv[-2]. */
1106     *vp = v;
1107     funobj = JSVAL_TO_OBJECT(v);
1108     parent = OBJ_GET_PARENT(cx, funobj);
1109     goto have_fun;
1110     }
1111     }
1112     fun = NULL;
1113     script = NULL;
1114     nslots = 0;
1115    
1116     /* Try a call or construct native object op. */
1117     if (flags & JSINVOKE_CONSTRUCT) {
1118     if (!JSVAL_IS_OBJECT(vp[1])) {
1119     ok = js_PrimitiveToObject(cx, &vp[1]);
1120     if (!ok)
1121     goto out2;
1122     }
1123     native = ops->construct;
1124     } else {
1125     native = ops->call;
1126     }
1127     if (!native)
1128     goto bad;
1129     } else {
1130     have_fun:
1131     /* Get private data and set derived locals from it. */
1132     fun = GET_FUNCTION_PRIVATE(cx, funobj);
1133     nslots = FUN_MINARGS(fun);
1134     nslots = (nslots > argc) ? nslots - argc : 0;
1135     if (FUN_INTERPRETED(fun)) {
1136     native = NULL;
1137     script = fun->u.i.script;
1138     } else {
1139     native = fun->u.n.native;
1140     script = NULL;
1141     nslots += fun->u.n.extra;
1142     }
1143    
1144     if (JSFUN_BOUND_METHOD_TEST(fun->flags)) {
1145     /* Handle bound method special case. */
1146     vp[1] = OBJECT_TO_JSVAL(parent);
1147     } else if (!JSVAL_IS_OBJECT(vp[1])) {
1148     JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT));
1149     if (PRIMITIVE_THIS_TEST(fun, vp[1]))
1150     goto start_call;
1151     }
1152     }
1153    
1154     if (flags & JSINVOKE_CONSTRUCT) {
1155     JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
1156     } else {
1157     /*
1158     * We must call js_ComputeThis in case we are not called from the
1159     * interpreter, where a prior bytecode has computed an appropriate
1160     * |this| already.
1161     *
1162     * But we need to compute |this| eagerly only for so-called "slow"
1163     * (i.e., not fast) native functions. Fast natives must use either
1164     * JS_THIS or JS_THIS_OBJECT, and scripted functions will go through
1165     * the appropriate this-computing bytecode, e.g., JSOP_THIS.
1166     */
1167     if (native && (!fun || !(fun->flags & JSFUN_FAST_NATIVE))) {
1168     if (!js_ComputeThis(cx, JS_FALSE, vp + 2)) {
1169     ok = JS_FALSE;
1170     goto out2;
1171     }
1172     flags |= JSFRAME_COMPUTED_THIS;
1173     }
1174     }
1175    
1176     start_call:
1177     if (native && fun && (fun->flags & JSFUN_FAST_NATIVE)) {
1178     #ifdef DEBUG_NOT_THROWING
1179     JSBool alreadyThrowing = cx->throwing;
1180     #endif
1181     JS_ASSERT(nslots == 0);
1182     #if JS_HAS_LVALUE_RETURN
1183     /* Set by JS_SetCallReturnValue2, used to return reference types. */
1184     cx->rval2set = JS_FALSE;
1185     #endif
1186     ok = ((JSFastNative) native)(cx, argc, vp);
1187     JS_RUNTIME_METER(cx->runtime, nativeCalls);
1188     #ifdef DEBUG_NOT_THROWING
1189     if (ok && !alreadyThrowing)
1190     ASSERT_NOT_THROWING(cx);
1191     #endif
1192     goto out2;
1193     }
1194    
1195     argv = vp + 2;
1196     sp = argv + argc;
1197    
1198     rootedArgsFlag = JSFRAME_ROOTED_ARGV;
1199     if (nslots != 0) {
1200     /*
1201     * The extra slots required by the function continue with argument
1202     * slots. Thus, when the last stack pool arena does not have room to
1203     * fit nslots right after sp and AllocateAfterSP fails, we have to copy
1204     * [vp..vp+2+argc) slots and clear rootedArgsFlag to root the copy.
1205     */
1206     if (!AllocateAfterSP(cx, sp, nslots)) {
1207     rootedArgsFlag = 0;
1208     newvp = js_AllocRawStack(cx, 2 + argc + nslots, NULL);
1209     if (!newvp) {
1210     ok = JS_FALSE;
1211     goto out2;
1212     }
1213     memcpy(newvp, vp, (2 + argc) * sizeof(jsval));
1214     argv = newvp + 2;
1215     sp = argv + argc;
1216     }
1217    
1218     /* Push void to initialize missing args. */
1219     i = nslots;
1220     do {
1221     *sp++ = JSVAL_VOID;
1222     } while (--i != 0);
1223     }
1224    
1225     /* Allocate space for local variables and stack of interpreted function. */
1226     if (script && script->nslots != 0) {
1227     if (!AllocateAfterSP(cx, sp, script->nslots)) {
1228     /* NB: Discontinuity between argv and slots, stack slots. */
1229     sp = js_AllocRawStack(cx, script->nslots, NULL);
1230     if (!sp) {
1231     ok = JS_FALSE;
1232     goto out2;
1233     }
1234     }
1235    
1236     /* Push void to initialize local variables. */
1237     for (jsval *end = sp + fun->u.i.nvars; sp != end; ++sp)
1238     *sp = JSVAL_VOID;
1239     }
1240    
1241     /*
1242     * Initialize the frame.
1243     *
1244     * To set thisp we use an explicit cast and not JSVAL_TO_OBJECT, as vp[1]
1245     * can be a primitive value here for those native functions specified with
1246     * JSFUN_THISP_(NUMBER|STRING|BOOLEAN) flags.
1247     */
1248     frame.thisp = (JSObject *)vp[1];
1249     frame.varobj = NULL;
1250     frame.callobj = frame.argsobj = NULL;
1251     frame.script = script;
1252     frame.callee = funobj;
1253     frame.fun = fun;
1254     frame.argc = argc;
1255     frame.argv = argv;
1256    
1257     /* Default return value for a constructor is the new object. */
1258     frame.rval = (flags & JSINVOKE_CONSTRUCT) ? vp[1] : JSVAL_VOID;
1259     frame.down = cx->fp;
1260     frame.annotation = NULL;
1261     frame.scopeChain = NULL; /* set below for real, after cx->fp is set */
1262     frame.regs = NULL;
1263     frame.slots = NULL;
1264     frame.sharpDepth = 0;
1265     frame.sharpArray = NULL;
1266     frame.flags = flags | rootedArgsFlag;
1267     frame.dormantNext = NULL;
1268     frame.xmlNamespace = NULL;
1269     frame.blockChain = NULL;
1270    
1271     MUST_FLOW_THROUGH("out");
1272     cx->fp = &frame;
1273    
1274     /* Init these now in case we goto out before first hook call. */
1275     hook = cx->debugHooks->callHook;
1276     hookData = NULL;
1277    
1278     /* call the hook if present */
1279     if (hook && (native || script))
1280     hookData = hook(cx, &frame, JS_TRUE, 0, cx->debugHooks->callHookData);
1281    
1282     /* Call the function, either a native method or an interpreted script. */
1283     if (native) {
1284     #ifdef DEBUG_NOT_THROWING
1285     JSBool alreadyThrowing = cx->throwing;
1286     #endif
1287    
1288     #if JS_HAS_LVALUE_RETURN
1289     /* Set by JS_SetCallReturnValue2, used to return reference types. */
1290     cx->rval2set = JS_FALSE;
1291     #endif
1292    
1293     /* If native, use caller varobj and scopeChain for eval. */
1294     JS_ASSERT(!frame.varobj);
1295     JS_ASSERT(!frame.scopeChain);
1296     if (frame.down) {
1297     frame.varobj = frame.down->varobj;
1298     frame.scopeChain = frame.down->scopeChain;
1299     }
1300    
1301     /* But ensure that we have a scope chain. */
1302     if (!frame.scopeChain)
1303     frame.scopeChain = parent;
1304    
1305     frame.displaySave = NULL;
1306     ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval);
1307     JS_RUNTIME_METER(cx->runtime, nativeCalls);
1308     #ifdef DEBUG_NOT_THROWING
1309     if (ok && !alreadyThrowing)
1310     ASSERT_NOT_THROWING(cx);
1311     #endif
1312     } else if (script) {
1313     /* Use parent scope so js_GetCallObject can find the right "Call". */
1314     frame.scopeChain = parent;
1315     if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) {
1316     /* Scope with a call object parented by the callee's parent. */
1317     if (!js_GetCallObject(cx, &frame, parent)) {
1318     ok = JS_FALSE;
1319     goto out;
1320     }
1321     }
1322     frame.slots = sp - fun->u.i.nvars;
1323    
1324     ok = js_Interpret(cx);
1325     } else {
1326     /* fun might be onerror trying to report a syntax error in itself. */
1327     frame.scopeChain = NULL;
1328     frame.displaySave = NULL;
1329     ok = JS_TRUE;
1330     }
1331    
1332     out:
1333     if (hookData) {
1334     hook = cx->debugHooks->callHook;
1335     if (hook)
1336     hook(cx, &frame, JS_FALSE, &ok, hookData);
1337     }
1338    
1339     /* If frame has a call object, sync values and clear back-pointer. */
1340     if (frame.callobj)
1341     ok &= js_PutCallObject(cx, &frame);
1342    
1343     /* If frame has an arguments object, sync values and clear back-pointer. */
1344     if (frame.argsobj)
1345     ok &= js_PutArgsObject(cx, &frame);
1346    
1347     *vp = frame.rval;
1348    
1349     /* Restore cx->fp now that we're done releasing frame objects. */
1350     cx->fp = frame.down;
1351    
1352     out2:
1353     /* Pop everything we may have allocated off the stack. */
1354     JS_ARENA_RELEASE(&cx->stackPool, mark);
1355     if (!ok)
1356     *vp = JSVAL_NULL;
1357     return ok;
1358    
1359     bad:
1360     js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
1361     ok = JS_FALSE;
1362     goto out2;
1363     }
1364    
1365     JSBool
1366     js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags,
1367     uintN argc, jsval *argv, jsval *rval)
1368     {
1369     jsval *invokevp;
1370     void *mark;
1371     JSBool ok;
1372    
1373     invokevp = js_AllocStack(cx, 2 + argc, &mark);
1374     if (!invokevp)
1375     return JS_FALSE;
1376    
1377     invokevp[0] = fval;
1378     invokevp[1] = OBJECT_TO_JSVAL(obj);
1379     memcpy(invokevp + 2, argv, argc * sizeof *argv);
1380    
1381     ok = js_Invoke(cx, argc, invokevp, flags);
1382     if (ok) {
1383     /*
1384     * Store *rval in the a scoped local root if a scope is open, else in
1385     * the lastInternalResult pigeon-hole GC root, solely so users of
1386     * js_InternalInvoke and its direct and indirect (js_ValueToString for
1387     * example) callers do not need to manage roots for local, temporary
1388     * references to such results.
1389     */
1390     *rval = *invokevp;
1391     if (JSVAL_IS_GCTHING(*rval) && *rval != JSVAL_NULL) {
1392     if (cx->localRootStack) {
1393     if (js_PushLocalRoot(cx, cx->localRootStack, *rval) < 0)
1394     ok = JS_FALSE;
1395     } else {
1396     cx->weakRoots.lastInternalResult = *rval;
1397     }
1398     }
1399     }
1400    
1401     js_FreeStack(cx, mark);
1402     return ok;
1403     }
1404    
1405     JSBool
1406     js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval,
1407     JSAccessMode mode, uintN argc, jsval *argv, jsval *rval)
1408     {
1409     JSSecurityCallbacks *callbacks;
1410    
1411     /*
1412     * js_InternalInvoke could result in another try to get or set the same id
1413     * again, see bug 355497.
1414     */
1415     JS_CHECK_RECURSION(cx, return JS_FALSE);
1416    
1417     /*
1418     * Check general (not object-ops/class-specific) access from the running
1419     * script to obj.id only if id has a scripted getter or setter that we're
1420     * about to invoke. If we don't check this case, nothing else will -- no
1421     * other native code has the chance to check.
1422     *
1423     * Contrast this non-native (scripted) case with native getter and setter
1424     * accesses, where the native itself must do an access check, if security
1425     * policies requires it. We make a checkAccess or checkObjectAccess call
1426     * back to the embedding program only in those cases where we're not going
1427     * to call an embedding-defined native function, getter, setter, or class
1428     * hook anyway. Where we do call such a native, there's no need for the
1429     * engine to impose a separate access check callback on all embeddings --
1430     * many embeddings have no security policy at all.
1431     */
1432     JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE);
1433     callbacks = JS_GetSecurityCallbacks(cx);
1434     if (callbacks &&
1435     callbacks->checkObjectAccess &&
1436     VALUE_IS_FUNCTION(cx, fval) &&
1437     FUN_INTERPRETED(GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(fval))) &&
1438     !callbacks->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode, &fval)) {
1439     return JS_FALSE;
1440     }
1441    
1442     return js_InternalCall(cx, obj, fval, argc, argv, rval);
1443     }
1444    
1445     JSBool
1446     js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
1447     JSStackFrame *down, uintN flags, jsval *result)
1448     {
1449     JSInterpreterHook hook;
1450     void *hookData, *mark;
1451     JSStackFrame *oldfp, frame;
1452     JSObject *obj, *tmp;
1453     JSBool ok;
1454    
1455     #ifdef INCLUDE_MOZILLA_DTRACE
1456     if (JAVASCRIPT_EXECUTE_START_ENABLED())
1457     jsdtrace_execute_start(script);
1458     #endif
1459    
1460     hook = cx->debugHooks->executeHook;
1461     hookData = mark = NULL;
1462     oldfp = cx->fp;
1463     frame.script = script;
1464     if (down) {
1465     /* Propagate arg state for eval and the debugger API. */
1466     frame.callobj = down->callobj;
1467     frame.argsobj = down->argsobj;
1468     frame.varobj = down->varobj;
1469     frame.callee = down->callee;
1470     frame.fun = down->fun;
1471     frame.thisp = down->thisp;
1472     if (down->flags & JSFRAME_COMPUTED_THIS)
1473     flags |= JSFRAME_COMPUTED_THIS;
1474     frame.argc = down->argc;
1475     frame.argv = down->argv;
1476     frame.annotation = down->annotation;
1477     frame.sharpArray = down->sharpArray;
1478     JS_ASSERT(script->nfixed == 0);
1479     } else {
1480     frame.callobj = frame.argsobj = NULL;
1481     obj = chain;
1482     if (cx->options & JSOPTION_VAROBJFIX) {
1483     while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
1484     obj = tmp;
1485     }
1486     frame.varobj = obj;
1487     frame.callee = NULL;
1488     frame.fun = NULL;
1489     frame.thisp = chain;
1490     frame.argc = 0;
1491     frame.argv = NULL;
1492     frame.annotation = NULL;
1493     frame.sharpArray = NULL;
1494     }
1495     if (script->nslots != 0) {
1496     frame.slots = js_AllocRawStack(cx, script->nslots, &mark);
1497     if (!frame.slots) {
1498     ok = JS_FALSE;
1499     goto out;
1500     }
1501     memset(frame.slots, 0, script->nfixed * sizeof(jsval));
1502     } else {
1503     frame.slots = NULL;
1504     }
1505    
1506     frame.rval = JSVAL_VOID;
1507     frame.down = down;
1508     frame.scopeChain = chain;
1509     frame.regs = NULL;
1510     frame.sharpDepth = 0;
1511     frame.flags = flags;
1512     frame.dormantNext = NULL;
1513     frame.xmlNamespace = NULL;
1514     frame.blockChain = NULL;
1515    
1516     /*
1517     * Here we wrap the call to js_Interpret with code to (conditionally)
1518     * save and restore the old stack frame chain into a chain of 'dormant'
1519     * frame chains. Since we are replacing cx->fp, we were running into
1520     * the problem that if GC was called under this frame, some of the GC
1521     * things associated with the old frame chain (available here only in
1522     * the C variable 'oldfp') were not rooted and were being collected.
1523     *
1524     * So, now we preserve the links to these 'dormant' frame chains in cx
1525     * before calling js_Interpret and cleanup afterwards. The GC walks
1526     * these dormant chains and marks objects in the same way that it marks
1527     * objects in the primary cx->fp chain.
1528     */
1529     if (oldfp && oldfp != down) {
1530     JS_ASSERT(!oldfp->dormantNext);
1531     oldfp->dormantNext = cx->dormantFrameChain;
1532     cx->dormantFrameChain = oldfp;
1533     }
1534    
1535     cx->fp = &frame;
1536     if (!down) {
1537     OBJ_TO_OUTER_OBJECT(cx, frame.thisp);
1538     if (!frame.thisp) {
1539     ok = JS_FALSE;
1540     goto out2;
1541     }
1542     frame.flags |= JSFRAME_COMPUTED_THIS;
1543     }
1544    
1545     if (hook) {
1546     hookData = hook(cx, &frame, JS_TRUE, 0,
1547     cx->debugHooks->executeHookData);
1548     }
1549    
1550     ok = js_Interpret(cx);
1551     if (result)
1552     *result = frame.rval;
1553    
1554     if (hookData) {
1555     hook = cx->debugHooks->executeHook;
1556     if (hook)
1557     hook(cx, &frame, JS_FALSE, &ok, hookData);
1558     }
1559    
1560     out2:
1561     if (mark)
1562     js_FreeRawStack(cx, mark);
1563     cx->fp = oldfp;
1564    
1565     if (oldfp && oldfp != down) {
1566     JS_ASSERT(cx->dormantFrameChain == oldfp);
1567     cx->dormantFrameChain = oldfp->dormantNext;
1568     oldfp->dormantNext = NULL;
1569     }
1570    
1571     out:
1572     #ifdef INCLUDE_MOZILLA_DTRACE
1573     if (JAVASCRIPT_EXECUTE_DONE_ENABLED())
1574     jsdtrace_execute_done(script);
1575     #endif
1576     return ok;
1577     }
1578    
1579     JSBool
1580     js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
1581     JSObject **objp, JSProperty **propp)
1582     {
1583     JSObject *obj2;
1584     JSProperty *prop;
1585     uintN oldAttrs, report;
1586     JSBool isFunction;
1587     jsval value;
1588     const char *type, *name;
1589    
1590     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1591     return JS_FALSE;
1592     if (propp) {
1593     *objp = obj2;
1594     *propp = prop;
1595     }
1596     if (!prop)
1597     return JS_TRUE;
1598    
1599     /*
1600     * Use prop as a speedup hint to OBJ_GET_ATTRIBUTES, but drop it on error.
1601     * An assertion at label bad: will insist that it is null.
1602     */
1603     if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) {
1604     OBJ_DROP_PROPERTY(cx, obj2, prop);
1605     #ifdef DEBUG
1606     prop = NULL;
1607     #endif
1608     goto bad;
1609     }
1610    
1611     /*
1612     * From here, return true, or else goto bad on failure to null out params.
1613     * If our caller doesn't want prop, drop it (we don't need it any longer).
1614     */
1615     if (!propp) {
1616     OBJ_DROP_PROPERTY(cx, obj2, prop);
1617     prop = NULL;
1618     }
1619    
1620     if (attrs == JSPROP_INITIALIZER) {
1621     /* Allow the new object to override properties. */
1622     if (obj2 != obj)
1623     return JS_TRUE;
1624     report = JSREPORT_WARNING | JSREPORT_STRICT;
1625     } else {
1626     /* We allow redeclaring some non-readonly properties. */
1627     if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) {
1628     /*
1629     * Allow redeclaration of variables and functions, but insist that
1630     * the new value is not a getter if the old value was, ditto for
1631     * setters -- unless prop is impermanent (in which case anyone
1632     * could delete it and redefine it, willy-nilly).
1633     */
1634     if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
1635     return JS_TRUE;
1636     if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0)
1637     return JS_TRUE;
1638     if (!(oldAttrs & JSPROP_PERMANENT))
1639     return JS_TRUE;
1640     }
1641    
1642     report = JSREPORT_ERROR;
1643     isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
1644     if (!isFunction) {
1645     if (!OBJ_GET_PROPERTY(cx, obj, id, &value))
1646     goto bad;
1647     isFunction = VALUE_IS_FUNCTION(cx, value);
1648     }
1649     }
1650    
1651     type = (attrs == JSPROP_INITIALIZER)
1652     ? "property"
1653     : (oldAttrs & attrs & JSPROP_GETTER)
1654     ? js_getter_str
1655     : (oldAttrs & attrs & JSPROP_SETTER)
1656     ? js_setter_str
1657     : (oldAttrs & JSPROP_READONLY)
1658     ? js_const_str
1659     : isFunction
1660     ? js_function_str
1661     : js_var_str;
1662     name = js_ValueToPrintableString(cx, ID_TO_VALUE(id));
1663     if (!name)
1664     goto bad;
1665     return JS_ReportErrorFlagsAndNumber(cx, report,
1666     js_GetErrorMessage, NULL,
1667     JSMSG_REDECLARED_VAR,
1668     type, name);
1669    
1670     bad:
1671     if (propp) {
1672     *objp = NULL;
1673     *propp = NULL;
1674     }
1675     JS_ASSERT(!prop);
1676     return JS_FALSE;
1677     }
1678    
1679     JSBool
1680     js_StrictlyEqual(JSContext *cx, jsval lval, jsval rval)
1681     {
1682     jsval ltag = JSVAL_TAG(lval), rtag = JSVAL_TAG(rval);
1683     jsdouble ld, rd;
1684    
1685     if (ltag == rtag) {
1686     if (ltag == JSVAL_STRING) {
1687     JSString *lstr = JSVAL_TO_STRING(lval),
1688     *rstr = JSVAL_TO_STRING(rval);
1689     return js_EqualStrings(lstr, rstr);
1690     }
1691     if (ltag == JSVAL_DOUBLE) {
1692     ld = *JSVAL_TO_DOUBLE(lval);
1693     rd = *JSVAL_TO_DOUBLE(rval);
1694     return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1695     }
1696     if (ltag == JSVAL_OBJECT &&
1697     lval != rval &&
1698     !JSVAL_IS_NULL(lval) &&
1699     !JSVAL_IS_NULL(rval)) {
1700     JSObject *lobj, *robj;
1701    
1702     lobj = js_GetWrappedObject(cx, JSVAL_TO_OBJECT(lval));
1703     robj = js_GetWrappedObject(cx, JSVAL_TO_OBJECT(rval));
1704     lval = OBJECT_TO_JSVAL(lobj);
1705     rval = OBJECT_TO_JSVAL(robj);
1706     }
1707     return lval == rval;
1708     }
1709     if (ltag == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) {
1710     ld = *JSVAL_TO_DOUBLE(lval);
1711     rd = JSVAL_TO_INT(rval);
1712     return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1713     }
1714     if (JSVAL_IS_INT(lval) && rtag == JSVAL_DOUBLE) {
1715     ld = JSVAL_TO_INT(lval);
1716     rd = *JSVAL_TO_DOUBLE(rval);
1717     return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1718     }
1719     return lval == rval;
1720     }
1721    
1722     JSBool
1723     js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp)
1724     {
1725     JSFunction *fun, *fun2;
1726     JSObject *obj, *obj2, *proto, *parent;
1727     jsval lval, rval;
1728     JSClass *clasp;
1729    
1730     fun = NULL;
1731     obj2 = NULL;
1732     lval = *vp;
1733     if (!JSVAL_IS_OBJECT(lval) ||
1734     (obj2 = JSVAL_TO_OBJECT(lval)) == NULL ||
1735     /* XXX clean up to avoid special cases above ObjectOps layer */
1736     OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass ||
1737     !obj2->map->ops->construct)
1738     {
1739     fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT);
1740     if (!fun)
1741     return JS_FALSE;
1742     }
1743    
1744     clasp = &js_ObjectClass;
1745     if (!obj2) {
1746     proto = parent = NULL;
1747     fun = NULL;
1748     } else {
1749     /*
1750     * Get the constructor prototype object for this function.
1751     * Use the nominal 'this' parameter slot, vp[1], as a local
1752     * root to protect this prototype, in case it has no other
1753     * strong refs.
1754     */
1755     if (!OBJ_GET_PROPERTY(cx, obj2,
1756     ATOM_TO_JSID(cx->runtime->atomState
1757     .classPrototypeAtom),
1758     &vp[1])) {
1759     return JS_FALSE;
1760     }
1761     rval = vp[1];
1762     proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL;
1763     parent = OBJ_GET_PARENT(cx, obj2);
1764    
1765     if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) {
1766     fun2 = GET_FUNCTION_PRIVATE(cx, obj2);
1767     if (!FUN_INTERPRETED(fun2) && fun2->u.n.clasp)
1768     clasp = fun2->u.n.clasp;
1769     }
1770     }
1771     obj = js_NewObject(cx, clasp, proto, parent, 0);
1772     if (!obj)
1773     return JS_FALSE;
1774    
1775     /* Now we have an object with a constructor method; call it. */
1776     vp[1] = OBJECT_TO_JSVAL(obj);
1777     if (!js_Invoke(cx, argc, vp, JSINVOKE_CONSTRUCT)) {
1778     cx->weakRoots.newborn[GCX_OBJECT] = NULL;
1779     return JS_FALSE;
1780     }
1781    
1782     /* Check the return value and if it's primitive, force it to be obj. */
1783     rval = *vp;
1784     if (clampReturn && JSVAL_IS_PRIMITIVE(rval)) {
1785     if (!fun) {
1786     /* native [[Construct]] returning primitive is error */
1787     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1788     JSMSG_BAD_NEW_RESULT,
1789     js_ValueToPrintableString(cx, rval));
1790     return JS_FALSE;
1791     }
1792     *vp = OBJECT_TO_JSVAL(obj);
1793     }
1794    
1795     JS_RUNTIME_METER(cx->runtime, constructs);
1796     return JS_TRUE;
1797     }
1798    
1799     JSBool
1800     js_InternNonIntElementId(JSContext *cx, JSObject *obj, jsval idval, jsid *idp)
1801     {
1802     JS_ASSERT(!JSVAL_IS_INT(idval));
1803    
1804     #if JS_HAS_XML_SUPPORT
1805     if (!JSVAL_IS_PRIMITIVE(idval)) {
1806     if (OBJECT_IS_XML(cx, obj)) {
1807     *idp = OBJECT_JSVAL_TO_JSID(idval);
1808     return JS_TRUE;
1809     }
1810     if (!js_IsFunctionQName(cx, JSVAL_TO_OBJECT(idval), idp))
1811     return JS_FALSE;
1812     if (*idp != 0)
1813     return JS_TRUE;
1814     }
1815     #endif
1816    
1817     return js_ValueToStringId(cx, idval, idp);
1818     }
1819    
1820     /*
1821     * Enter the new with scope using an object at sp[-1] and associate the depth
1822     * of the with block with sp + stackIndex.
1823     */
1824     JS_STATIC_INTERPRET JSBool
1825     js_EnterWith(JSContext *cx, jsint stackIndex)
1826     {
1827     JSStackFrame *fp;
1828     jsval *sp;
1829     JSObject *obj, *parent, *withobj;
1830    
1831     fp = cx->fp;
1832     sp = fp->regs->sp;
1833     JS_ASSERT(stackIndex < 0);
1834     JS_ASSERT(StackBase(fp) <= sp + stackIndex);
1835    
1836     if (!JSVAL_IS_PRIMITIVE(sp[-1])) {
1837     obj = JSVAL_TO_OBJECT(sp[-1]);
1838     } else {
1839     obj = js_ValueToNonNullObject(cx, sp[-1]);
1840     if (!obj)
1841     return JS_FALSE;
1842     sp[-1] = OBJECT_TO_JSVAL(obj);
1843     }
1844    
1845     parent = js_GetScopeChain(cx, fp);
1846     if (!parent)
1847     return JS_FALSE;
1848    
1849     OBJ_TO_INNER_OBJECT(cx, obj);
1850     if (!obj)
1851     return JS_FALSE;
1852    
1853     withobj = js_NewWithObject(cx, obj, parent,
1854     sp + stackIndex - StackBase(fp));
1855     if (!withobj)
1856     return JS_FALSE;
1857    
1858     fp->scopeChain = withobj;
1859     js_DisablePropertyCache(cx);
1860     return JS_TRUE;
1861     }
1862    
1863     JS_STATIC_INTERPRET void
1864     js_LeaveWith(JSContext *cx)
1865     {
1866     JSObject *withobj;
1867    
1868     withobj = cx->fp->scopeChain;
1869     JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass);
1870     JS_ASSERT(OBJ_GET_PRIVATE(cx, withobj) == cx->fp);
1871     JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);
1872     cx->fp->scopeChain = OBJ_GET_PARENT(cx, withobj);
1873     JS_SetPrivate(cx, withobj, NULL);
1874     js_EnablePropertyCache(cx);
1875     }
1876    
1877     JSClass *
1878     js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth)
1879     {
1880     JSClass *clasp;
1881    
1882     clasp = OBJ_GET_CLASS(cx, obj);
1883     if ((clasp == &js_WithClass || clasp == &js_BlockClass) &&
1884     OBJ_GET_PRIVATE(cx, obj) == cx->fp &&
1885     OBJ_BLOCK_DEPTH(cx, obj) >= stackDepth) {
1886     return clasp;
1887     }
1888     return NULL;
1889     }
1890    
1891     JS_STATIC_INTERPRET jsint
1892     js_CountWithBlocks(JSContext *cx, JSStackFrame *fp)
1893     {
1894     jsint n;
1895     JSObject *obj;
1896     JSClass *clasp;
1897    
1898     n = 0;
1899     for (obj = fp->scopeChain;
1900     (clasp = js_IsActiveWithOrBlock(cx, obj, 0)) != NULL;
1901     obj = OBJ_GET_PARENT(cx, obj)) {
1902     if (clasp == &js_WithClass)
1903     ++n;
1904     }
1905     return n;
1906     }
1907    
1908     /*
1909     * Unwind block and scope chains to match the given depth. The function sets
1910     * fp->sp on return to stackDepth.
1911     */
1912     JSBool
1913     js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth,
1914     JSBool normalUnwind)
1915     {
1916     JSObject *obj;
1917     JSClass *clasp;
1918    
1919     JS_ASSERT(stackDepth >= 0);
1920     JS_ASSERT(StackBase(fp) + stackDepth <= fp->regs->sp);
1921    
1922     for (obj = fp->blockChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
1923     JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
1924     if (OBJ_BLOCK_DEPTH(cx, obj) < stackDepth)
1925     break;
1926     }
1927     fp->blockChain = obj;
1928    
1929     for (;;) {
1930     obj = fp->scopeChain;
1931     clasp = js_IsActiveWithOrBlock(cx, obj, stackDepth);
1932     if (!clasp)
1933     break;
1934     if (clasp == &js_BlockClass) {
1935     /* Don't fail until after we've updated all stacks. */
1936     normalUnwind &= js_PutBlockObject(cx, normalUnwind);
1937     } else {
1938     js_LeaveWith(cx);
1939     }
1940     }
1941    
1942     fp->regs->sp = StackBase(fp) + stackDepth;
1943     return normalUnwind;
1944     }
1945    
1946     JS_STATIC_INTERPRET JSBool
1947     js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2)
1948     {
1949     jsval v;
1950     jsdouble d;
1951    
1952     v = *vp;
1953     if (JSVAL_IS_DOUBLE(v)) {
1954     d = *JSVAL_TO_DOUBLE(v);
1955     } else if (JSVAL_IS_INT(v)) {
1956     d = JSVAL_TO_INT(v);
1957     } else {
1958     d = js_ValueToNumber(cx, vp);
1959     if (JSVAL_IS_NULL(*vp))
1960     return JS_FALSE;
1961     JS_ASSERT(JSVAL_IS_NUMBER(*vp) || *vp == JSVAL_TRUE);
1962    
1963     /* Store the result of v conversion back in vp for post increments. */
1964     if ((cs->format & JOF_POST) &&
1965     *vp == JSVAL_TRUE
1966     && !js_NewNumberInRootedValue(cx, d, vp)) {
1967     return JS_FALSE;
1968     }
1969     }
1970    
1971     (cs->format & JOF_INC) ? d++ : d--;
1972     if (!js_NewNumberInRootedValue(cx, d, vp2))
1973     return JS_FALSE;
1974    
1975     if (!(cs->format & JOF_POST))
1976     *vp = *vp2;
1977     return JS_TRUE;
1978     }
1979    
1980     #ifdef DEBUG
1981    
1982     JS_STATIC_INTERPRET void
1983     js_TraceOpcode(JSContext *cx, jsint len)
1984     {
1985     FILE *tracefp;
1986     JSStackFrame *fp;
1987     JSFrameRegs *regs;
1988     JSOp prevop;
1989     intN ndefs, n, nuses;
1990     jsval *siter;
1991     JSString *str;
1992     JSOp op;
1993    
1994     tracefp = (FILE *) cx->tracefp;
1995     JS_ASSERT(tracefp);
1996     fp = cx->fp;
1997     regs = fp->regs;
1998     if (len != 0) {
1999     prevop = (JSOp) regs->pc[-len];
2000     ndefs = js_CodeSpec[prevop].ndefs;
2001     if (ndefs != 0) {
2002     if (prevop == JSOP_FORELEM && regs->sp[-1] == JSVAL_FALSE)
2003     --ndefs;
2004     for (n = -ndefs; n < 0; n++) {
2005     char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
2006     NULL);
2007     if (bytes) {
2008     fprintf(tracefp, "%s %s",
2009     (n == -ndefs) ? " output:" : ",",
2010     bytes);
2011     JS_free(cx, bytes);
2012     }
2013     }
2014     fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));
2015     }
2016     fprintf(tracefp, " stack: ");
2017     for (siter = StackBase(fp); siter < regs->sp; siter++) {
2018     str = js_ValueToString(cx, *siter);
2019     if (!str)
2020     fputs("<null>", tracefp);
2021     else
2022     js_FileEscapedString(tracefp, str, 0);
2023     fputc(' ', tracefp);
2024     }
2025     fputc('\n', tracefp);
2026     }
2027    
2028     fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, fp->script, regs->pc));
2029     js_Disassemble1(cx, fp->script, regs->pc,
2030     PTRDIFF(regs->pc, fp->script->code, jsbytecode),
2031     JS_FALSE, tracefp);
2032     op = (JSOp) *regs->pc;
2033     nuses = js_CodeSpec[op].nuses;
2034     if (nuses != 0) {
2035     for (n = -nuses; n < 0; n++) {
2036     char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
2037     NULL);
2038     if (bytes) {
2039     fprintf(tracefp, "%s %s",
2040     (n == -nuses) ? " inputs:" : ",",
2041     bytes);
2042     JS_free(cx, bytes);
2043     }
2044     }
2045     fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));
2046     }
2047     }
2048    
2049     #endif /* DEBUG */
2050    
2051     #ifdef JS_OPMETER
2052    
2053     # include <stdlib.h>
2054    
2055     # define HIST_NSLOTS 8
2056    
2057     /*
2058     * The second dimension is hardcoded at 256 because we know that many bits fit
2059     * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address
2060     * any particular row.
2061     */
2062     static uint32 succeeds[JSOP_LIMIT][256];
2063     static uint32 slot_ops[JSOP_LIMIT][HIST_NSLOTS];
2064    
2065     JS_STATIC_INTERPRET void
2066     js_MeterOpcodePair(JSOp op1, JSOp op2)
2067     {
2068     if (op1 != JSOP_STOP)
2069     ++succeeds[op1][op2];
2070     }
2071    
2072     JS_STATIC_INTERPRET void
2073     js_MeterSlotOpcode(JSOp op, uint32 slot)
2074     {
2075     if (slot < HIST_NSLOTS)
2076     ++slot_ops[op][slot];
2077     }
2078    
2079     typedef struct Edge {
2080     const char *from;
2081     const char *to;
2082     uint32 count;
2083     } Edge;
2084    
2085     static int
2086     compare_edges(const void *a, const void *b)
2087     {
2088     const Edge *ea = (const Edge *) a;
2089     const Edge *eb = (const Edge *) b;
2090    
2091     return (int32)eb->count - (int32)ea->count;
2092     }
2093    
2094     void
2095     js_DumpOpMeters()
2096     {
2097     const char *name, *from, *style;
2098     FILE *fp;
2099     uint32 total, count;
2100     uint32 i, j, nedges;
2101     Edge *graph;
2102    
2103     name = getenv("JS_OPMETER_FILE");
2104     if (!name)
2105     name = "/tmp/ops.dot";
2106     fp = fopen(name, "w");
2107     if (!fp) {
2108     perror(name);
2109     return;
2110     }
2111    
2112     total = nedges = 0;
2113     for (i = 0; i < JSOP_LIMIT; i++) {
2114     for (j = 0; j < JSOP_LIMIT; j++) {
2115     count = succeeds[i][j];
2116     if (count != 0) {
2117     total += count;
2118     ++nedges;
2119     }
2120     }
2121     }
2122    
2123     # define SIGNIFICANT(count,total) (200. * (count) >= (total))
2124    
2125     graph = (Edge *) calloc(nedges, sizeof graph[0]);
2126     for (i = nedges = 0; i < JSOP_LIMIT; i++) {
2127     from = js_CodeName[i];
2128     for (j = 0; j < JSOP_LIMIT; j++) {
2129     count = succeeds[i][j];
2130     if (count != 0 && SIGNIFICANT(count, total)) {
2131     graph[nedges].from = from;
2132     graph[nedges].to = js_CodeName[j];
2133     graph[nedges].count = count;
2134     ++nedges;
2135     }
2136     }
2137     }
2138     qsort(graph, nedges, sizeof(Edge), compare_edges);
2139    
2140     # undef SIGNIFICANT
2141    
2142     fputs("digraph {\n", fp);
2143     for (i = 0, style = NULL; i < nedges; i++) {
2144     JS_ASSERT(i == 0 || graph[i-1].count >= graph[i].count);
2145     if (!style || graph[i-1].count != graph[i].count) {
2146     style = (i > nedges * .75) ? "dotted" :
2147     (i > nedges * .50) ? "dashed" :
2148     (i > nedges * .25) ? "solid" : "bold";
2149     }
2150     fprintf(fp, " %s -> %s [label=\"%lu\" style=%s]\n",
2151     graph[i].from, graph[i].to,
2152     (unsigned long)graph[i].count, style);
2153     }
2154     free(graph);
2155     fputs("}\n", fp);
2156     fclose(fp);
2157    
2158     name = getenv("JS_OPMETER_HIST");
2159     if (!name)
2160     name = "/tmp/ops.hist";
2161     fp = fopen(name, "w");
2162     if (!fp) {
2163     perror(name);
2164     return;
2165     }
2166     fputs("bytecode", fp);
2167     for (j = 0; j < HIST_NSLOTS; j++)
2168     fprintf(fp, " slot %1u", (unsigned)j);
2169     putc('\n', fp);
2170     fputs("========", fp);
2171     for (j = 0; j < HIST_NSLOTS; j++)
2172     fputs(" =======", fp);
2173     putc('\n', fp);
2174     for (i = 0; i < JSOP_LIMIT; i++) {
2175     for (j = 0; j < HIST_NSLOTS; j++) {
2176     if (slot_ops[i][j] != 0) {
2177     /* Reuse j in the next loop, since we break after. */
2178     fprintf(fp, "%-8.8s", js_CodeName[i]);
2179     for (j = 0; j < HIST_NSLOTS; j++)
2180     fprintf(fp, " %7lu", (unsigned long)slot_ops[i][j]);
2181     putc('\n', fp);
2182     break;
2183     }
2184     }
2185     }
2186     fclose(fp);
2187     }
2188    
2189     #endif /* JS_OPSMETER */
2190    
2191     #endif /* !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___ */
2192    
2193     #ifndef jsinvoke_cpp___
2194    
2195     #define PUSH(v) (*regs.sp++ = (v))
2196     #define PUSH_OPND(v) PUSH(v)
2197     #define STORE_OPND(n,v) (regs.sp[n] = (v))
2198     #define POP() (*--regs.sp)
2199     #define POP_OPND() POP()
2200     #define FETCH_OPND(n) (regs.sp[n])
2201    
2202     /*
2203     * Push the jsdouble d using sp from the lexical environment. Try to convert d
2204     * to a jsint that fits in a jsval, otherwise GC-alloc space for it and push a
2205     * reference.
2206     */
2207     #define STORE_NUMBER(cx, n, d) \
2208     JS_BEGIN_MACRO \
2209     jsint i_; \
2210     \
2211     if (JSDOUBLE_IS_INT(d, i_) && INT_FITS_IN_JSVAL(i_)) \
2212     regs.sp[n] = INT_TO_JSVAL(i_); \
2213     else if (!js_NewDoubleInRootedValue(cx, d, &regs.sp[n])) \
2214     goto error; \
2215     JS_END_MACRO
2216    
2217     #define STORE_INT(cx, n, i) \
2218     JS_BEGIN_MACRO \
2219     if (INT_FITS_IN_JSVAL(i)) \
2220     regs.sp[n] = INT_TO_JSVAL(i); \
2221     else if (!js_NewDoubleInRootedValue(cx, (jsdouble) (i), &regs.sp[n])) \
2222     goto error; \
2223     JS_END_MACRO
2224    
2225     #define STORE_UINT(cx, n, u) \
2226     JS_BEGIN_MACRO \
2227     if ((u) <= JSVAL_INT_MAX) \
2228     regs.sp[n] = INT_TO_JSVAL(u); \
2229     else if (!js_NewDoubleInRootedValue(cx, (jsdouble) (u), &regs.sp[n])) \
2230     goto error; \
2231     JS_END_MACRO
2232    
2233     #define FETCH_NUMBER(cx, n, d) \
2234     JS_BEGIN_MACRO \
2235     jsval v_; \
2236     \
2237     v_ = FETCH_OPND(n); \
2238     VALUE_TO_NUMBER(cx, n, v_, d); \
2239     JS_END_MACRO
2240    
2241     #define FETCH_INT(cx, n, i) \
2242     JS_BEGIN_MACRO \
2243     jsval v_; \
2244     \
2245     v_= FETCH_OPND(n); \
2246     if (JSVAL_IS_INT(v_)) { \
2247     i = JSVAL_TO_INT(v_); \
2248     } else { \
2249     i = js_ValueToECMAInt32(cx, &regs.sp[n]); \
2250     if (JSVAL_IS_NULL(regs.sp[n])) \
2251     goto error; \
2252     } \
2253     JS_END_MACRO
2254    
2255     #define FETCH_UINT(cx, n, ui) \
2256     JS_BEGIN_MACRO \
2257     jsval v_; \
2258     \
2259     v_= FETCH_OPND(n); \
2260     if (JSVAL_IS_INT(v_)) { \
2261     ui = (uint32) JSVAL_TO_INT(v_); \
2262     } else { \
2263     ui = js_ValueToECMAUint32(cx, &regs.sp[n]); \
2264     if (JSVAL_IS_NULL(regs.sp[n])) \
2265     goto error; \
2266     } \
2267     JS_END_MACRO
2268    
2269     /*
2270     * Optimized conversion macros that test for the desired type in v before
2271     * homing sp and calling a conversion function.
2272     */
2273     #define VALUE_TO_NUMBER(cx, n, v, d) \
2274     JS_BEGIN_MACRO \
2275     JS_ASSERT(v == regs.sp[n]); \
2276     if (JSVAL_IS_INT(v)) { \
2277     d = (jsdouble)JSVAL_TO_INT(v); \
2278     } else if (JSVAL_IS_DOUBLE(v)) { \
2279     d = *JSVAL_TO_DOUBLE(v); \
2280     } else { \
2281     d = js_ValueToNumber(cx, &regs.sp[n]); \
2282     if (JSVAL_IS_NULL(regs.sp[n])) \
2283     goto error; \
2284     JS_ASSERT(JSVAL_IS_NUMBER(regs.sp[n]) || \
2285     regs.sp[n] == JSVAL_TRUE); \
2286     } \
2287     JS_END_MACRO
2288    
2289     #define POP_BOOLEAN(cx, v, b) \
2290     JS_BEGIN_MACRO \
2291     v = FETCH_OPND(-1); \
2292     if (v == JSVAL_NULL) { \
2293     b = JS_FALSE; \
2294     } else if (JSVAL_IS_BOOLEAN(v)) { \
2295     b = JSVAL_TO_BOOLEAN(v); \
2296     } else { \
2297     b = js_ValueToBoolean(v); \
2298     } \
2299     regs.sp--; \
2300     JS_END_MACRO
2301    
2302     #define VALUE_TO_OBJECT(cx, n, v, obj) \
2303     JS_BEGIN_MACRO \
2304     if (!JSVAL_IS_PRIMITIVE(v)) { \
2305     obj = JSVAL_TO_OBJECT(v); \
2306     } else { \
2307     obj = js_ValueToNonNullObject(cx, v); \
2308     if (!obj) \
2309     goto error; \
2310     STORE_OPND(n, OBJECT_TO_JSVAL(obj)); \
2311     } \
2312     JS_END_MACRO
2313    
2314     #define FETCH_OBJECT(cx, n, v, obj) \
2315     JS_BEGIN_MACRO \
2316     v = FETCH_OPND(n); \
2317     VALUE_TO_OBJECT(cx, n, v, obj); \
2318     JS_END_MACRO
2319    
2320     #define DEFAULT_VALUE(cx, n, hint, v) \
2321     JS_BEGIN_MACRO \
2322     JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); \
2323     JS_ASSERT(v == regs.sp[n]); \
2324     if (!OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, &regs.sp[n])) \
2325     goto error; \
2326     v = regs.sp[n]; \
2327     JS_END_MACRO
2328    
2329     /*
2330     * Quickly test if v is an int from the [-2**29, 2**29) range, that is, when
2331     * the lowest bit of v is 1 and the bits 30 and 31 are both either 0 or 1. For
2332     * such v we can do increment or decrement via adding or subtracting two
2333     * without checking that the result overflows JSVAL_INT_MIN or JSVAL_INT_MAX.
2334     */
2335     #define CAN_DO_FAST_INC_DEC(v) (((((v) << 1) ^ v) & 0x80000001) == 1)
2336    
2337     JS_STATIC_ASSERT(JSVAL_INT == 1);
2338     JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL(JSVAL_INT_MIN)));
2339     JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL(JSVAL_INT_MAX)));
2340    
2341     /*
2342     * Conditional assert to detect failure to clear a pending exception that is
2343     * suppressed (or unintentional suppression of a wanted exception).
2344     */
2345     #if defined DEBUG_brendan || defined DEBUG_mrbkap || defined DEBUG_shaver
2346     # define DEBUG_NOT_THROWING 1
2347     #endif
2348    
2349     #ifdef DEBUG_NOT_THROWING
2350     # define ASSERT_NOT_THROWING(cx) JS_ASSERT(!(cx)->throwing)
2351     #else
2352     # define ASSERT_NOT_THROWING(cx) /* nothing */
2353     #endif
2354    
2355     /*
2356     * Define JS_OPMETER to instrument bytecode succession, generating a .dot file
2357     * on shutdown that shows the graph of significant predecessor/successor pairs
2358     * executed, where the edge labels give the succession counts. The .dot file
2359     * is named by the JS_OPMETER_FILE envariable, and defaults to /tmp/ops.dot.
2360     *
2361     * Bonus feature: JS_OPMETER also enables counters for stack-addressing ops
2362     * such as JSOP_GETLOCAL, JSOP_INCARG, via METER_SLOT_OP. The resulting counts
2363     * are written to JS_OPMETER_HIST, defaulting to /tmp/ops.hist.
2364     */
2365     #ifndef JS_OPMETER
2366     # define METER_OP_INIT(op) /* nothing */
2367     # define METER_OP_PAIR(op1,op2) /* nothing */
2368     # define METER_SLOT_OP(op,slot) /* nothing */
2369     #else
2370    
2371     /*
2372     * The second dimension is hardcoded at 256 because we know that many bits fit
2373     * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address
2374     * any particular row.
2375     */
2376     # define METER_OP_INIT(op) ((op) = JSOP_STOP)
2377     # define METER_OP_PAIR(op1,op2) (js_MeterOpcodePair(op1, op2))
2378     # define METER_SLOT_OP(op,slot) (js_MeterSlotOpcode(op, slot))
2379    
2380     #endif
2381    
2382     #define MAX_INLINE_CALL_COUNT 3000
2383    
2384     /*
2385     * Threaded interpretation via computed goto appears to be well-supported by
2386     * GCC 3 and higher. IBM's C compiler when run with the right options (e.g.,
2387     * -qlanglvl=extended) also supports threading. Ditto the SunPro C compiler.
2388     * Currently it's broken for JS_VERSION < 160, though this isn't worth fixing.
2389     * Add your compiler support macros here.
2390     */
2391     #ifndef JS_THREADED_INTERP
2392     # if JS_VERSION >= 160 && ( \
2393     __GNUC__ >= 3 || \
2394     (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \
2395     __SUNPRO_C >= 0x570)
2396     # define JS_THREADED_INTERP 1
2397     # else
2398     # define JS_THREADED_INTERP 0
2399     # endif
2400     #endif
2401    
2402     /*
2403     * Interpreter assumes the following to implement condition-free interrupt
2404     * implementation when !JS_THREADED_INTERP.
2405     */
2406     JS_STATIC_ASSERT(JSOP_INTERRUPT == 0);
2407    
2408     /*
2409     * Ensure that the intrepreter switch can close call-bytecode cases in the
2410     * same way as non-call bytecodes.
2411     */
2412     JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
2413     JS_STATIC_ASSERT(JSOP_GETGVAR_LENGTH == JSOP_CALLGVAR_LENGTH);
2414     JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH);
2415     JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
2416     JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
2417     JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH);
2418    
2419     /*
2420     * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
2421     * remain distinct for the decompiler. Ditto for JSOP_NULL{,THIS}.
2422     */
2423     JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
2424     JS_STATIC_ASSERT(JSOP_NULL_LENGTH == JSOP_NULLTHIS_LENGTH);
2425    
2426     /* See TRY_BRANCH_AFTER_COND. */
2427     JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH);
2428     JS_STATIC_ASSERT(JSOP_IFNE == JSOP_IFEQ + 1);
2429    
2430     /* For the fastest case inder JSOP_INCNAME, etc. */
2431     JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_DECNAME_LENGTH);
2432     JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEINC_LENGTH);
2433     JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH);
2434    
2435     JSBool
2436     js_Interpret(JSContext *cx)
2437     {
2438     JSRuntime *rt;
2439     JSStackFrame *fp;
2440     JSScript *script;
2441     uintN inlineCallCount;
2442     JSAtom **atoms;
2443     JSVersion currentVersion, originalVersion;
2444     JSFrameRegs regs;
2445     JSObject *obj, *obj2, *parent;
2446     JSBool ok, cond;
2447     jsint len;
2448     jsbytecode *endpc, *pc2;
2449     JSOp op, op2;
2450     jsatomid index;
2451     JSAtom *atom;
2452     uintN argc, attrs, flags;
2453     uint32 slot;
2454     jsval *vp, lval, rval, ltmp, rtmp;
2455     jsid id;
2456     JSProperty *prop;
2457     JSScopeProperty *sprop;
2458     JSString *str, *str2;
2459     jsint i, j;
2460     jsdouble d, d2;
2461     JSClass *clasp;
2462     JSFunction *fun;
2463     JSType type;
2464     #if JS_THREADED_INTERP
2465     register void * const *jumpTable;
2466     #else
2467     register uint32 switchMask;
2468     uintN switchOp;
2469     #endif
2470     jsint low, high, off, npairs;
2471     JSBool match;
2472     #if JS_HAS_GETTER_SETTER
2473     JSPropertyOp getter, setter;
2474     #endif
2475     JSAutoResolveFlags rf(cx, JSRESOLVE_INFER);
2476    
2477     #ifdef __GNUC__
2478     # define JS_EXTENSION __extension__
2479     # define JS_EXTENSION_(s) __extension__ ({ s; })
2480     #else
2481     # define JS_EXTENSION
2482     # define JS_EXTENSION_(s) s
2483     #endif
2484    
2485     #if JS_THREADED_INTERP
2486     static void *const normalJumpTable[] = {
2487     # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2488     JS_EXTENSION &&L_##op,
2489     # include "jsopcode.tbl"
2490     # undef OPDEF
2491     };
2492    
2493     #ifdef JS_TRACER
2494     static void *const recordingJumpTable[] = {
2495     # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2496     JS_EXTENSION &&R_##op,
2497     # include "jsopcode.tbl"
2498     # undef OPDEF
2499     };
2500     #endif /* JS_TRACER */
2501    
2502     static void *const interruptJumpTable[] = {
2503     # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2504     JS_EXTENSION &&L_JSOP_INTERRUPT,
2505     # include "jsopcode.tbl"
2506     # undef OPDEF
2507     };
2508    
2509     METER_OP_INIT(op); /* to nullify first METER_OP_PAIR */
2510    
2511     # ifdef JS_TRACER
2512     # define CHECK_RECORDER() JS_BEGIN_MACRO \
2513     JS_ASSERT(!TRACE_RECORDER(cx) ^ \
2514     (jumpTable == recordingJumpTable)); \
2515     JS_END_MACRO
2516     # else
2517     # define CHECK_RECORDER() ((void)0)
2518     # endif
2519    
2520     # define DO_OP() JS_BEGIN_MACRO \
2521     CHECK_RECORDER(); \
2522     JS_EXTENSION_(goto *jumpTable[op]); \
2523     JS_END_MACRO
2524     # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
2525     METER_OP_PAIR(op, regs.pc[n]); \
2526     op = (JSOp) *(regs.pc += (n)); \
2527     DO_OP(); \
2528     JS_END_MACRO
2529    
2530     # define BEGIN_CASE(OP) L_##OP: \
2531     CHECK_RECORDER();
2532     # define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH);
2533     # define END_VARLEN_CASE DO_NEXT_OP(len);
2534     # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP) \
2535     JS_ASSERT(js_CodeSpec[OP].length == 1); \
2536     op = (JSOp) *++regs.pc; \
2537     DO_OP();
2538    
2539     # define END_EMPTY_CASES
2540    
2541     #else /* !JS_THREADED_INTERP */
2542    
2543     # define DO_OP() goto do_op
2544     # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
2545     JS_ASSERT((n) == len); \
2546     goto advance_pc; \
2547     JS_END_MACRO
2548    
2549     # define BEGIN_CASE(OP) case OP:
2550     # define END_CASE(OP) END_CASE_LEN(OP##_LENGTH)
2551     # define END_CASE_LEN(n) END_CASE_LENX(n)
2552     # define END_CASE_LENX(n) END_CASE_LEN##n
2553    
2554     /*
2555     * To share the code for all len == 1 cases we use the specialized label with
2556     * code that falls through to advance_pc: .
2557     */
2558     # define END_CASE_LEN1 goto advance_pc_by_one;
2559     # define END_CASE_LEN2 len = 2; goto advance_pc;
2560     # define END_CASE_LEN3 len = 3; goto advance_pc;
2561     # define END_CASE_LEN4 len = 4; goto advance_pc;
2562     # define END_CASE_LEN5 len = 5; goto advance_pc;
2563     # define END_VARLEN_CASE goto advance_pc;
2564     # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)
2565     # define END_EMPTY_CASES goto advance_pc_by_one;
2566    
2567     #endif /* !JS_THREADED_INTERP */
2568    
2569     #ifdef JS_TRACER
2570     /* We had better not be entering the interpreter from JIT-compiled code. */
2571     TraceRecorder *tr = NULL;
2572     if (JS_ON_TRACE(cx)) {
2573     tr = TRACE_RECORDER(cx);
2574     SET_TRACE_RECORDER(cx, NULL);
2575     JS_TRACE_MONITOR(cx).onTrace = JS_FALSE;
2576     }
2577     #endif
2578    
2579     /* Check for too deep of a native thread stack. */
2580     JS_CHECK_RECURSION(cx, return JS_FALSE);
2581    
2582     rt = cx->runtime;
2583    
2584     /* Set registerized frame pointer and derived script pointer. */
2585     fp = cx->fp;
2586     script = fp->script;
2587     JS_ASSERT(script->length != 0);
2588    
2589     /* Count of JS function calls that nest in this C js_Interpret frame. */
2590     inlineCallCount = 0;
2591    
2592     /*
2593     * Initialize the index segment register used by LOAD_ATOM and
2594     * GET_FULL_INDEX macros below. As a register we use a pointer based on
2595     * the atom map to turn frequently executed LOAD_ATOM into simple array
2596     * access. For less frequent object and regexp loads we have to recover
2597     * the segment from atoms pointer first.
2598     */
2599     atoms = script->atomMap.vector;
2600    
2601     #define LOAD_ATOM(PCOFF) \
2602     JS_BEGIN_MACRO \
2603     JS_ASSERT((size_t)(atoms - script->atomMap.vector) < \
2604     (size_t)(script->atomMap.length - \
2605     GET_INDEX(regs.pc + PCOFF))); \
2606     atom = atoms[GET_INDEX(regs.pc + PCOFF)]; \
2607     JS_END_MACRO
2608    
2609     #define GET_FULL_INDEX(PCOFF) \
2610     (atoms - script->atomMap.vector + GET_INDEX(regs.pc + PCOFF))
2611    
2612     #define LOAD_OBJECT(PCOFF) \
2613     JS_GET_SCRIPT_OBJECT(script, GET_FULL_INDEX(PCOFF), obj)
2614    
2615     #define LOAD_FUNCTION(PCOFF) \
2616     JS_GET_SCRIPT_FUNCTION(script, GET_FULL_INDEX(PCOFF), fun)
2617    
2618     #ifdef JS_TRACER
2619    
2620     #define MONITOR_BRANCH(oldpc) \
2621     JS_BEGIN_MACRO \
2622     if (TRACING_ENABLED(cx)) { \
2623     ENABLE_TRACER(js_MonitorLoopEdge(cx, oldpc, inlineCallCount)); \
2624     fp = cx->fp; \
2625     script = fp->script; \
2626     atoms = script->atomMap.vector; \
2627     currentVersion = (JSVersion) script->version; \
2628     JS_ASSERT(fp->regs == &regs); \
2629     } \
2630     JS_END_MACRO
2631    
2632     #else /* !JS_TRACER */
2633    
2634     #define MONITOR_BRANCH(oldpc) ((void) 0)
2635    
2636     #endif /* !JS_TRACER */
2637    
2638     /*
2639     * Prepare to call a user-supplied branch handler, and abort the script
2640     * if it returns false.
2641     */
2642     #define CHECK_BRANCH() \
2643     JS_BEGIN_MACRO \
2644     if ((cx->operationCount -= JSOW_SCRIPT_JUMP) <= 0) { \
2645     if (!js_ResetOperationCount(cx)) \
2646     goto error; \
2647     } \
2648     JS_END_MACRO
2649    
2650     #define BRANCH(n) \
2651     JS_BEGIN_MACRO \
2652     regs.pc += n; \
2653     if (n <= 0) { \
2654