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

Annotation of /trunk/js/jsinterp.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 399 - (hide annotations)
Tue Dec 9 03:37:47 2008 UTC (13 years, 6 months ago) by siliconforks
File size: 270966 byte(s)
Use SpiderMonkey from Firefox 3.1b2.

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