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

Contents of /trunk/js/jsinterp.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

1 /* -*- 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, JSScopeProperty *sprop)
89 {
90 JSRuntime *rt;
91 uint32 shape;
92 JSTempValueRooter tvr;
93
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 if (sprop)
100 JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr);
101 js_GC(cx, gcLocked ? GC_LOCK_HELD : GC_NORMAL);
102 if (sprop)
103 JS_POP_TEMP_ROOT(cx, &tvr);
104 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 JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code)
318 < cx->fp->script->length);
319
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 frame.imacpc = NULL;
1270 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
1503 frame.imacpc = NULL;
1504 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 if (!FUN_INTERPRETED(fun2) &&
1777 !(fun2->flags & JSFUN_TRACEABLE) &&
1778 fun2->u.n.u.clasp) {
1779 clasp = fun2->u.n.u.clasp;
1780 }
1781 }
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 fprintf(tracefp, "%4u: ",
2039 js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : regs->pc));
2040 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 /*
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 }
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 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 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 #define MONITOR_BRANCH() \
2645 JS_BEGIN_MACRO \
2646 if (TRACING_ENABLED(cx)) { \
2647 ENABLE_TRACER(js_MonitorLoopEdge(cx, inlineCallCount)); \
2648 fp = cx->fp; \
2649 script = fp->script; \
2650 atoms = script->atomMap.vector; \
2651 currentVersion = (JSVersion) script->version; \
2652 JS_ASSERT(fp->regs == &regs); \
2653 if (cx->throwing) \
2654 goto error; \
2655 } \
2656 JS_END_MACRO
2657
2658 #else /* !JS_TRACER */
2659
2660 #define MONITOR_BRANCH() ((void) 0)
2661
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() \
2669 JS_BEGIN_MACRO \
2670 if ((cx->operationCount -= JSOW_SCRIPT_JUMP) <= 0) { \
2671 if (!js_ResetOperationCount(cx)) \
2672 goto error; \
2673 } \
2674 JS_END_MACRO
2675
2676 #define BRANCH(n) \
2677 JS_BEGIN_MACRO \
2678 regs.pc += n; \
2679 if (n <= 0) { \
2680 CHECK_BRANCH(); \
2681 MONITOR_BRANCH(); \
2682 } \
2683 op = (JSOp) *regs.pc; \
2684 DO_OP(); \
2685 JS_END_MACRO
2686
2687 MUST_FLOW_THROUGH("exit");
2688 ++cx->interpLevel;
2689
2690 /*
2691 * Optimized Get and SetVersion for proper script language versioning.
2692 *
2693 * If any native method or JSClass/JSObjectOps hook calls js_SetVersion
2694 * and changes cx->version, the effect will "stick" and we will stop
2695 * maintaining currentVersion. This is relied upon by testsuites, for
2696 * the most part -- web browsers select version before compiling and not
2697 * at run-time.
2698 */
2699 currentVersion = (JSVersion) script->version;
2700 originalVersion = (JSVersion) cx->version;
2701 if (currentVersion != originalVersion)
2702 js_SetVersion(cx, currentVersion);
2703
2704 /* Update the static-link display. */
2705 if (script->staticDepth < JS_DISPLAY_SIZE) {
2706 JSStackFrame **disp = &cx->display[script->staticDepth];
2707 fp->displaySave = *disp;
2708 *disp = fp;
2709 }
2710 #ifdef DEBUG
2711 fp->pcDisabledSave = JS_PROPERTY_CACHE(cx).disabled;
2712 #endif
2713
2714 /*
2715 * Load the debugger's interrupt hook here and after calling out to native
2716 * functions (but not to getters, setters, or other native hooks), so we do
2717 * not have to reload it each time through the interpreter loop -- we hope
2718 * the compiler can keep it in a register when it is non-null.
2719 */
2720 #if JS_THREADED_INTERP
2721 #ifdef JS_TRACER
2722 # define LOAD_INTERRUPT_HANDLER(cx) \
2723 ((void) (jumpTable = (cx)->debugHooks->interruptHandler \
2724 ? interruptJumpTable \
2725 : TRACE_RECORDER(cx) \
2726 ? recordingJumpTable \
2727 : normalJumpTable))
2728 # define ENABLE_TRACER(flag) \
2729 JS_BEGIN_MACRO \
2730 bool flag_ = (flag); \
2731 JS_ASSERT(flag_ == !!TRACE_RECORDER(cx)); \
2732 jumpTable = flag_ ? recordingJumpTable : normalJumpTable; \
2733 JS_END_MACRO
2734 #else /* !JS_TRACER */
2735 # define LOAD_INTERRUPT_HANDLER(cx) \
2736 ((void) (jumpTable = (cx)->debugHooks->interruptHandler \
2737 ? interruptJumpTable \
2738 : normalJumpTable))
2739 # define ENABLE_TRACER(flag) ((void)0)
2740 #endif /* !JS_TRACER */
2741 #else /* !JS_THREADED_INTERP */
2742 #ifdef JS_TRACER
2743 # define LOAD_INTERRUPT_HANDLER(cx) \
2744 ((void) (switchMask = ((cx)->debugHooks->interruptHandler || \
2745 TRACE_RECORDER(cx)) ? 0 : 255))
2746 # define ENABLE_TRACER(flag) \
2747 JS_BEGIN_MACRO \
2748 bool flag_ = (flag); \
2749 JS_ASSERT(flag_ == !!TRACE_RECORDER(cx)); \
2750 switchMask = flag_ ? 0 : 255; \
2751 JS_END_MACRO
2752 #else /* !JS_TRACER */
2753 # define LOAD_INTERRUPT_HANDLER(cx) \
2754 ((void) (switchMask = ((cx)->debugHooks->interruptHandler \
2755 ? 0 : 255)))
2756 # define ENABLE_TRACER(flag) ((void)0)
2757 #endif /* !JS_TRACER */
2758 #endif /* !JS_THREADED_INTERP */
2759
2760 LOAD_INTERRUPT_HANDLER(cx);
2761
2762 #if !JS_HAS_GENERATORS
2763 JS_ASSERT(!fp->regs);
2764 #else
2765 /* Initialize the pc and sp registers unless we're resuming a generator. */
2766 if (JS_LIKELY(!fp->regs)) {
2767 #endif
2768 ASSERT_NOT_THROWING(cx);
2769 regs.pc = script->code;
2770 regs.sp = StackBase(fp);
2771 fp->regs = &regs;
2772 #if JS_HAS_GENERATORS
2773 } else {
2774 JSGenerator *gen;
2775
2776 JS_ASSERT(fp->flags & JSFRAME_GENERATOR);
2777 gen = FRAME_TO_GENERATOR(fp);
2778 JS_ASSERT(fp->regs == &gen->savedRegs);
2779 regs = gen->savedRegs;
2780 fp->regs = &regs;
2781 JS_ASSERT((size_t) (regs.pc - script->code) <= script->length);
2782 JS_ASSERT((size_t) (regs.sp - StackBase(fp)) <= StackDepth(script));
2783 JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
2784 JS_PROPERTY_CACHE(cx).disabled += js_CountWithBlocks(cx, fp);
2785
2786 /*
2787 * To support generator_throw and to catch ignored exceptions,
2788 * fail if cx->throwing is set.
2789 */
2790 if (cx->throwing) {
2791 #ifdef DEBUG_NOT_THROWING
2792 if (cx->exception != JSVAL_ARETURN) {
2793 printf("JS INTERPRETER CALLED WITH PENDING EXCEPTION %lx\n",
2794 (unsigned long) cx->exception);
2795 }
2796 #endif
2797 goto error;
2798 }
2799 }
2800 #endif /* JS_HAS_GENERATORS */
2801
2802 /*
2803 * It is important that "op" be initialized before calling DO_OP because
2804 * it is possible for "op" to be specially assigned during the normal
2805 * processing of an opcode while looping. We rely on DO_NEXT_OP to manage
2806 * "op" correctly in all other cases.
2807 */
2808 len = 0;
2809 DO_NEXT_OP(len);
2810
2811 #if JS_THREADED_INTERP
2812 /*
2813 * This is a loop, but it does not look like a loop. The loop-closing
2814 * jump is distributed throughout goto *jumpTable[op] inside of DO_OP.
2815 * When interrupts are enabled, jumpTable is set to interruptJumpTable
2816 * where all jumps point to the JSOP_INTERRUPT case. The latter, after
2817 * calling the interrupt handler, dispatches through normalJumpTable to
2818 * continue the normal bytecode processing.
2819 */
2820 #else
2821 for (;;) {
2822 advance_pc_by_one:
2823 JS_ASSERT(js_CodeSpec[op].length == 1);
2824 len = 1;
2825 advance_pc:
2826 regs.pc += len;
2827 op = (JSOp) *regs.pc;
2828 #ifdef DEBUG
2829 if (cx->tracefp)
2830 js_TraceOpcode(cx, len);
2831 #endif
2832
2833 do_op:
2834 switchOp = op & switchMask;
2835 do_switch:
2836 switch (switchOp) {
2837 #endif /* !JS_THREADED_INTERP */
2838
2839 BEGIN_CASE(JSOP_INTERRUPT)
2840 {
2841 JSTrapHandler handler;
2842
2843 handler = cx->debugHooks->interruptHandler;
2844 if (handler) {
2845 switch (handler(cx, script, regs.pc, &rval,
2846 cx->debugHooks->interruptHandlerData)) {
2847 case JSTRAP_ERROR:
2848 goto error;
2849 case JSTRAP_CONTINUE:
2850 break;
2851 case JSTRAP_RETURN:
2852 fp->rval = rval;
2853 ok = JS_TRUE;
2854 goto forced_return;
2855 case JSTRAP_THROW:
2856 cx->throwing = JS_TRUE;
2857 cx->exception = rval;
2858 goto error;
2859 default:;
2860 }
2861 #if !JS_THREADED_INTERP
2862 } else {
2863 /* this was not a real interrupt, the tracer is trying to
2864 record a trace */
2865 switchOp = op + 256;
2866 goto do_switch;
2867 #endif
2868 }
2869 LOAD_INTERRUPT_HANDLER(cx);
2870
2871 #if JS_THREADED_INTERP
2872 JS_EXTENSION_(goto *normalJumpTable[op]);
2873 #else
2874 switchOp = op;
2875 goto do_switch;
2876 #endif
2877 }
2878
2879 /* No-ops for ease of decompilation. */
2880 ADD_EMPTY_CASE(JSOP_NOP)
2881 ADD_EMPTY_CASE(JSOP_CONDSWITCH)
2882 ADD_EMPTY_CASE(JSOP_TRY)
2883 ADD_EMPTY_CASE(JSOP_FINALLY)
2884 #if JS_HAS_XML_SUPPORT
2885 ADD_EMPTY_CASE(JSOP_STARTXML)
2886 ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
2887 #endif
2888 END_EMPTY_CASES
2889
2890 /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
2891 BEGIN_CASE(JSOP_LINENO)
2892 END_CASE(JSOP_LINENO)
2893
2894 BEGIN_CASE(JSOP_PUSH)
2895 PUSH_OPND(JSVAL_VOID);
2896 END_CASE(JSOP_PUSH)
2897
2898 BEGIN_CASE(JSOP_POP)
2899 regs.sp--;
2900 END_CASE(JSOP_POP)
2901
2902 BEGIN_CASE(JSOP_POPN)
2903 regs.sp -= GET_UINT16(regs.pc);
2904 #ifdef DEBUG
2905 JS_ASSERT(StackBase(fp) <= regs.sp);
2906 obj = fp->blockChain;
2907 JS_ASSERT_IF(obj,
2908 OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
2909 <= (size_t) (regs.sp - StackBase(fp)));
2910 for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
2911 clasp = OBJ_GET_CLASS(cx, obj);
2912 if (clasp != &js_BlockClass && clasp != &js_WithClass)
2913 continue;
2914 if (OBJ_GET_PRIVATE(cx, obj) != fp)
2915 break;
2916 JS_ASSERT(StackBase(fp) + OBJ_BLOCK_DEPTH(cx, obj)
2917 + ((clasp == &js_BlockClass)
2918 ? OBJ_BLOCK_COUNT(cx, obj)
2919 : 1)
2920 <= regs.sp);
2921 }
2922 #endif
2923 END_CASE(JSOP_POPN)
2924
2925 BEGIN_CASE(JSOP_SETRVAL)
2926 BEGIN_CASE(JSOP_POPV)
2927 ASSERT_NOT_THROWING(cx);
2928 fp->rval = POP_OPND();
2929 END_CASE(JSOP_POPV)
2930
2931 BEGIN_CASE(JSOP_ENTERWITH)
2932 if (!js_EnterWith(cx, -1))
2933 goto error;
2934
2935 /*
2936 * We must ensure that different "with" blocks have different
2937 * stack depth associated with them. This allows the try handler
2938 * search to properly recover the scope chain. Thus we must keep
2939 * the stack at least at the current level.
2940 *
2941 * We set sp[-1] to the current "with" object to help asserting
2942 * the enter/leave balance in [leavewith].
2943 */
2944 regs.sp[-1] = OBJECT_TO_JSVAL(fp->scopeChain);
2945 END_CASE(JSOP_ENTERWITH)
2946
2947 BEGIN_CASE(JSOP_LEAVEWITH)
2948 JS_ASSERT(regs.sp[-1] == OBJECT_TO_JSVAL(fp->scopeChain));
2949 regs.sp--;
2950 js_LeaveWith(cx);
2951 END_CASE(JSOP_LEAVEWITH)
2952
2953 BEGIN_CASE(JSOP_RETURN)
2954 CHECK_BRANCH();
2955 fp->rval = POP_OPND();
2956 /* FALL THROUGH */
2957
2958 BEGIN_CASE(JSOP_RETRVAL) /* fp->rval already set */
2959 BEGIN_CASE(JSOP_STOP)
2960 /*
2961 * When the inlined frame exits with an exception or an error, ok
2962 * will be false after the inline_return label.
2963 */
2964 ASSERT_NOT_THROWING(cx);
2965
2966 if (fp->imacpc) {
2967 /*
2968 * If we are at the end of an imacro, return to its caller in
2969 * the current frame.
2970 */
2971 JS_ASSERT(op == JSOP_STOP);
2972
2973 end_imacro:
2974 JS_ASSERT((uintN)(regs.sp - fp->slots) <= script->nslots);
2975 regs.pc = fp->imacpc + js_CodeSpec[*fp->imacpc].length;
2976 fp->imacpc = NULL;
2977 atoms = script->atomMap.vector;
2978 op = JSOp(*regs.pc);
2979 DO_OP();
2980 }
2981
2982 JS_ASSERT(regs.sp == StackBase(fp));
2983 if ((fp->flags & JSFRAME_CONSTRUCTING) &&
2984 JSVAL_IS_PRIMITIVE(fp->rval)) {
2985 if (!fp->fun) {
2986 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2987 JSMSG_BAD_NEW_RESULT,
2988 js_ValueToPrintableString(cx, rval));
2989 goto error;
2990 }
2991 fp->rval = OBJECT_TO_JSVAL(fp->thisp);
2992 }
2993 ok = JS_TRUE;
2994 if (inlineCallCount)
2995 inline_return:
2996 {
2997 JSInlineFrame *ifp = (JSInlineFrame *) fp;
2998 void *hookData = ifp->hookData;
2999
3000 JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled == fp->pcDisabledSave);
3001 JS_ASSERT(!fp->blockChain);
3002 JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
3003
3004 if (script->staticDepth < JS_DISPLAY_SIZE)
3005 cx->display[script->staticDepth] = fp->displaySave;
3006
3007 if (hookData) {
3008 JSInterpreterHook hook;
3009 JSBool status;
3010
3011 hook = cx->debugHooks->callHook;
3012 if (hook) {
3013 /*
3014 * Do not pass &ok directly as exposing the address
3015 * inhibits optimizations and uninitialised warnings.
3016 */
3017 status = ok;
3018 hook(cx, fp, JS_FALSE, &status, hookData);
3019 ok = status;
3020 LOAD_INTERRUPT_HANDLER(cx);
3021 }
3022 }
3023
3024 /*
3025 * If fp has a call object, sync values and clear the back-
3026 * pointer. This can happen for a lightweight function if it
3027 * calls eval unexpectedly (in a way that is hidden from the
3028 * compiler). See bug 325540.
3029 */
3030 if (fp->callobj)
3031 ok &= js_PutCallObject(cx, fp);
3032
3033 if (fp->argsobj)
3034 ok &= js_PutArgsObject(cx, fp);
3035
3036 #ifdef INCLUDE_MOZILLA_DTRACE
3037 /* DTrace function return, inlines */
3038 if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
3039 jsdtrace_function_rval(cx, fp, fp->fun);
3040 if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
3041 jsdtrace_function_return(cx, fp, fp->fun);
3042 #endif
3043
3044 /* Restore context version only if callee hasn't set version. */
3045 if (JS_LIKELY(cx->version == currentVersion)) {
3046 currentVersion = ifp->callerVersion;
3047 if (currentVersion != cx->version)
3048 js_SetVersion(cx, currentVersion);
3049 }
3050
3051 /*
3052 * If inline-constructing, replace primitive rval with the new
3053 * object passed in via |this|, and instrument this constructor
3054 * invocation
3055 */
3056 if (fp->flags & JSFRAME_CONSTRUCTING) {
3057 if (JSVAL_IS_PRIMITIVE(fp->rval))
3058 fp->rval = OBJECT_TO_JSVAL(fp->thisp);
3059 JS_RUNTIME_METER(cx->runtime, constructs);
3060 }
3061
3062 /* Restore caller's registers. */
3063 regs = ifp->callerRegs;
3064
3065 /* Store the return value in the caller's operand frame. */
3066 regs.sp -= 1 + (size_t) ifp->frame.argc;
3067 regs.sp[-1] = fp->rval;
3068
3069 /* Restore cx->fp and release the inline frame's space. */
3070 cx->fp = fp = fp->down;
3071 JS_ASSERT(fp->regs == &ifp->callerRegs);
3072 fp->regs = &regs;
3073 JS_ARENA_RELEASE(&cx->stackPool, ifp->mark);
3074
3075 /* Restore the calling script's interpreter registers. */
3076 script = fp->script;
3077 atoms = script->atomMap.vector;
3078
3079 /* Resume execution in the calling frame. */
3080 inlineCallCount--;
3081 if (JS_LIKELY(ok)) {
3082 TRACE_0(LeaveFrame);
3083 JS_ASSERT(js_CodeSpec[*regs.pc].length == JSOP_CALL_LENGTH);
3084 len = JSOP_CALL_LENGTH;
3085 DO_NEXT_OP(len);
3086 }
3087 goto error;
3088 }
3089 goto exit;
3090
3091 BEGIN_CASE(JSOP_DEFAULT)
3092 (void) POP();
3093 /* FALL THROUGH */
3094 BEGIN_CASE(JSOP_GOTO)
3095 len = GET_JUMP_OFFSET(regs.pc);
3096 BRANCH(len);
3097 END_CASE(JSOP_GOTO)
3098
3099 BEGIN_CASE(JSOP_IFEQ)
3100 POP_BOOLEAN(cx, rval, cond);
3101 if (cond == JS_FALSE) {
3102 len = GET_JUMP_OFFSET(regs.pc);
3103 BRANCH(len);
3104 }
3105 END_CASE(JSOP_IFEQ)
3106
3107 BEGIN_CASE(JSOP_IFNE)
3108 POP_BOOLEAN(cx, rval, cond);
3109 if (cond != JS_FALSE) {
3110 len = GET_JUMP_OFFSET(regs.pc);
3111 BRANCH(len);
3112 }
3113 END_CASE(JSOP_IFNE)
3114
3115 BEGIN_CASE(JSOP_OR)
3116 POP_BOOLEAN(cx, rval, cond);
3117 if (cond == JS_TRUE) {
3118 len = GET_JUMP_OFFSET(regs.pc);
3119 PUSH_OPND(rval);
3120 DO_NEXT_OP(len);
3121 }
3122 END_CASE(JSOP_OR)
3123
3124 BEGIN_CASE(JSOP_AND)
3125 POP_BOOLEAN(cx, rval, cond);
3126 if (cond == JS_FALSE) {
3127 len = GET_JUMP_OFFSET(regs.pc);
3128 PUSH_OPND(rval);
3129 DO_NEXT_OP(len);
3130 }
3131 END_CASE(JSOP_AND)
3132
3133 BEGIN_CASE(JSOP_DEFAULTX)
3134 (void) POP();
3135 /* FALL THROUGH */
3136 BEGIN_CASE(JSOP_GOTOX)
3137 len = GET_JUMPX_OFFSET(regs.pc);
3138 BRANCH(len);
3139 END_CASE(JSOP_GOTOX);
3140
3141 BEGIN_CASE(JSOP_IFEQX)
3142 POP_BOOLEAN(cx, rval, cond);
3143 if (cond == JS_FALSE) {
3144 len = GET_JUMPX_OFFSET(regs.pc);
3145 BRANCH(len);
3146 }
3147 END_CASE(JSOP_IFEQX)
3148
3149 BEGIN_CASE(JSOP_IFNEX)
3150 POP_BOOLEAN(cx, rval, cond);
3151 if (cond != JS_FALSE) {
3152 len = GET_JUMPX_OFFSET(regs.pc);
3153 BRANCH(len);
3154 }
3155 END_CASE(JSOP_IFNEX)
3156
3157 BEGIN_CASE(JSOP_ORX)
3158 POP_BOOLEAN(cx, rval, cond);
3159 if (cond == JS_TRUE) {
3160 len = GET_JUMPX_OFFSET(regs.pc);
3161 PUSH_OPND(rval);
3162 DO_NEXT_OP(len);
3163 }
3164 END_CASE(JSOP_ORX)
3165
3166 BEGIN_CASE(JSOP_ANDX)
3167 POP_BOOLEAN(cx, rval, cond);
3168 if (cond == JS_FALSE) {
3169 len = GET_JUMPX_OFFSET(regs.pc);
3170 PUSH_OPND(rval);
3171 DO_NEXT_OP(len);
3172 }
3173 END_CASE(JSOP_ANDX)
3174
3175 /*
3176 * If the index value at sp[n] is not an int that fits in a jsval, it could
3177 * be an object (an XML QName, AttributeName, or AnyName), but only if we are
3178 * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a
3179 * string atom id.
3180 */
3181 #define FETCH_ELEMENT_ID(obj, n, id) \
3182 JS_BEGIN_MACRO \
3183 jsval idval_ = FETCH_OPND(n); \
3184 if (JSVAL_IS_INT(idval_)) { \
3185 id = INT_JSVAL_TO_JSID(idval_); \
3186 } else { \
3187 if (!js_InternNonIntElementId(cx, obj, idval_, &id)) \
3188 goto error; \
3189 regs.sp[n] = ID_TO_VALUE(id); \
3190 } \
3191 JS_END_MACRO
3192
3193 #define TRY_BRANCH_AFTER_COND(cond,spdec) \
3194 JS_BEGIN_MACRO \
3195 uintN diff_; \
3196 JS_ASSERT(js_CodeSpec[op].length == 1); \
3197 diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ; \
3198 if (diff_ <= 1) { \
3199 regs.sp -= spdec; \
3200 if (cond == (diff_ != 0)) { \
3201 ++regs.pc; \
3202 len = GET_JUMP_OFFSET(regs.pc); \
3203 BRANCH(len); \
3204 } \
3205 len = 1 + JSOP_IFEQ_LENGTH; \
3206 DO_NEXT_OP(len); \
3207 } \
3208 JS_END_MACRO
3209
3210 BEGIN_CASE(JSOP_IN)
3211 rval = FETCH_OPND(-1);
3212 if (JSVAL_IS_PRIMITIVE(rval)) {
3213 js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rval, NULL);
3214 goto error;
3215 }
3216 obj = JSVAL_TO_OBJECT(rval);
3217 FETCH_ELEMENT_ID(obj, -2, id);
3218 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
3219 goto error;
3220 cond = prop != NULL;
3221 if (prop)
3222 OBJ_DROP_PROPERTY(cx, obj2, prop);
3223 TRY_BRANCH_AFTER_COND(cond, 2);
3224 regs.sp--;
3225 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));
3226 END_CASE(JSOP_IN)
3227
3228 BEGIN_CASE(JSOP_ITER)
3229 JS_ASSERT(regs.sp > StackBase(fp));
3230 flags = regs.pc[1];
3231 if (!js_ValueToIterator(cx, flags, &regs.sp[-1]))
3232 goto error;
3233 LOAD_INTERRUPT_HANDLER(cx);
3234 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));
3235 PUSH(JSVAL_VOID);
3236 END_CASE(JSOP_ITER)
3237
3238 BEGIN_CASE(JSOP_NEXTITER)
3239 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3240 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-2]));
3241 if (!js_CallIteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-2]), &regs.sp[-1]))
3242 goto error;
3243 LOAD_INTERRUPT_HANDLER(cx);
3244 rval = BOOLEAN_TO_JSVAL(regs.sp[-1] != JSVAL_HOLE);
3245 PUSH(rval);
3246 TRACE_0(IteratorNextComplete);
3247 END_CASE(JSOP_NEXTITER)
3248
3249 BEGIN_CASE(JSOP_ENDITER)
3250 /*
3251 * Decrease the stack pointer even when !ok -- see comments in the
3252 * exception capturing code for details.
3253 */
3254 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3255 ok = js_CloseIterator(cx, regs.sp[-2]);
3256 regs.sp -= 2;
3257 if (!ok)
3258 goto error;
3259 END_CASE(JSOP_ENDITER)
3260
3261 BEGIN_CASE(JSOP_FORARG)
3262 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3263 slot = GET_ARGNO(regs.pc);
3264 JS_ASSERT(slot < fp->fun->nargs);
3265 fp->argv[slot] = regs.sp[-1];
3266 END_CASE(JSOP_FORARG)
3267
3268 BEGIN_CASE(JSOP_FORLOCAL)
3269 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3270 slot = GET_SLOTNO(regs.pc);
3271 JS_ASSERT(slot < fp->script->nslots);
3272 vp = &fp->slots[slot];
3273 GC_POKE(cx, *vp);
3274 *vp = regs.sp[-1];
3275 END_CASE(JSOP_FORLOCAL)
3276
3277 BEGIN_CASE(JSOP_FORNAME)
3278 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3279 LOAD_ATOM(0);
3280 id = ATOM_TO_JSID(atom);
3281 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
3282 goto error;
3283 if (prop)
3284 OBJ_DROP_PROPERTY(cx, obj2, prop);
3285 ok = OBJ_SET_PROPERTY(cx, obj, id, &regs.sp[-1]);
3286 if (!ok)
3287 goto error;
3288 END_CASE(JSOP_FORNAME)
3289
3290 BEGIN_CASE(JSOP_FORPROP)
3291 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3292 LOAD_ATOM(0);
3293 id = ATOM_TO_JSID(atom);
3294 FETCH_OBJECT(cx, -1, lval, obj);
3295 ok = OBJ_SET_PROPERTY(cx, obj, id, &regs.sp[-2]);
3296 if (!ok)
3297 goto error;
3298 regs.sp--;
3299 END_CASE(JSOP_FORPROP)
3300
3301 BEGIN_CASE(JSOP_FORELEM)
3302 /*
3303 * JSOP_FORELEM simply dups the property identifier at top of stack
3304 * and lets the subsequent JSOP_ENUMELEM opcode sequence handle the
3305 * left-hand side expression evaluation and assignment. This opcode
3306 * exists solely to help the decompiler.
3307 */
3308 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3309 rval = FETCH_OPND(-1);
3310 PUSH(rval);
3311 END_CASE(JSOP_FORELEM)
3312
3313 BEGIN_CASE(JSOP_DUP)
3314 JS_ASSERT(regs.sp > StackBase(fp));
3315 rval = FETCH_OPND(-1);
3316 PUSH(rval);
3317 END_CASE(JSOP_DUP)
3318
3319 BEGIN_CASE(JSOP_DUP2)
3320 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3321 lval = FETCH_OPND(-2);
3322 rval = FETCH_OPND(-1);
3323 PUSH(lval);
3324 PUSH(rval);
3325 END_CASE(JSOP_DUP2)
3326
3327 BEGIN_CASE(JSOP_SWAP)
3328 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3329 lval = FETCH_OPND(-2);
3330 rval = FETCH_OPND(-1);
3331 STORE_OPND(-1, lval);
3332 STORE_OPND(-2, rval);
3333 END_CASE(JSOP_SWAP)
3334
3335 #define PROPERTY_OP(n, call) \
3336 JS_BEGIN_MACRO \
3337 /* Fetch the left part and resolve it to a non-null object. */ \
3338 FETCH_OBJECT(cx, n, lval, obj); \
3339 \
3340 /* Get or set the property. */ \
3341 if (!call) \
3342 goto error; \
3343 JS_END_MACRO
3344
3345 #define ELEMENT_OP(n, call) \
3346 JS_BEGIN_MACRO \
3347 /* Fetch the left part and resolve it to a non-null object. */ \
3348 FETCH_OBJECT(cx, n - 1, lval, obj); \
3349 \
3350 /* Fetch index and convert it to id suitable for use with obj. */ \
3351 FETCH_ELEMENT_ID(obj, n, id); \
3352 \
3353 /* Get or set the element. */ \
3354 if (!call) \
3355 goto error; \
3356 JS_END_MACRO
3357
3358 #define NATIVE_GET(cx,obj,pobj,sprop,vp) \
3359 JS_BEGIN_MACRO \
3360 if (SPROP_HAS_STUB_GETTER(sprop)) { \
3361 /* Fast path for Object instance properties. */ \
3362 JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT || \
3363 !SPROP_HAS_STUB_SETTER(sprop)); \
3364 *vp = ((sprop)->slot != SPROP_INVALID_SLOT) \
3365 ? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot) \
3366 : JSVAL_VOID; \
3367 } else { \
3368 if (!js_NativeGet(cx, obj, pobj, sprop, vp)) \
3369 goto error; \
3370 } \
3371 JS_END_MACRO
3372
3373 #define NATIVE_SET(cx,obj,sprop,vp) \
3374 JS_BEGIN_MACRO \
3375 if (SPROP_HAS_STUB_SETTER(sprop) && \
3376 (sprop)->slot != SPROP_INVALID_SLOT) { \
3377 /* Fast path for, e.g., Object instance properties. */ \
3378 LOCKED_OBJ_WRITE_BARRIER(cx, obj, (sprop)->slot, *vp); \
3379 } else { \
3380 if (!js_NativeSet(cx, obj, sprop, vp)) \
3381 goto error; \
3382 } \
3383 JS_END_MACRO
3384
3385 /*
3386 * Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on
3387 * single-thread DEBUG js shell testing to verify property cache hits.
3388 */
3389 #if defined DEBUG && !defined JS_THREADSAFE
3390 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) \
3391 do { \
3392 JSAtom *atom_; \
3393 JSObject *obj_, *pobj_; \
3394 JSProperty *prop_; \
3395 JSScopeProperty *sprop_; \
3396 uint32 sample_ = rt->gcNumber; \
3397 if (pcoff >= 0) \
3398 GET_ATOM_FROM_BYTECODE(script, regs.pc, pcoff, atom_); \
3399 else \
3400 atom_ = rt->atomState.lengthAtom; \
3401 if (JOF_OPMODE(*regs.pc) == JOF_NAME) { \
3402 ok = js_FindProperty(cx, ATOM_TO_JSID(atom_), &obj_, &pobj_, \
3403 &prop_); \
3404 } else { \
3405 obj_ = obj; \
3406 ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(atom_), &pobj_, \
3407 &prop_); \
3408 } \
3409 if (!ok) \
3410 goto error; \
3411 if (rt->gcNumber != sample_) \
3412 break; \
3413 JS_ASSERT(prop_); \
3414 JS_ASSERT(pobj_ == pobj); \
3415 sprop_ = (JSScopeProperty *) prop_; \
3416 if (PCVAL_IS_SLOT(entry->vword)) { \
3417 JS_ASSERT(PCVAL_TO_SLOT(entry->vword) == sprop_->slot); \
3418 } else if (PCVAL_IS_SPROP(entry->vword)) { \
3419 JS_ASSERT(PCVAL_TO_SPROP(entry->vword) == sprop_); \
3420 } else { \
3421 jsval v_; \
3422 JS_ASSERT(PCVAL_IS_OBJECT(entry->vword)); \
3423 JS_ASSERT(entry->vword != PCVAL_NULL); \
3424 JS_ASSERT(SCOPE_IS_BRANDED(OBJ_SCOPE(pobj))); \
3425 JS_ASSERT(SPROP_HAS_STUB_GETTER(sprop_)); \
3426 JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop_, OBJ_SCOPE(pobj_))); \
3427 v_ = LOCKED_OBJ_GET_SLOT(pobj_, sprop_->slot); \
3428 JS_ASSERT(VALUE_IS_FUNCTION(cx, v_)); \
3429 JS_ASSERT(PCVAL_TO_OBJECT(entry->vword) == JSVAL_TO_OBJECT(v_)); \
3430 } \
3431 OBJ_DROP_PROPERTY(cx, pobj_, prop_); \
3432 } while (0)
3433 #else
3434 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0)
3435 #endif
3436
3437 /*
3438 * Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is
3439 * the constant length of the SET opcode sequence, and spdec is the constant
3440 * by which to decrease the stack pointer to pop all of the SET op's operands.
3441 *
3442 * NB: unlike macros that could conceivably be replaced by functions (ignoring
3443 * goto error), where a call should not hav