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

Contents of /trunk/js/jsinterp.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

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


1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=79:
3 *
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
19 *
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
24 *
25 * Contributor(s):
26 *
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
38 *
39 * ***** END LICENSE BLOCK ***** */
40
41 /*
42 * JavaScript bytecode interpreter.
43 */
44 #include "jsstddef.h"
45 #include <stdio.h>
46 #include <string.h>
47 #include <math.h>
48 #include "jstypes.h"
49 #include "jsarena.h" /* Added by JSIFY */
50 #include "jsutil.h" /* Added by JSIFY */
51 #include "jsprf.h"
52 #include "jsapi.h"
53 #include "jsarray.h"
54 #include "jsatom.h"
55 #include "jsbool.h"
56 #include "jscntxt.h"
57 #include "jsversion.h"
58 #include "jsdbgapi.h"
59 #include "jsfun.h"
60 #include "jsgc.h"
61 #include "jsinterp.h"
62 #include "jsiter.h"
63 #include "jslock.h"
64 #include "jsnum.h"
65 #include "jsobj.h"
66 #include "jsopcode.h"
67 #include "jsscan.h"
68 #include "jsscope.h"
69 #include "jsscript.h"
70 #include "jsstr.h"
71 #include "jsstaticcheck.h"
72 #include "jstracer.h"
73
74 #ifdef INCLUDE_MOZILLA_DTRACE
75 #include "jsdtracef.h"
76 #endif
77
78 #if JS_HAS_XML_SUPPORT
79 #include "jsxml.h"
80 #endif
81
82 #include "jsautooplen.h"
83
84 /* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */
85 #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___
86
87 uint32
88 js_GenerateShape(JSContext *cx, JSBool gcLocked)
89 {
90 JSRuntime *rt;
91 uint32 shape;
92
93 rt = cx->runtime;
94 shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);
95 JS_ASSERT(shape != 0);
96 if (shape & SHAPE_OVERFLOW_BIT) {
97 rt->gcPoke = JS_TRUE;
98 js_GC(cx, gcLocked ? GC_LOCK_HELD : GC_NORMAL);
99 shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);
100 JS_ASSERT(shape != 0);
101 JS_ASSERT_IF(shape & SHAPE_OVERFLOW_BIT,
102 JS_PROPERTY_CACHE(cx).disabled);
103 }
104 return shape;
105 }
106
107 void
108 js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
109 uintN scopeIndex, uintN protoIndex,
110 JSObject *pobj, JSScopeProperty *sprop,
111 JSPropCacheEntry **entryp)
112 {
113 JSPropertyCache *cache;
114 jsbytecode *pc;
115 JSScope *scope;
116 JSOp op;
117 const JSCodeSpec *cs;
118 jsuword vword;
119 ptrdiff_t pcoff;
120 jsuword khash;
121 JSAtom *atom;
122 JSPropCacheEntry *entry;
123
124 JS_ASSERT(!cx->runtime->gcRunning);
125 cache = &JS_PROPERTY_CACHE(cx);
126 pc = cx->fp->regs->pc;
127 if (cache->disabled || (cx->fp->flags & JSFRAME_EVAL)) {
128 PCMETER(cache->disfills++);
129 *entryp = NULL;
130 return;
131 }
132
133 /*
134 * Check for fill from js_SetPropertyHelper where the setter removed sprop
135 * from pobj's scope (via unwatch or delete, e.g.).
136 */
137 scope = OBJ_SCOPE(pobj);
138 JS_ASSERT(scope->object == pobj);
139 if (!SCOPE_HAS_PROPERTY(scope, sprop)) {
140 PCMETER(cache->oddfills++);
141 *entryp = NULL;
142 return;
143 }
144
145 /*
146 * Check for overdeep scope and prototype chain. Because resolve, getter,
147 * and setter hooks can change the prototype chain using JS_SetPrototype
148 * after js_LookupPropertyWithFlags has returned the nominal protoIndex,
149 * we have to validate protoIndex if it is non-zero. If it is zero, then
150 * we know thanks to the SCOPE_HAS_PROPERTY test above, and from the fact
151 * that obj == pobj, that protoIndex is invariant.
152 *
153 * The scopeIndex can't be wrong. We require JS_SetParent calls to happen
154 * before any running script might consult a parent-linked scope chain. If
155 * this requirement is not satisfied, the fill in progress will never hit,
156 * but vcap vs. scope shape tests ensure nothing malfunctions.
157 */
158 JS_ASSERT_IF(scopeIndex == 0 && protoIndex == 0, obj == pobj);
159 if (protoIndex != 0) {
160 JSObject *tmp;
161
162 JS_ASSERT(pobj != obj);
163 protoIndex = 1;
164 tmp = obj;
165 for (;;) {
166 tmp = OBJ_GET_PROTO(cx, tmp);
167 if (!tmp) {
168 PCMETER(cache->noprotos++);
169 *entryp = NULL;
170 return;
171 }
172 if (tmp == pobj)
173 break;
174 ++protoIndex;
175 }
176 }
177 if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) {
178 PCMETER(cache->longchains++);
179 *entryp = NULL;
180 return;
181 }
182
183 /*
184 * Optimize the cached vword based on our parameters and the current pc's
185 * opcode format flags.
186 */
187 op = (JSOp) *pc;
188 cs = &js_CodeSpec[op];
189
190 do {
191 /*
192 * Check for a prototype "plain old method" callee computation. What
193 * is a plain old method? It's a function-valued property with stub
194 * getter and setter, so get of a function is idempotent and set is
195 * transparent.
196 */
197 if (cs->format & JOF_CALLOP) {
198 if (SPROP_HAS_STUB_GETTER(sprop) &&
199 SPROP_HAS_VALID_SLOT(sprop, scope)) {
200 jsval v;
201
202 v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
203 if (VALUE_IS_FUNCTION(cx, v)) {
204 /*
205 * Great, we have a function-valued prototype property
206 * where the getter is JS_PropertyStub. The type id in
207 * pobj's scope does not evolve with changes to property
208 * values, however.
209 *
210 * So here, on first cache fill for this method, we brand
211 * the scope with a new shape and set the SCOPE_BRANDED
212 * flag. Once this scope flag is set, any write that adds
213 * or deletes a function-valued plain old property in
214 * scope->object will result in shape being regenerated.
215 */
216 if (!SCOPE_IS_BRANDED(scope)) {
217 PCMETER(cache->brandfills++);
218 #ifdef DEBUG_notme
219 fprintf(stderr,
220 "branding %p (%s) for funobj %p (%s), kshape %lu\n",
221 pobj, LOCKED_OBJ_GET_CLASS(pobj)->name,
222 JSVAL_TO_OBJECT(v),
223 JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx,
224 JSVAL_TO_OBJECT(v))),
225 kshape);
226 #endif
227 SCOPE_MAKE_UNIQUE_SHAPE(cx, scope);
228 SCOPE_SET_BRANDED(scope);
229 kshape = scope->shape;
230 }
231 vword = JSVAL_OBJECT_TO_PCVAL(v);
232 break;
233 }
234 }
235 }
236
237 /* If getting a value via a stub getter, we can cache the slot. */
238 if (!(cs->format & JOF_SET) &&
239 SPROP_HAS_STUB_GETTER(sprop) &&
240 SPROP_HAS_VALID_SLOT(sprop, scope)) {
241 /* Great, let's cache sprop's slot and use it on cache hit. */
242 vword = SLOT_TO_PCVAL(sprop->slot);
243 } else {
244 /* Best we can do is to cache sprop (still a nice speedup). */
245 vword = SPROP_TO_PCVAL(sprop);
246 }
247 } while (0);
248
249 /*
250 * Our caller preserved the scope shape prior to the js_GetPropertyHelper
251 * or similar call out of the interpreter. We want to cache under that
252 * shape if op is overtly mutating, to bias for the case where the mutator
253 * udpates shape predictably.
254 *
255 * Note that an apparently non-mutating op such as JSOP_NAME may still
256 * mutate the base object via, e.g., lazy standard class initialization,
257 * but that is a one-time event and we'll have to miss the old shape and
258 * re-fill under the new one.
259 */
260 if (!(cs->format & (JOF_SET | JOF_INCDEC)) && obj == pobj)
261 kshape = scope->shape;
262
263 khash = PROPERTY_CACHE_HASH_PC(pc, kshape);
264 if (obj == pobj) {
265 JS_ASSERT(kshape != 0 || scope->shape != 0);
266 JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
267 JS_ASSERT(OBJ_SCOPE(obj)->object == obj);
268 } else {
269 if (op == JSOP_LENGTH) {
270 atom = cx->runtime->atomState.lengthAtom;
271 } else {
272 pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? 2 : 0;
273 GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
274 }
275 JS_ASSERT_IF(scopeIndex == 0,
276 protoIndex != 1 || OBJ_GET_PROTO(cx, obj) == pobj);
277 if (scopeIndex != 0 || protoIndex != 1) {
278 khash = PROPERTY_CACHE_HASH_ATOM(atom, obj, pobj);
279 PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1)
280 cache->pcrecycles++);
281 pc = (jsbytecode *) atom;
282 kshape = (jsuword) obj;
283 }
284 }
285
286 entry = &cache->table[khash];
287 PCMETER(if (entry != *entryp) cache->modfills++);
288 PCMETER(if (!PCVAL_IS_NULL(entry->vword)) cache->recycles++);
289 entry->kpc = pc;
290 entry->kshape = kshape;
291 entry->vcap = PCVCAP_MAKE(scope->shape, scopeIndex, protoIndex);
292 entry->vword = vword;
293 *entryp = entry;
294
295 cache->empty = JS_FALSE;
296 PCMETER(cache->fills++);
297 }
298
299 JSAtom *
300 js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
301 JSObject **objp, JSObject **pobjp,
302 JSPropCacheEntry **entryp)
303 {
304 JSOp op;
305 const JSCodeSpec *cs;
306 ptrdiff_t pcoff;
307 JSAtom *atom;
308 JSObject *obj, *pobj, *tmp;
309 JSPropCacheEntry *entry;
310 uint32 vcap;
311
312 JS_ASSERT(JS_UPTRDIFF(pc, cx->fp->script->code) < cx->fp->script->length);
313
314 op = (JSOp) *pc;
315 cs = &js_CodeSpec[op];
316 if (op == JSOP_LENGTH) {
317 atom = cx->runtime->atomState.lengthAtom;
318 } else {
319 pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? 2 : 0;
320 GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
321 }
322
323 obj = *objp;
324 JS_ASSERT(OBJ_IS_NATIVE(obj));
325 entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_ATOM(atom, obj, NULL)];
326 *entryp = entry;
327 vcap = entry->vcap;
328
329 if (entry->kpc != (jsbytecode *) atom) {
330 PCMETER(JS_PROPERTY_CACHE(cx).idmisses++);
331
332 #ifdef DEBUG_notme
333 entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_PC(pc, OBJ_SHAPE(obj))];
334 fprintf(stderr,
335 "id miss for %s from %s:%u"
336 " (pc %u, kpc %u, kshape %u, shape %u)\n",
337 js_AtomToPrintableString(cx, atom),
338 cx->fp->script->filename,
339 js_PCToLineNumber(cx, cx->fp->script, pc),
340 pc - cx->fp->script->code,
341 entry->kpc - cx->fp->script->code,
342 entry->kshape,
343 OBJ_SHAPE(obj));
344 js_Disassemble1(cx, cx->fp->script, pc,
345 PTRDIFF(pc, cx->fp->script->code, jsbytecode),
346 JS_FALSE, stderr);
347 #endif
348
349 return atom;
350 }
351
352 if (entry->kshape != (jsuword) obj) {
353 PCMETER(JS_PROPERTY_CACHE(cx).komisses++);
354 return atom;
355 }
356
357 pobj = obj;
358 JS_LOCK_OBJ(cx, pobj);
359
360 if (JOF_MODE(cs->format) == JOF_NAME) {
361 while (vcap & (PCVCAP_SCOPEMASK << PCVCAP_PROTOBITS)) {
362 tmp = LOCKED_OBJ_GET_PARENT(pobj);
363 if (!tmp || !OBJ_IS_NATIVE(tmp))
364 break;
365 JS_UNLOCK_OBJ(cx, pobj);
366 pobj = tmp;
367 JS_LOCK_OBJ(cx, pobj);
368 vcap -= PCVCAP_PROTOSIZE;
369 }
370
371 *objp = pobj;
372 }
373
374 while (vcap & PCVCAP_PROTOMASK) {
375 tmp = LOCKED_OBJ_GET_PROTO(pobj);
376 if (!tmp || !OBJ_IS_NATIVE(tmp))
377 break;
378 JS_UNLOCK_OBJ(cx, pobj);
379 pobj = tmp;
380 JS_LOCK_OBJ(cx, pobj);
381 --vcap;
382 }
383
384 if (PCVCAP_SHAPE(vcap) == OBJ_SHAPE(pobj)) {
385 #ifdef DEBUG
386 jsid id = ATOM_TO_JSID(atom);
387
388 CHECK_FOR_STRING_INDEX(id);
389 JS_ASSERT(SCOPE_GET_PROPERTY(OBJ_SCOPE(pobj), id));
390 JS_ASSERT(OBJ_SCOPE(pobj)->object == pobj);
391 #endif
392 *pobjp = pobj;
393 return NULL;
394 }
395
396 PCMETER(JS_PROPERTY_CACHE(cx).vcmisses++);
397 JS_UNLOCK_OBJ(cx, pobj);
398 return atom;
399 }
400
401 #ifdef DEBUG
402 #define ASSERT_CACHE_IS_EMPTY(cache) \
403 JS_BEGIN_MACRO \
404 JSPropertyCache *cache_ = (cache); \
405 uintN i_; \
406 JS_ASSERT(cache_->empty); \
407 for (i_ = 0; i_ < PROPERTY_CACHE_SIZE; i_++) { \
408 JS_ASSERT(!cache_->table[i_].kpc); \
409 JS_ASSERT(!cache_->table[i_].kshape); \
410 JS_ASSERT(!cache_->table[i_].vcap); \
411 JS_ASSERT(!cache_->table[i_].vword); \
412 } \
413 JS_END_MACRO
414 #else
415 #define ASSERT_CACHE_IS_EMPTY(cache) ((void)0)
416 #endif
417
418 JS_STATIC_ASSERT(PCVAL_NULL == 0);
419
420 void
421 js_FlushPropertyCache(JSContext *cx)
422 {
423 JSPropertyCache *cache;
424
425 cache = &JS_PROPERTY_CACHE(cx);
426 if (cache->empty) {
427 ASSERT_CACHE_IS_EMPTY(cache);
428 return;
429 }
430
431 memset(cache->table, 0, sizeof cache->table);
432 cache->empty = JS_TRUE;
433
434 #ifdef JS_PROPERTY_CACHE_METERING
435 { static FILE *fp;
436 if (!fp)
437 fp = fopen("/tmp/propcache.stats", "w");
438 if (fp) {
439 fputs("Property cache stats for ", fp);
440 #ifdef JS_THREADSAFE
441 fprintf(fp, "thread %lu, ", (unsigned long) cx->thread->id);
442 #endif
443 fprintf(fp, "GC %u\n", cx->runtime->gcNumber);
444
445 # define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)cache->mem)
446 P(fills);
447 P(nofills);
448 P(rofills);
449 P(disfills);
450 P(oddfills);
451 P(modfills);
452 P(brandfills);
453 P(noprotos);
454 P(longchains);
455 P(recycles);
456 P(pcrecycles);
457 P(tests);
458 P(pchits);
459 P(protopchits);
460 P(initests);
461 P(inipchits);
462 P(inipcmisses);
463 P(settests);
464 P(addpchits);
465 P(setpchits);
466 P(setpcmisses);
467 P(slotchanges);
468 P(setmisses);
469 P(idmisses);
470 P(komisses);
471 P(vcmisses);
472 P(misses);
473 P(flushes);
474 P(pcpurges);
475 # undef P
476
477 fprintf(fp, "hit rates: pc %g%% (proto %g%%), set %g%%, ini %g%%, full %g%%\n",
478 (100. * cache->pchits) / cache->tests,
479 (100. * cache->protopchits) / cache->tests,
480 (100. * (cache->addpchits + cache->setpchits))
481 / cache->settests,
482 (100. * cache->inipchits) / cache->initests,
483 (100. * (cache->tests - cache->misses)) / cache->tests);
484 fflush(fp);
485 }
486 }
487 #endif
488
489 PCMETER(cache->flushes++);
490 }
491
492 void
493 js_FlushPropertyCacheForScript(JSContext *cx, JSScript *script)
494 {
495 JSPropertyCache *cache;
496 JSPropCacheEntry *entry;
497
498 cache = &JS_PROPERTY_CACHE(cx);
499 for (entry = cache->table; entry < cache->table + PROPERTY_CACHE_SIZE;
500 entry++) {
501 if (JS_UPTRDIFF(entry->kpc, script->code) < script->length) {
502 entry->kpc = NULL;
503 entry->kshape = 0;
504 #ifdef DEBUG
505 entry->vcap = entry->vword = 0;
506 #endif
507 }
508 }
509 }
510
511 void
512 js_DisablePropertyCache(JSContext *cx)
513 {
514 JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
515 ++JS_PROPERTY_CACHE(cx).disabled;
516 }
517
518 void
519 js_EnablePropertyCache(JSContext *cx)
520 {
521 --JS_PROPERTY_CACHE(cx).disabled;
522 JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
523 }
524
525 /*
526 * Check if the current arena has enough space to fit nslots after sp and, if
527 * so, reserve the necessary space.
528 */
529 static JSBool
530 AllocateAfterSP(JSContext *cx, jsval *sp, uintN nslots)
531 {
532 uintN surplus;
533 jsval *sp2;
534
535 JS_ASSERT((jsval *) cx->stackPool.current->base <= sp);
536 JS_ASSERT(sp <= (jsval *) cx->stackPool.current->avail);
537 surplus = (jsval *) cx->stackPool.current->avail - sp;
538 if (nslots <= surplus)
539 return JS_TRUE;
540
541 /*
542 * No room before current->avail, check if the arena has enough space to
543 * fit the missing slots before the limit.
544 */
545 if (nslots > (size_t) ((jsval *) cx->stackPool.current->limit - sp))
546 return JS_FALSE;
547
548 JS_ARENA_ALLOCATE_CAST(sp2, jsval *, &cx->stackPool,
549 (nslots - surplus) * sizeof(jsval));
550 JS_ASSERT(sp2 == sp + surplus);
551 return JS_TRUE;
552 }
553
554 JS_STATIC_INTERPRET jsval *
555 js_AllocRawStack(JSContext *cx, uintN nslots, void **markp)
556 {
557 jsval *sp;
558
559 if (!cx->stackPool.first.next) {
560 int64 *timestamp;
561
562 JS_ARENA_ALLOCATE_CAST(timestamp, int64 *,
563 &cx->stackPool, sizeof *timestamp);
564 if (!timestamp) {
565 js_ReportOutOfScriptQuota(cx);
566 return NULL;
567 }
568 *timestamp = JS_Now();
569 }
570
571 if (markp)
572 *markp = JS_ARENA_MARK(&cx->stackPool);
573 JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval));
574 if (!sp)
575 js_ReportOutOfScriptQuota(cx);
576 return sp;
577 }
578
579 JS_STATIC_INTERPRET void
580 js_FreeRawStack(JSContext *cx, void *mark)
581 {
582 JS_ARENA_RELEASE(&cx->stackPool, mark);
583 }
584
585 JS_FRIEND_API(jsval *)
586 js_AllocStack(JSContext *cx, uintN nslots, void **markp)
587 {
588 jsval *sp;
589 JSArena *a;
590 JSStackHeader *sh;
591
592 /* Callers don't check for zero nslots: we do to avoid empty segments. */
593 if (nslots == 0) {
594 *markp = NULL;
595 return (jsval *) JS_ARENA_MARK(&cx->stackPool);
596 }
597
598 /* Allocate 2 extra slots for the stack segment header we'll likely need. */
599 sp = js_AllocRawStack(cx, 2 + nslots, markp);
600 if (!sp)
601 return NULL;
602
603 /* Try to avoid another header if we can piggyback on the last segment. */
604 a = cx->stackPool.current;
605 sh = cx->stackHeaders;
606 if (sh && JS_STACK_SEGMENT(sh) + sh->nslots == sp) {
607 /* Extend the last stack segment, give back the 2 header slots. */
608 sh->nslots += nslots;
609 a->avail -= 2 * sizeof(jsval);
610 } else {
611 /*
612 * Need a new stack segment, so allocate and push a stack segment
613 * header from the 2 extra slots.
614 */
615 sh = (JSStackHeader *)sp;
616 sh->nslots = nslots;
617 sh->down = cx->stackHeaders;
618 cx->stackHeaders = sh;
619 sp += 2;
620 }
621
622 /*
623 * Store JSVAL_NULL using memset, to let compilers optimize as they see
624 * fit, in case a caller allocates and pushes GC-things one by one, which
625 * could nest a last-ditch GC that will scan this segment.
626 */
627 memset(sp, 0, nslots * sizeof(jsval));
628 return sp;
629 }
630
631 JS_FRIEND_API(void)
632 js_FreeStack(JSContext *cx, void *mark)
633 {
634 JSStackHeader *sh;
635 jsuword slotdiff;
636
637 /* Check for zero nslots allocation special case. */
638 if (!mark)
639 return;
640
641 /* We can assert because js_FreeStack always balances js_AllocStack. */
642 sh = cx->stackHeaders;
643 JS_ASSERT(sh);
644
645 /* If mark is in the current segment, reduce sh->nslots, else pop sh. */
646 slotdiff = JS_UPTRDIFF(mark, JS_STACK_SEGMENT(sh)) / sizeof(jsval);
647 if (slotdiff < (jsuword)sh->nslots)
648 sh->nslots = slotdiff;
649 else
650 cx->stackHeaders = sh->down;
651
652 /* Release the stackPool space allocated since mark was set. */
653 JS_ARENA_RELEASE(&cx->stackPool, mark);
654 }
655
656 JSObject *
657 js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
658 {
659 JSObject *obj, *cursor, *clonedChild, *parent;
660 JSTempValueRooter tvr;
661
662 obj = fp->blockChain;
663 if (!obj) {
664 /*
665 * Don't force a call object for a lightweight function call, but do
666 * insist that there is a call object for a heavyweight function call.
667 */
668 JS_ASSERT(!fp->fun ||
669 !(fp->fun->flags & JSFUN_HEAVYWEIGHT) ||
670 fp->callobj);
671 JS_ASSERT(fp->scopeChain);
672 return fp->scopeChain;
673 }
674
675 /*
676 * We have one or more lexical scopes to reflect into fp->scopeChain, so
677 * make sure there's a call object at the current head of the scope chain,
678 * if this frame is a call frame.
679 */
680 if (fp->fun && !fp->callobj) {
681 JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass ||
682 OBJ_GET_PRIVATE(cx, fp->scopeChain) != fp);
683 if (!js_GetCallObject(cx, fp, fp->scopeChain))
684 return NULL;
685 }
686
687 /*
688 * Clone the block chain. To avoid recursive cloning we set the parent of
689 * the cloned child after we clone the parent. In the following loop when
690 * clonedChild is null it indicates the first iteration when no special GC
691 * rooting is necessary. On the second and the following iterations we
692 * have to protect cloned so far chain against the GC during cloning of
693 * the cursor object.
694 */
695 cursor = obj;
696 clonedChild = NULL;
697 for (;;) {
698 parent = OBJ_GET_PARENT(cx, cursor);
699
700 /*
701 * We pass fp->scopeChain and not null even if we override the parent
702 * slot later as null triggers useless calculations of slot's value in
703 * js_NewObject that js_CloneBlockObject calls.
704 */
705 cursor = js_CloneBlockObject(cx, cursor, fp->scopeChain, fp);
706 if (!cursor) {
707 if (clonedChild)
708 JS_POP_TEMP_ROOT(cx, &tvr);
709 return NULL;
710 }
711 if (!clonedChild) {
712 /*
713 * The first iteration. Check if other follow and root obj if so
714 * to protect the whole cloned chain against GC.
715 */
716 obj = cursor;
717 if (!parent)
718 break;
719 JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
720 } else {
721 /*
722 * Avoid OBJ_SET_PARENT overhead as clonedChild cannot escape to
723 * other threads.
724 */
725 STOBJ_SET_PARENT(clonedChild, cursor);
726 if (!parent) {
727 JS_ASSERT(tvr.u.value == OBJECT_TO_JSVAL(obj));
728 JS_POP_TEMP_ROOT(cx, &tvr);
729 break;
730 }
731 }
732 clonedChild = cursor;
733 cursor = parent;
734 }
735 fp->flags |= JSFRAME_POP_BLOCKS;
736 fp->scopeChain = obj;
737 fp->blockChain = NULL;
738 return obj;
739 }
740
741 JSBool
742 js_GetPrimitiveThis(JSContext *cx, jsval *vp, JSClass *clasp, jsval *thisvp)
743 {
744 jsval v;
745 JSObject *obj;
746
747 v = vp[1];
748 if (JSVAL_IS_OBJECT(v)) {
749 obj = JS_THIS_OBJECT(cx, vp);
750 if (!JS_InstanceOf(cx, obj, clasp, vp + 2))
751 return JS_FALSE;
752 v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
753 }
754 *thisvp = v;
755 return JS_TRUE;
756 }
757
758 /*
759 * ECMA requires "the global object", but in embeddings such as the browser,
760 * which have multiple top-level objects (windows, frames, etc. in the DOM),
761 * we prefer fun's parent. An example that causes this code to run:
762 *
763 * // in window w1
764 * function f() { return this }
765 * function g() { return f }
766 *
767 * // in window w2
768 * var h = w1.g()
769 * alert(h() == w1)
770 *
771 * The alert should display "true".
772 */
773 JS_STATIC_INTERPRET JSObject *
774 js_ComputeGlobalThis(JSContext *cx, JSBool lazy, jsval *argv)
775 {
776 JSObject *thisp;
777
778 if (JSVAL_IS_PRIMITIVE(argv[-2]) ||
779 !OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]))) {
780 thisp = cx->globalObject;
781 } else {
782 JSStackFrame *fp;
783 jsid id;
784 jsval v;
785 uintN attrs;
786 JSBool ok;
787 JSObject *parent;
788
789 /*
790 * Walk up the parent chain, first checking that the running script
791 * has access to the callee's parent object. Note that if lazy, the
792 * running script whose principals we want to check is the script
793 * associated with fp->down, not with fp.
794 *
795 * FIXME: 417851 -- this access check should not be required, as it
796 * imposes a performance penalty on all js_ComputeGlobalThis calls,
797 * and it represents a maintenance hazard.
798 */
799 fp = cx->fp; /* quell GCC overwarning */
800 if (lazy) {
801 JS_ASSERT(fp->argv == argv);
802 fp->dormantNext = cx->dormantFrameChain;
803 cx->dormantFrameChain = fp;
804 cx->fp = fp->down;
805 fp->down = NULL;
806 }
807 thisp = JSVAL_TO_OBJECT(argv[-2]);
808 id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
809
810 ok = OBJ_CHECK_ACCESS(cx, thisp, id, JSACC_PARENT, &v, &attrs);
811 if (lazy) {
812 cx->dormantFrameChain = fp->dormantNext;
813 fp->dormantNext = NULL;
814 fp->down = cx->fp;
815 cx->fp = fp;
816 }
817 if (!ok)
818 return NULL;
819
820 thisp = JSVAL_IS_VOID(v)
821 ? OBJ_GET_PARENT(cx, thisp)
822 : JSVAL_TO_OBJECT(v);
823 while ((parent = OBJ_GET_PARENT(cx, thisp)) != NULL)
824 thisp = parent;
825 }
826
827 OBJ_TO_OUTER_OBJECT(cx, thisp);
828 if (!thisp)
829 return NULL;
830 argv[-1] = OBJECT_TO_JSVAL(thisp);
831 return thisp;
832 }
833
834 static JSObject *
835 ComputeThis(JSContext *cx, JSBool lazy, jsval *argv)
836 {
837 JSObject *thisp;
838
839 JS_ASSERT(!JSVAL_IS_NULL(argv[-1]));
840 if (!JSVAL_IS_OBJECT(argv[-1])) {
841 if (!js_PrimitiveToObject(cx, &argv[-1]))
842 return NULL;
843 thisp = JSVAL_TO_OBJECT(argv[-1]);
844 } else {
845 thisp = JSVAL_TO_OBJECT(argv[-1]);
846 if (OBJ_GET_CLASS(cx, thisp) == &js_CallClass)
847 return js_ComputeGlobalThis(cx, lazy, argv);
848
849 if (thisp->map->ops->thisObject) {
850 /* Some objects (e.g., With) delegate 'this' to another object. */
851 thisp = thisp->map->ops->thisObject(cx, thisp);
852 if (!thisp)
853 return NULL;
854 }
855 OBJ_TO_OUTER_OBJECT(cx, thisp);
856 if (!thisp)
857 return NULL;
858 argv[-1] = OBJECT_TO_JSVAL(thisp);
859 }
860 return thisp;
861 }
862
863 JSObject *
864 js_ComputeThis(JSContext *cx, JSBool lazy, jsval *argv)
865 {
866 if (JSVAL_IS_NULL(argv[-1]))
867 return js_ComputeGlobalThis(cx, lazy, argv);
868 return ComputeThis(cx, lazy, argv);
869 }
870
871 #if JS_HAS_NO_SUCH_METHOD
872
873 #define JSSLOT_FOUND_FUNCTION JSSLOT_PRIVATE
874 #define JSSLOT_SAVED_ID (JSSLOT_PRIVATE + 1)
875
876 JSClass js_NoSuchMethodClass = {
877 "NoSuchMethod",
878 JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS |
879 JSCLASS_HAS_CACHED_PROTO(JSProto_NoSuchMethod),
880 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
881 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
882 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
883 };
884
885 JS_BEGIN_EXTERN_C
886
887 JSObject*
888 js_InitNoSuchMethodClass(JSContext *cx, JSObject* obj);
889
890 JS_END_EXTERN_C
891
892 JSObject*
893 js_InitNoSuchMethodClass(JSContext *cx, JSObject* obj)
894 {
895 JSObject *proto;
896
897 proto = JS_InitClass(cx, obj, NULL, &js_NoSuchMethodClass, NULL, 0, NULL,
898 NULL, NULL, NULL);
899 if (!proto)
900 return NULL;
901
902 OBJ_CLEAR_PROTO(cx, proto);
903 return proto;
904 }
905
906 /*
907 * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of
908 * the base object, we search for the __noSuchMethod__ method in the base.
909 * If it exists, we store the method and the property's id into an object of
910 * NoSuchMethod class and store this object into the callee's stack slot.
911 * Later, js_Invoke will recognise such an object and transfer control to
912 * NoSuchMethod that invokes the method like:
913 *
914 * this.__noSuchMethod__(id, args)
915 *
916 * where id is the name of the method that this invocation attempted to
917 * call by name, and args is an Array containing this invocation's actual
918 * parameters.
919 */
920 JS_STATIC_INTERPRET JSBool
921 js_OnUnknownMethod(JSContext *cx, jsval *vp)
922 {
923 JSObject *obj;
924 jsid id;
925 JSTempValueRooter tvr;
926 JSBool ok;
927
928 JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
929 obj = JSVAL_TO_OBJECT(vp[1]);
930 JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
931
932 MUST_FLOW_THROUGH("out");
933 id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
934 #if JS_HAS_XML_SUPPORT
935 if (OBJECT_IS_XML(cx, obj)) {
936 JSXMLObjectOps *ops;
937
938 ops = (JSXMLObjectOps *) obj->map->ops;
939 obj = ops->getMethod(cx, obj, id, &tvr.u.value);
940 if (!obj) {
941 ok = JS_FALSE;
942 goto out;
943 }
944 vp[1] = OBJECT_TO_JSVAL(obj);
945 } else
946 #endif
947 {
948 ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value);
949 if (!ok)
950 goto out;
951 }
952 if (JSVAL_IS_PRIMITIVE(tvr.u.value)) {
953 vp[0] = tvr.u.value;
954 } else {
955 #if JS_HAS_XML_SUPPORT
956 /* Extract the function name from function::name qname. */
957 if (!JSVAL_IS_PRIMITIVE(vp[0])) {
958 obj = JSVAL_TO_OBJECT(vp[0]);
959 ok = js_IsFunctionQName(cx, obj, &id);
960 if (!ok)
961 goto out;
962 if (id != 0)
963 vp[0] = ID_TO_VALUE(id);
964 }
965 #endif
966 obj = js_NewObject(cx, &js_NoSuchMethodClass, NULL, NULL, 0);
967 if (!obj) {
968 ok = JS_FALSE;
969 goto out;
970 }
971 obj->fslots[JSSLOT_FOUND_FUNCTION] = tvr.u.value;
972 obj->fslots[JSSLOT_SAVED_ID] = vp[0];
973 vp[0] = OBJECT_TO_JSVAL(obj);
974 }
975 ok = JS_TRUE;
976
977 out:
978 JS_POP_TEMP_ROOT(cx, &tvr);
979 return ok;
980 }
981
982 static JSBool
983 NoSuchMethod(JSContext *cx, uintN argc, jsval *vp, uint32 flags)
984 {
985 jsval *invokevp;
986 void *mark;
987 JSBool ok;
988 JSObject *obj, *argsobj;
989
990 invokevp = js_AllocStack(cx, 2 + 2, &mark);
991 if (!invokevp)
992 return JS_FALSE;
993
994 JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[0]));
995 JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
996 obj = JSVAL_TO_OBJECT(vp[0]);
997 JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NoSuchMethodClass);
998
999 invokevp[0] = obj->fslots[JSSLOT_FOUND_FUNCTION];
1000 invokevp[1] = vp[1];
1001 invokevp[2] = obj->fslots[JSSLOT_SAVED_ID];
1002 argsobj = js_NewArrayObject(cx, argc, vp + 2);
1003 if (!argsobj) {
1004 ok = JS_FALSE;
1005 } else {
1006 invokevp[3] = OBJECT_TO_JSVAL(argsobj);
1007 ok = (flags & JSINVOKE_CONSTRUCT)
1008 ? js_InvokeConstructor(cx, 2, JS_TRUE, invokevp)
1009 : js_Invoke(cx, 2, invokevp, flags);
1010 vp[0] = invokevp[0];
1011 }
1012 js_FreeStack(cx, mark);
1013 return ok;
1014 }
1015
1016 #endif /* JS_HAS_NO_SUCH_METHOD */
1017
1018 /*
1019 * We check if the function accepts a primitive value as |this|. For that we
1020 * use a table that maps value's tag into the corresponding function flag.
1021 */
1022 JS_STATIC_ASSERT(JSVAL_INT == 1);
1023 JS_STATIC_ASSERT(JSVAL_DOUBLE == 2);
1024 JS_STATIC_ASSERT(JSVAL_STRING == 4);
1025 JS_STATIC_ASSERT(JSVAL_BOOLEAN == 6);
1026
1027 const uint16 js_PrimitiveTestFlags[] = {
1028 JSFUN_THISP_NUMBER, /* INT */
1029 JSFUN_THISP_NUMBER, /* DOUBLE */
1030 JSFUN_THISP_NUMBER, /* INT */
1031 JSFUN_THISP_STRING, /* STRING */
1032 JSFUN_THISP_NUMBER, /* INT */
1033 JSFUN_THISP_BOOLEAN, /* BOOLEAN */
1034 JSFUN_THISP_NUMBER /* INT */
1035 };
1036
1037 /*
1038 * Find a function reference and its 'this' object implicit first parameter
1039 * under argc arguments on cx's stack, and call the function. Push missing
1040 * required arguments, allocate declared local variables, and pop everything
1041 * when done. Then push the return value.
1042 */
1043 JS_FRIEND_API(JSBool)
1044 js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags)
1045 {
1046 void *mark;
1047 JSStackFrame frame;
1048 jsval *sp, *argv, *newvp;
1049 jsval v;
1050 JSObject *funobj, *parent;
1051 JSBool ok;
1052 JSClass *clasp;
1053 JSObjectOps *ops;
1054 JSNative native;
1055 JSFunction *fun;
1056 JSScript *script;
1057 uintN nslots, i;
1058 uint32 rootedArgsFlag;
1059 JSInterpreterHook hook;
1060 void *hookData;
1061
1062 /* [vp .. vp + 2 + argc) must belong to the last JS stack arena. */
1063 JS_ASSERT((jsval *) cx->stackPool.current->base <= vp);
1064 JS_ASSERT(vp + 2 + argc <= (jsval *) cx->stackPool.current->avail);
1065
1066 /*
1067 * Mark the top of stack and load frequently-used registers. After this
1068 * point the control should flow through label out2: to return.
1069 */
1070 mark = JS_ARENA_MARK(&cx->stackPool);
1071 v = *vp;
1072
1073 if (JSVAL_IS_PRIMITIVE(v))
1074 goto bad;
1075
1076 funobj = JSVAL_TO_OBJECT(v);
1077 parent = OBJ_GET_PARENT(cx, funobj);
1078 clasp = OBJ_GET_CLASS(cx, funobj);
1079 if (clasp != &js_FunctionClass) {
1080 #if JS_HAS_NO_SUCH_METHOD
1081 if (clasp == &js_NoSuchMethodClass) {
1082 ok = NoSuchMethod(cx, argc, vp, flags);
1083 goto out2;
1084 }
1085 #endif
1086
1087 /* Function is inlined, all other classes use object ops. */
1088 ops = funobj->map->ops;
1089
1090 /*
1091 * XXX this makes no sense -- why convert to function if clasp->call?
1092 * XXX better to call that hook without converting
1093 * XXX the only thing that needs fixing is liveconnect
1094 *
1095 * Try converting to function, for closure and API compatibility.
1096 * We attempt the conversion under all circumstances for 1.2, but
1097 * only if there is a call op defined otherwise.
1098 */
1099 if ((ops == &js_ObjectOps) ? clasp->call : ops->call) {
1100 ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v);
1101 if (!ok)
1102 goto out2;
1103
1104 if (VALUE_IS_FUNCTION(cx, v)) {
1105 /* Make vp refer to funobj to keep it available as argv[-2]. */
1106 *vp = v;
1107 funobj = JSVAL_TO_OBJECT(v);
1108 parent = OBJ_GET_PARENT(cx, funobj);
1109 goto have_fun;
1110 }
1111 }
1112 fun = NULL;
1113 script = NULL;
1114 nslots = 0;
1115
1116 /* Try a call or construct native object op. */
1117 if (flags & JSINVOKE_CONSTRUCT) {
1118 if (!JSVAL_IS_OBJECT(vp[1])) {
1119 ok = js_PrimitiveToObject(cx, &vp[1]);
1120 if (!ok)
1121 goto out2;
1122 }
1123 native = ops->construct;
1124 } else {
1125 native = ops->call;
1126 }
1127 if (!native)
1128 goto bad;
1129 } else {
1130 have_fun:
1131 /* Get private data and set derived locals from it. */
1132 fun = GET_FUNCTION_PRIVATE(cx, funobj);
1133 nslots = FUN_MINARGS(fun);
1134 nslots = (nslots > argc) ? nslots - argc : 0;
1135 if (FUN_INTERPRETED(fun)) {
1136 native = NULL;
1137 script = fun->u.i.script;
1138 } else {
1139 native = fun->u.n.native;
1140 script = NULL;
1141 nslots += fun->u.n.extra;
1142 }
1143
1144 if (JSFUN_BOUND_METHOD_TEST(fun->flags)) {
1145 /* Handle bound method special case. */
1146 vp[1] = OBJECT_TO_JSVAL(parent);
1147 } else if (!JSVAL_IS_OBJECT(vp[1])) {
1148 JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT));
1149 if (PRIMITIVE_THIS_TEST(fun, vp[1]))
1150 goto start_call;
1151 }
1152 }
1153
1154 if (flags & JSINVOKE_CONSTRUCT) {
1155 JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
1156 } else {
1157 /*
1158 * We must call js_ComputeThis in case we are not called from the
1159 * interpreter, where a prior bytecode has computed an appropriate
1160 * |this| already.
1161 *
1162 * But we need to compute |this| eagerly only for so-called "slow"
1163 * (i.e., not fast) native functions. Fast natives must use either
1164 * JS_THIS or JS_THIS_OBJECT, and scripted functions will go through
1165 * the appropriate this-computing bytecode, e.g., JSOP_THIS.
1166 */
1167 if (native && (!fun || !(fun->flags & JSFUN_FAST_NATIVE))) {
1168 if (!js_ComputeThis(cx, JS_FALSE, vp + 2)) {
1169 ok = JS_FALSE;
1170 goto out2;
1171 }
1172 flags |= JSFRAME_COMPUTED_THIS;
1173 }
1174 }
1175
1176 start_call:
1177 if (native && fun && (fun->flags & JSFUN_FAST_NATIVE)) {
1178 #ifdef DEBUG_NOT_THROWING
1179 JSBool alreadyThrowing = cx->throwing;
1180 #endif
1181 JS_ASSERT(nslots == 0);
1182 #if JS_HAS_LVALUE_RETURN
1183 /* Set by JS_SetCallReturnValue2, used to return reference types. */
1184 cx->rval2set = JS_FALSE;
1185 #endif
1186 ok = ((JSFastNative) native)(cx, argc, vp);
1187 JS_RUNTIME_METER(cx->runtime, nativeCalls);
1188 #ifdef DEBUG_NOT_THROWING
1189 if (ok && !alreadyThrowing)
1190 ASSERT_NOT_THROWING(cx);
1191 #endif
1192 goto out2;
1193 }
1194
1195 argv = vp + 2;
1196 sp = argv + argc;
1197
1198 rootedArgsFlag = JSFRAME_ROOTED_ARGV;
1199 if (nslots != 0) {
1200 /*
1201 * The extra slots required by the function continue with argument
1202 * slots. Thus, when the last stack pool arena does not have room to
1203 * fit nslots right after sp and AllocateAfterSP fails, we have to copy
1204 * [vp..vp+2+argc) slots and clear rootedArgsFlag to root the copy.
1205 */
1206 if (!AllocateAfterSP(cx, sp, nslots)) {
1207 rootedArgsFlag = 0;
1208 newvp = js_AllocRawStack(cx, 2 + argc + nslots, NULL);
1209 if (!newvp) {
1210 ok = JS_FALSE;
1211 goto out2;
1212 }
1213 memcpy(newvp, vp, (2 + argc) * sizeof(jsval));
1214 argv = newvp + 2;
1215 sp = argv + argc;
1216 }
1217
1218 /* Push void to initialize missing args. */
1219 i = nslots;
1220 do {
1221 *sp++ = JSVAL_VOID;
1222 } while (--i != 0);
1223 }
1224
1225 /* Allocate space for local variables and stack of interpreted function. */
1226 if (script && script->nslots != 0) {
1227 if (!AllocateAfterSP(cx, sp, script->nslots)) {
1228 /* NB: Discontinuity between argv and slots, stack slots. */
1229 sp = js_AllocRawStack(cx, script->nslots, NULL);
1230 if (!sp) {
1231 ok = JS_FALSE;
1232 goto out2;
1233 }
1234 }
1235
1236 /* Push void to initialize local variables. */
1237 for (jsval *end = sp + fun->u.i.nvars; sp != end; ++sp)
1238 *sp = JSVAL_VOID;
1239 }
1240
1241 /*
1242 * Initialize the frame.
1243 *
1244 * To set thisp we use an explicit cast and not JSVAL_TO_OBJECT, as vp[1]
1245 * can be a primitive value here for those native functions specified with
1246 * JSFUN_THISP_(NUMBER|STRING|BOOLEAN) flags.
1247 */
1248 frame.thisp = (JSObject *)vp[1];
1249 frame.varobj = NULL;
1250 frame.callobj = frame.argsobj = NULL;
1251 frame.script = script;
1252 frame.callee = funobj;
1253 frame.fun = fun;
1254 frame.argc = argc;
1255 frame.argv = argv;
1256
1257 /* Default return value for a constructor is the new object. */
1258 frame.rval = (flags & JSINVOKE_CONSTRUCT) ? vp[1] : JSVAL_VOID;
1259 frame.down = cx->fp;
1260 frame.annotation = NULL;
1261 frame.scopeChain = NULL; /* set below for real, after cx->fp is set */
1262 frame.regs = NULL;
1263 frame.slots = NULL;
1264 frame.sharpDepth = 0;
1265 frame.sharpArray = NULL;
1266 frame.flags = flags | rootedArgsFlag;
1267 frame.dormantNext = NULL;
1268 frame.xmlNamespace = NULL;
1269 frame.blockChain = NULL;
1270
1271 MUST_FLOW_THROUGH("out");
1272 cx->fp = &frame;
1273
1274 /* Init these now in case we goto out before first hook call. */
1275 hook = cx->debugHooks->callHook;
1276 hookData = NULL;
1277
1278 /* call the hook if present */
1279 if (hook && (native || script))
1280 hookData = hook(cx, &frame, JS_TRUE, 0, cx->debugHooks->callHookData);
1281
1282 /* Call the function, either a native method or an interpreted script. */
1283 if (native) {
1284 #ifdef DEBUG_NOT_THROWING
1285 JSBool alreadyThrowing = cx->throwing;
1286 #endif
1287
1288 #if JS_HAS_LVALUE_RETURN
1289 /* Set by JS_SetCallReturnValue2, used to return reference types. */
1290 cx->rval2set = JS_FALSE;
1291 #endif
1292
1293 /* If native, use caller varobj and scopeChain for eval. */
1294 JS_ASSERT(!frame.varobj);
1295 JS_ASSERT(!frame.scopeChain);
1296 if (frame.down) {
1297 frame.varobj = frame.down->varobj;
1298 frame.scopeChain = frame.down->scopeChain;
1299 }
1300
1301 /* But ensure that we have a scope chain. */
1302 if (!frame.scopeChain)
1303 frame.scopeChain = parent;
1304
1305 frame.displaySave = NULL;
1306 ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval);
1307 JS_RUNTIME_METER(cx->runtime, nativeCalls);
1308 #ifdef DEBUG_NOT_THROWING
1309 if (ok && !alreadyThrowing)
1310 ASSERT_NOT_THROWING(cx);
1311 #endif
1312 } else if (script) {
1313 /* Use parent scope so js_GetCallObject can find the right "Call". */
1314 frame.scopeChain = parent;
1315 if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) {
1316 /* Scope with a call object parented by the callee's parent. */
1317 if (!js_GetCallObject(cx, &frame, parent)) {
1318 ok = JS_FALSE;
1319 goto out;
1320 }
1321 }
1322 frame.slots = sp - fun->u.i.nvars;
1323
1324 ok = js_Interpret(cx);
1325 } else {
1326 /* fun might be onerror trying to report a syntax error in itself. */
1327 frame.scopeChain = NULL;
1328 frame.displaySave = NULL;
1329 ok = JS_TRUE;
1330 }
1331
1332 out:
1333 if (hookData) {
1334 hook = cx->debugHooks->callHook;
1335 if (hook)
1336 hook(cx, &frame, JS_FALSE, &ok, hookData);
1337 }
1338
1339 /* If frame has a call object, sync values and clear back-pointer. */
1340 if (frame.callobj)
1341 ok &= js_PutCallObject(cx, &frame);
1342
1343 /* If frame has an arguments object, sync values and clear back-pointer. */
1344 if (frame.argsobj)
1345 ok &= js_PutArgsObject(cx, &frame);
1346
1347 *vp = frame.rval;
1348
1349 /* Restore cx->fp now that we're done releasing frame objects. */
1350 cx->fp = frame.down;
1351
1352 out2:
1353 /* Pop everything we may have allocated off the stack. */
1354 JS_ARENA_RELEASE(&cx->stackPool, mark);
1355 if (!ok)
1356 *vp = JSVAL_NULL;
1357 return ok;
1358
1359 bad:
1360 js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
1361 ok = JS_FALSE;
1362 goto out2;
1363 }
1364
1365 JSBool
1366 js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags,
1367 uintN argc, jsval *argv, jsval *rval)
1368 {
1369 jsval *invokevp;
1370 void *mark;
1371 JSBool ok;
1372
1373 invokevp = js_AllocStack(cx, 2 + argc, &mark);
1374 if (!invokevp)
1375 return JS_FALSE;
1376
1377 invokevp[0] = fval;
1378 invokevp[1] = OBJECT_TO_JSVAL(obj);
1379 memcpy(invokevp + 2, argv, argc * sizeof *argv);
1380
1381 ok = js_Invoke(cx, argc, invokevp, flags);
1382 if (ok) {
1383 /*
1384 * Store *rval in the a scoped local root if a scope is open, else in
1385 * the lastInternalResult pigeon-hole GC root, solely so users of
1386 * js_InternalInvoke and its direct and indirect (js_ValueToString for
1387 * example) callers do not need to manage roots for local, temporary
1388 * references to such results.
1389 */
1390 *rval = *invokevp;
1391 if (JSVAL_IS_GCTHING(*rval) && *rval != JSVAL_NULL) {
1392 if (cx->localRootStack) {
1393 if (js_PushLocalRoot(cx, cx->localRootStack, *rval) < 0)
1394 ok = JS_FALSE;
1395 } else {
1396 cx->weakRoots.lastInternalResult = *rval;
1397 }
1398 }
1399 }
1400
1401 js_FreeStack(cx, mark);
1402 return ok;
1403 }
1404
1405 JSBool
1406 js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval,
1407 JSAccessMode mode, uintN argc, jsval *argv, jsval *rval)
1408 {
1409 JSSecurityCallbacks *callbacks;
1410
1411 /*
1412 * js_InternalInvoke could result in another try to get or set the same id
1413 * again, see bug 355497.
1414 */
1415 JS_CHECK_RECURSION(cx, return JS_FALSE);
1416
1417 /*
1418 * Check general (not object-ops/class-specific) access from the running
1419 * script to obj.id only if id has a scripted getter or setter that we're
1420 * about to invoke. If we don't check this case, nothing else will -- no
1421 * other native code has the chance to check.
1422 *
1423 * Contrast this non-native (scripted) case with native getter and setter
1424 * accesses, where the native itself must do an access check, if security
1425 * policies requires it. We make a checkAccess or checkObjectAccess call
1426 * back to the embedding program only in those cases where we're not going
1427 * to call an embedding-defined native function, getter, setter, or class
1428 * hook anyway. Where we do call such a native, there's no need for the
1429 * engine to impose a separate access check callback on all embeddings --
1430 * many embeddings have no security policy at all.
1431 */
1432 JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE);
1433 callbacks = JS_GetSecurityCallbacks(cx);
1434 if (callbacks &&
1435 callbacks->checkObjectAccess &&
1436 VALUE_IS_FUNCTION(cx, fval) &&
1437 FUN_INTERPRETED(GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(fval))) &&
1438 !callbacks->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode, &fval)) {
1439 return JS_FALSE;
1440 }
1441
1442 return js_InternalCall(cx, obj, fval, argc, argv, rval);
1443 }
1444
1445 JSBool
1446 js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
1447 JSStackFrame *down, uintN flags, jsval *result)
1448 {
1449 JSInterpreterHook hook;
1450 void *hookData, *mark;
1451 JSStackFrame *oldfp, frame;
1452 JSObject *obj, *tmp;
1453 JSBool ok;
1454
1455 #ifdef INCLUDE_MOZILLA_DTRACE
1456 if (JAVASCRIPT_EXECUTE_START_ENABLED())
1457 jsdtrace_execute_start(script);
1458 #endif
1459
1460 hook = cx->debugHooks->executeHook;
1461 hookData = mark = NULL;
1462 oldfp = cx->fp;
1463 frame.script = script;
1464 if (down) {
1465 /* Propagate arg state for eval and the debugger API. */
1466 frame.callobj = down->callobj;
1467 frame.argsobj = down->argsobj;
1468 frame.varobj = down->varobj;
1469 frame.callee = down->callee;
1470 frame.fun = down->fun;
1471 frame.thisp = down->thisp;
1472 if (down->flags & JSFRAME_COMPUTED_THIS)
1473 flags |= JSFRAME_COMPUTED_THIS;
1474 frame.argc = down->argc;
1475 frame.argv = down->argv;
1476 frame.annotation = down->annotation;
1477 frame.sharpArray = down->sharpArray;
1478 JS_ASSERT(script->nfixed == 0);
1479 } else {
1480 frame.callobj = frame.argsobj = NULL;
1481 obj = chain;
1482 if (cx->options & JSOPTION_VAROBJFIX) {
1483 while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
1484 obj = tmp;
1485 }
1486 frame.varobj = obj;
1487 frame.callee = NULL;
1488 frame.fun = NULL;
1489 frame.thisp = chain;
1490 frame.argc = 0;
1491 frame.argv = NULL;
1492 frame.annotation = NULL;
1493 frame.sharpArray = NULL;
1494 }
1495 if (script->nslots != 0) {
1496 frame.slots = js_AllocRawStack(cx, script->nslots, &mark);
1497 if (!frame.slots) {
1498 ok = JS_FALSE;
1499 goto out;
1500 }
1501 memset(frame.slots, 0, script->nfixed * sizeof(jsval));
1502 } else {
1503 frame.slots = NULL;
1504 }
1505
1506 frame.rval = JSVAL_VOID;
1507 frame.down = down;
1508 frame.scopeChain = chain;
1509 frame.regs = NULL;
1510 frame.sharpDepth = 0;
1511 frame.flags = flags;
1512 frame.dormantNext = NULL;
1513 frame.xmlNamespace = NULL;
1514 frame.blockChain = NULL;
1515
1516 /*
1517 * Here we wrap the call to js_Interpret with code to (conditionally)
1518 * save and restore the old stack frame chain into a chain of 'dormant'
1519 * frame chains. Since we are replacing cx->fp, we were running into
1520 * the problem that if GC was called under this frame, some of the GC
1521 * things associated with the old frame chain (available here only in
1522 * the C variable 'oldfp') were not rooted and were being collected.
1523 *
1524 * So, now we preserve the links to these 'dormant' frame chains in cx
1525 * before calling js_Interpret and cleanup afterwards. The GC walks
1526 * these dormant chains and marks objects in the same way that it marks
1527 * objects in the primary cx->fp chain.
1528 */
1529 if (oldfp && oldfp != down) {
1530 JS_ASSERT(!oldfp->dormantNext);
1531 oldfp->dormantNext = cx->dormantFrameChain;
1532 cx->dormantFrameChain = oldfp;
1533 }
1534
1535 cx->fp = &frame;
1536 if (!down) {
1537 OBJ_TO_OUTER_OBJECT(cx, frame.thisp);
1538 if (!frame.thisp) {
1539 ok = JS_FALSE;
1540 goto out2;
1541 }
1542 frame.flags |= JSFRAME_COMPUTED_THIS;
1543 }
1544
1545 if (hook) {
1546 hookData = hook(cx, &frame, JS_TRUE, 0,
1547 cx->debugHooks->executeHookData);
1548 }
1549
1550 ok = js_Interpret(cx);
1551 if (result)
1552 *result = frame.rval;
1553
1554 if (hookData) {
1555 hook = cx->debugHooks->executeHook;
1556 if (hook)
1557 hook(cx, &frame, JS_FALSE, &ok, hookData);
1558 }
1559
1560 out2:
1561 if (mark)
1562 js_FreeRawStack(cx, mark);
1563 cx->fp = oldfp;
1564
1565 if (oldfp && oldfp != down) {
1566 JS_ASSERT(cx->dormantFrameChain == oldfp);
1567 cx->dormantFrameChain = oldfp->dormantNext;
1568 oldfp->dormantNext = NULL;
1569 }
1570
1571 out:
1572 #ifdef INCLUDE_MOZILLA_DTRACE
1573 if (JAVASCRIPT_EXECUTE_DONE_ENABLED())
1574 jsdtrace_execute_done(script);
1575 #endif
1576 return ok;
1577 }
1578
1579 JSBool
1580 js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
1581 JSObject **objp, JSProperty **propp)
1582 {
1583 JSObject *obj2;
1584 JSProperty *prop;
1585 uintN oldAttrs, report;
1586 JSBool isFunction;
1587 jsval value;
1588 const char *type, *name;
1589
1590 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1591 return JS_FALSE;
1592 if (propp) {
1593 *objp = obj2;
1594 *propp = prop;
1595 }
1596 if (!prop)
1597 return JS_TRUE;
1598
1599 /*
1600 * Use prop as a speedup hint to OBJ_GET_ATTRIBUTES, but drop it on error.
1601 * An assertion at label bad: will insist that it is null.
1602 */
1603 if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) {
1604 OBJ_DROP_PROPERTY(cx, obj2, prop);
1605 #ifdef DEBUG
1606 prop = NULL;
1607 #endif
1608 goto bad;
1609 }
1610
1611 /*
1612 * From here, return true, or else goto bad on failure to null out params.
1613 * If our caller doesn't want prop, drop it (we don't need it any longer).
1614 */
1615 if (!propp) {
1616 OBJ_DROP_PROPERTY(cx, obj2, prop);
1617 prop = NULL;
1618 }
1619
1620 if (attrs == JSPROP_INITIALIZER) {
1621 /* Allow the new object to override properties. */
1622 if (obj2 != obj)
1623 return JS_TRUE;
1624 report = JSREPORT_WARNING | JSREPORT_STRICT;
1625 } else {
1626 /* We allow redeclaring some non-readonly properties. */
1627 if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) {
1628 /*
1629 * Allow redeclaration of variables and functions, but insist that
1630 * the new value is not a getter if the old value was, ditto for
1631 * setters -- unless prop is impermanent (in which case anyone
1632 * could delete it and redefine it, willy-nilly).
1633 */
1634 if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
1635 return JS_TRUE;
1636 if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0)
1637 return JS_TRUE;
1638 if (!(oldAttrs & JSPROP_PERMANENT))
1639 return JS_TRUE;
1640 }
1641
1642 report = JSREPORT_ERROR;
1643 isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
1644 if (!isFunction) {
1645 if (!OBJ_GET_PROPERTY(cx, obj, id, &value))
1646 goto bad;
1647 isFunction = VALUE_IS_FUNCTION(cx, value);
1648 }
1649 }
1650
1651 type = (attrs == JSPROP_INITIALIZER)
1652 ? "property"
1653 : (oldAttrs & attrs & JSPROP_GETTER)
1654 ? js_getter_str
1655 : (oldAttrs & attrs & JSPROP_SETTER)
1656 ? js_setter_str
1657 : (oldAttrs & JSPROP_READONLY)
1658 ? js_const_str
1659 : isFunction
1660 ? js_function_str
1661 : js_var_str;
1662 name = js_ValueToPrintableString(cx, ID_TO_VALUE(id));
1663 if (!name)
1664 goto bad;
1665 return JS_ReportErrorFlagsAndNumber(cx, report,
1666 js_GetErrorMessage, NULL,
1667 JSMSG_REDECLARED_VAR,
1668 type, name);
1669
1670 bad:
1671 if (propp) {
1672 *objp = NULL;
1673 *propp = NULL;
1674 }
1675 JS_ASSERT(!prop);
1676 return JS_FALSE;
1677 }
1678
1679 JSBool
1680 js_StrictlyEqual(JSContext *cx, jsval lval, jsval rval)
1681 {
1682 jsval ltag = JSVAL_TAG(lval), rtag = JSVAL_TAG(rval);
1683 jsdouble ld, rd;
1684
1685 if (ltag == rtag) {
1686 if (ltag == JSVAL_STRING) {
1687 JSString *lstr = JSVAL_TO_STRING(lval),
1688 *rstr = JSVAL_TO_STRING(rval);
1689 return js_EqualStrings(lstr, rstr);
1690 }
1691 if (ltag == JSVAL_DOUBLE) {
1692 ld = *JSVAL_TO_DOUBLE(lval);
1693 rd = *JSVAL_TO_DOUBLE(rval);
1694 return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1695 }
1696 if (ltag == JSVAL_OBJECT &&
1697 lval != rval &&
1698 !JSVAL_IS_NULL(lval) &&
1699 !JSVAL_IS_NULL(rval)) {
1700 JSObject *lobj, *robj;
1701
1702 lobj = js_GetWrappedObject(cx, JSVAL_TO_OBJECT(lval));
1703 robj = js_GetWrappedObject(cx, JSVAL_TO_OBJECT(rval));
1704 lval = OBJECT_TO_JSVAL(lobj);
1705 rval = OBJECT_TO_JSVAL(robj);
1706 }
1707 return lval == rval;
1708 }
1709 if (ltag == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) {
1710 ld = *JSVAL_TO_DOUBLE(lval);
1711 rd = JSVAL_TO_INT(rval);
1712 return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1713 }
1714 if (JSVAL_IS_INT(lval) && rtag == JSVAL_DOUBLE) {
1715 ld = JSVAL_TO_INT(lval);
1716 rd = *JSVAL_TO_DOUBLE(rval);
1717 return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1718 }
1719 return lval == rval;
1720 }
1721
1722 JSBool
1723 js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp)
1724 {
1725 JSFunction *fun, *fun2;
1726 JSObject *obj, *obj2, *proto, *parent;
1727 jsval lval, rval;
1728 JSClass *clasp;
1729
1730 fun = NULL;
1731 obj2 = NULL;
1732 lval = *vp;
1733 if (!JSVAL_IS_OBJECT(lval) ||
1734 (obj2 = JSVAL_TO_OBJECT(lval)) == NULL ||
1735 /* XXX clean up to avoid special cases above ObjectOps layer */
1736 OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass ||
1737 !obj2->map->ops->construct)
1738 {
1739 fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT);
1740 if (!fun)
1741 return JS_FALSE;
1742 }
1743
1744 clasp = &js_ObjectClass;
1745 if (!obj2) {
1746 proto = parent = NULL;
1747 fun = NULL;
1748 } else {
1749 /*
1750 * Get the constructor prototype object for this function.
1751 * Use the nominal 'this' parameter slot, vp[1], as a local
1752 * root to protect this prototype, in case it has no other
1753 * strong refs.
1754 */
1755 if (!OBJ_GET_PROPERTY(cx, obj2,
1756 ATOM_TO_JSID(cx->runtime->atomState
1757 .classPrototypeAtom),
1758 &vp[1])) {
1759 return JS_FALSE;
1760 }
1761 rval = vp[1];
1762 proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL;
1763 parent = OBJ_GET_PARENT(cx, obj2);
1764
1765 if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) {
1766 fun2 = GET_FUNCTION_PRIVATE(cx, obj2);
1767 if (!FUN_INTERPRETED(fun2) && fun2->u.n.clasp)
1768 clasp = fun2->u.n.clasp;
1769 }
1770 }
1771 obj = js_NewObject(cx, clasp, proto, parent, 0);
1772 if (!obj)
1773 return JS_FALSE;
1774
1775 /* Now we have an object with a constructor method; call it. */
1776 vp[1] = OBJECT_TO_JSVAL(obj);
1777 if (!js_Invoke(cx, argc, vp, JSINVOKE_CONSTRUCT)) {
1778 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
1779 return JS_FALSE;
1780 }
1781
1782 /* Check the return value and if it's primitive, force it to be obj. */
1783 rval = *vp;
1784 if (clampReturn && JSVAL_IS_PRIMITIVE(rval)) {
1785 if (!fun) {
1786 /* native [[Construct]] returning primitive is error */
1787 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1788 JSMSG_BAD_NEW_RESULT,
1789 js_ValueToPrintableString(cx, rval));
1790 return JS_FALSE;
1791 }
1792 *vp = OBJECT_TO_JSVAL(obj);
1793 }
1794
1795 JS_RUNTIME_METER(cx->runtime, constructs);
1796 return JS_TRUE;
1797 }
1798
1799 JSBool
1800 js_InternNonIntElementId(JSContext *cx, JSObject *obj, jsval idval, jsid *idp)
1801 {
1802 JS_ASSERT(!JSVAL_IS_INT(idval));
1803
1804 #if JS_HAS_XML_SUPPORT
1805 if (!JSVAL_IS_PRIMITIVE(idval)) {
1806 if (OBJECT_IS_XML(cx, obj)) {
1807 *idp = OBJECT_JSVAL_TO_JSID(idval);
1808 return JS_TRUE;
1809 }
1810 if (!js_IsFunctionQName(cx, JSVAL_TO_OBJECT(idval), idp))
1811 return JS_FALSE;
1812 if (*idp != 0)
1813 return JS_TRUE;
1814 }
1815 #endif
1816
1817 return js_ValueToStringId(cx, idval, idp);
1818 }
1819
1820 /*
1821 * Enter the new with scope using an object at sp[-1] and associate the depth
1822 * of the with block with sp + stackIndex.
1823 */
1824 JS_STATIC_INTERPRET JSBool
1825 js_EnterWith(JSContext *cx, jsint stackIndex)
1826 {
1827 JSStackFrame *fp;
1828 jsval *sp;
1829 JSObject *obj, *parent, *withobj;
1830
1831 fp = cx->fp;
1832 sp = fp->regs->sp;
1833 JS_ASSERT(stackIndex < 0);
1834 JS_ASSERT(StackBase(fp) <= sp + stackIndex);
1835
1836 if (!JSVAL_IS_PRIMITIVE(sp[-1])) {
1837 obj = JSVAL_TO_OBJECT(sp[-1]);
1838 } else {
1839 obj = js_ValueToNonNullObject(cx, sp[-1]);
1840 if (!obj)
1841 return JS_FALSE;
1842 sp[-1] = OBJECT_TO_JSVAL(obj);
1843 }
1844
1845 parent = js_GetScopeChain(cx, fp);
1846 if (!parent)
1847 return JS_FALSE;
1848
1849 OBJ_TO_INNER_OBJECT(cx, obj);
1850 if (!obj)
1851 return JS_FALSE;
1852
1853 withobj = js_NewWithObject(cx, obj, parent,
1854 sp + stackIndex - StackBase(fp));
1855 if (!withobj)
1856 return JS_FALSE;
1857
1858 fp->scopeChain = withobj;
1859 js_DisablePropertyCache(cx);
1860 return JS_TRUE;
1861 }
1862
1863 JS_STATIC_INTERPRET void
1864 js_LeaveWith(JSContext *cx)
1865 {
1866 JSObject *withobj;
1867
1868 withobj = cx->fp->scopeChain;
1869 JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass);
1870 JS_ASSERT(OBJ_GET_PRIVATE(cx, withobj) == cx->fp);
1871 JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);
1872 cx->fp->scopeChain = OBJ_GET_PARENT(cx, withobj);
1873 JS_SetPrivate(cx, withobj, NULL);
1874 js_EnablePropertyCache(cx);
1875 }
1876
1877 JSClass *
1878 js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth)
1879 {
1880 JSClass *clasp;
1881
1882 clasp = OBJ_GET_CLASS(cx, obj);
1883 if ((clasp == &js_WithClass || clasp == &js_BlockClass) &&
1884 OBJ_GET_PRIVATE(cx, obj) == cx->fp &&
1885 OBJ_BLOCK_DEPTH(cx, obj) >= stackDepth) {
1886 return clasp;
1887 }
1888 return NULL;
1889 }
1890
1891 JS_STATIC_INTERPRET jsint
1892 js_CountWithBlocks(JSContext *cx, JSStackFrame *fp)
1893 {
1894 jsint n;
1895 JSObject *obj;
1896 JSClass *clasp;
1897
1898 n = 0;
1899 for (obj = fp->scopeChain;
1900 (clasp = js_IsActiveWithOrBlock(cx, obj, 0)) != NULL;
1901 obj = OBJ_GET_PARENT(cx, obj)) {
1902 if (clasp == &js_WithClass)
1903 ++n;
1904 }
1905 return n;
1906 }
1907
1908 /*
1909 * Unwind block and scope chains to match the given depth. The function sets
1910 * fp->sp on return to stackDepth.
1911 */
1912 JSBool
1913 js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth,
1914 JSBool normalUnwind)
1915 {
1916 JSObject *obj;
1917 JSClass *clasp;
1918
1919 JS_ASSERT(stackDepth >= 0);
1920 JS_ASSERT(StackBase(fp) + stackDepth <= fp->regs->sp);
1921
1922 for (obj = fp->blockChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
1923 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
1924 if (OBJ_BLOCK_DEPTH(cx, obj) < stackDepth)
1925 break;
1926 }
1927 fp->blockChain = obj;
1928
1929 for (;;) {
1930 obj = fp->scopeChain;
1931 clasp = js_IsActiveWithOrBlock(cx, obj, stackDepth);
1932 if (!clasp)
1933 break;
1934 if (clasp == &js_BlockClass) {
1935 /* Don't fail until after we've updated all stacks. */
1936 normalUnwind &= js_PutBlockObject(cx, normalUnwind);
1937 } else {
1938 js_LeaveWith(cx);
1939 }
1940 }
1941
1942 fp->regs->sp = StackBase(fp) + stackDepth;
1943 return normalUnwind;
1944 }
1945
1946 JS_STATIC_INTERPRET JSBool
1947 js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2)
1948 {
1949 jsval v;
1950 jsdouble d;
1951
1952 v = *vp;
1953 if (JSVAL_IS_DOUBLE(v)) {
1954 d = *JSVAL_TO_DOUBLE(v);
1955 } else if (JSVAL_IS_INT(v)) {
1956 d = JSVAL_TO_INT(v);
1957 } else {
1958 d = js_ValueToNumber(cx, vp);
1959 if (JSVAL_IS_NULL(*vp))
1960 return JS_FALSE;
1961 JS_ASSERT(JSVAL_IS_NUMBER(*vp) || *vp == JSVAL_TRUE);
1962
1963 /* Store the result of v conversion back in vp for post increments. */
1964 if ((cs->format & JOF_POST) &&
1965 *vp == JSVAL_TRUE
1966 && !js_NewNumberInRootedValue(cx, d, vp)) {
1967 return JS_FALSE;
1968 }
1969 }
1970
1971 (cs->format & JOF_INC) ? d++ : d--;
1972 if (!js_NewNumberInRootedValue(cx, d, vp2))
1973 return JS_FALSE;
1974
1975 if (!(cs->format & JOF_POST))
1976 *vp = *vp2;
1977 return JS_TRUE;
1978 }
1979
1980 #ifdef DEBUG
1981
1982 JS_STATIC_INTERPRET void
1983 js_TraceOpcode(JSContext *cx, jsint len)
1984 {
1985 FILE *tracefp;
1986 JSStackFrame *fp;
1987 JSFrameRegs *regs;
1988 JSOp prevop;
1989 intN ndefs, n, nuses;
1990 jsval *siter;
1991 JSString *str;
1992 JSOp op;
1993
1994 tracefp = (FILE *) cx->tracefp;
1995 JS_ASSERT(tracefp);
1996 fp = cx->fp;
1997 regs = fp->regs;
1998 if (len != 0) {
1999 prevop = (JSOp) regs->pc[-len];
2000 ndefs = js_CodeSpec[prevop].ndefs;
2001 if (ndefs != 0) {
2002 if (prevop == JSOP_FORELEM && regs->sp[-1] == JSVAL_FALSE)
2003 --ndefs;
2004 for (n = -ndefs; n < 0; n++) {
2005 char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
2006 NULL);
2007 if (bytes) {
2008 fprintf(tracefp, "%s %s",
2009 (n == -ndefs) ? " output:" : ",",
2010 bytes);
2011 JS_free(cx, bytes);
2012 }
2013 }
2014 fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));
2015 }
2016 fprintf(tracefp, " stack: ");
2017 for (siter = StackBase(fp); siter < regs->sp; siter++) {
2018 str = js_ValueToString(cx, *siter);
2019 if (!str)
2020 fputs("<null>", tracefp);
2021 else
2022 js_FileEscapedString(tracefp, str, 0);
2023 fputc(' ', tracefp);
2024 }
2025 fputc('\n', tracefp);
2026 }
2027
2028 fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, fp->script, regs->pc));
2029 js_Disassemble1(cx, fp->script, regs->pc,
2030 PTRDIFF(regs->pc, fp->script->code, jsbytecode),
2031 JS_FALSE, tracefp);
2032 op = (JSOp) *regs->pc;
2033 nuses = js_CodeSpec[op].nuses;
2034 if (nuses != 0) {
2035 for (n = -nuses; n < 0; n++) {
2036 char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
2037 NULL);
2038 if (bytes) {
2039 fprintf(tracefp, "%s %s",
2040 (n == -nuses) ? " inputs:" : ",",
2041 bytes);
2042 JS_free(cx, bytes);
2043 }
2044 }
2045 fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));
2046 }
2047 }
2048
2049 #endif /* DEBUG */
2050
2051 #ifdef JS_OPMETER
2052
2053 # include <stdlib.h>
2054
2055 # define HIST_NSLOTS 8
2056
2057 /*
2058 * The second dimension is hardcoded at 256 because we know that many bits fit
2059 * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address
2060 * any particular row.
2061 */
2062 static uint32 succeeds[JSOP_LIMIT][256];
2063 static uint32 slot_ops[JSOP_LIMIT][HIST_NSLOTS];
2064
2065 JS_STATIC_INTERPRET void
2066 js_MeterOpcodePair(JSOp op1, JSOp op2)
2067 {
2068 if (op1 != JSOP_STOP)
2069 ++succeeds[op1][op2];
2070 }
2071
2072 JS_STATIC_INTERPRET void
2073 js_MeterSlotOpcode(JSOp op, uint32 slot)
2074 {
2075 if (slot < HIST_NSLOTS)
2076 ++slot_ops[op][slot];
2077 }
2078
2079 typedef struct Edge {
2080 const char *from;
2081 const char *to;
2082 uint32 count;
2083 } Edge;
2084
2085 static int
2086 compare_edges(const void *a, const void *b)
2087 {
2088 const Edge *ea = (const Edge *) a;
2089 const Edge *eb = (const Edge *) b;
2090
2091 return (int32)eb->count - (int32)ea->count;
2092 }
2093
2094 void
2095 js_DumpOpMeters()
2096 {
2097 const char *name, *from, *style;
2098 FILE *fp;
2099 uint32 total, count;
2100 uint32 i, j, nedges;
2101 Edge *graph;
2102
2103 name = getenv("JS_OPMETER_FILE");
2104 if (!name)
2105 name = "/tmp/ops.dot";
2106 fp = fopen(name, "w");
2107 if (!fp) {
2108 perror(name);
2109 return;
2110 }
2111
2112 total = nedges = 0;
2113 for (i = 0; i < JSOP_LIMIT; i++) {
2114 for (j = 0; j < JSOP_LIMIT; j++) {
2115 count = succeeds[i][j];
2116 if (count != 0) {
2117 total += count;
2118 ++nedges;
2119 }
2120 }
2121 }
2122
2123 # define SIGNIFICANT(count,total) (200. * (count) >= (total))
2124
2125 graph = (Edge *) calloc(nedges, sizeof graph[0]);
2126 for (i = nedges = 0; i < JSOP_LIMIT; i++) {
2127 from = js_CodeName[i];
2128 for (j = 0; j < JSOP_LIMIT; j++) {
2129 count = succeeds[i][j];
2130 if (count != 0 && SIGNIFICANT(count, total)) {
2131 graph[nedges].from = from;
2132 graph[nedges].to = js_CodeName[j];
2133 graph[nedges].count = count;
2134 ++nedges;
2135 }
2136 }
2137 }
2138 qsort(graph, nedges, sizeof(Edge), compare_edges);
2139
2140 # undef SIGNIFICANT
2141
2142 fputs("digraph {\n", fp);
2143 for (i = 0, style = NULL; i < nedges; i++) {
2144 JS_ASSERT(i == 0 || graph[i-1].count >= graph[i].count);
2145 if (!style || graph[i-1].count != graph[i].count) {
2146 style = (i > nedges * .75) ? "dotted" :
2147 (i > nedges * .50) ? "dashed" :
2148 (i > nedges * .25) ? "solid" : "bold";
2149 }
2150 fprintf(fp, " %s -> %s [label=\"%lu\" style=%s]\n",
2151 graph[i].from, graph[i].to,
2152 (unsigned long)graph[i].count, style);
2153 }
2154 free(graph);
2155 fputs("}\n", fp);
2156 fclose(fp);
2157
2158 name = getenv("JS_OPMETER_HIST");
2159 if (!name)
2160 name = "/tmp/ops.hist";
2161 fp = fopen(name, "w");
2162 if (!fp) {
2163 perror(name);
2164 return;
2165 }
2166 fputs("bytecode", fp);
2167 for (j = 0; j < HIST_NSLOTS; j++)
2168 fprintf(fp, " slot %1u", (unsigned)j);
2169 putc('\n', fp);
2170 fputs("========", fp);
2171 for (j = 0; j < HIST_NSLOTS; j++)
2172 fputs(" =======", fp);
2173 putc('\n', fp);
2174 for (i = 0; i < JSOP_LIMIT; i++) {
2175 for (j = 0; j < HIST_NSLOTS; j++) {
2176 if (slot_ops[i][j] != 0) {
2177 /* Reuse j in the next loop, since we break after. */
2178 fprintf(fp, "%-8.8s", js_CodeName[i]);
2179 for (j = 0; j < HIST_NSLOTS; j++)
2180 fprintf(fp, " %7lu", (unsigned long)slot_ops[i][j]);
2181 putc('\n', fp);
2182 break;
2183 }
2184 }
2185 }
2186 fclose(fp);
2187 }
2188
2189 #endif /* JS_OPSMETER */
2190
2191 #endif /* !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___ */
2192
2193 #ifndef jsinvoke_cpp___
2194
2195 #define PUSH(v) (*regs.sp++ = (v))
2196 #define PUSH_OPND(v) PUSH(v)
2197 #define STORE_OPND(n,v) (regs.sp[n] = (v))
2198 #define POP() (*--regs.sp)
2199 #define POP_OPND() POP()
2200 #define FETCH_OPND(n) (regs.sp[n])
2201
2202 /*
2203 * Push the jsdouble d using sp from the lexical environment. Try to convert d
2204 * to a jsint that fits in a jsval, otherwise GC-alloc space for it and push a
2205 * reference.
2206 */
2207 #define STORE_NUMBER(cx, n, d) \
2208 JS_BEGIN_MACRO \
2209 jsint i_; \
2210 \
2211 if (JSDOUBLE_IS_INT(d, i_) && INT_FITS_IN_JSVAL(i_)) \
2212 regs.sp[n] = INT_TO_JSVAL(i_); \
2213 else if (!js_NewDoubleInRootedValue(cx, d, &regs.sp[n])) \
2214 goto error; \
2215 JS_END_MACRO
2216
2217 #define STORE_INT(cx, n, i) \
2218 JS_BEGIN_MACRO \
2219 if (INT_FITS_IN_JSVAL(i)) \
2220 regs.sp[n] = INT_TO_JSVAL(i); \
2221 else if (!js_NewDoubleInRootedValue(cx, (jsdouble) (i), &regs.sp[n])) \
2222 goto error; \
2223 JS_END_MACRO
2224
2225 #define STORE_UINT(cx, n, u) \
2226 JS_BEGIN_MACRO \
2227 if ((u) <= JSVAL_INT_MAX) \
2228 regs.sp[n] = INT_TO_JSVAL(u); \
2229 else if (!js_NewDoubleInRootedValue(cx, (jsdouble) (u), &regs.sp[n])) \
2230 goto error; \
2231 JS_END_MACRO
2232
2233 #define FETCH_NUMBER(cx, n, d) \
2234 JS_BEGIN_MACRO \
2235 jsval v_; \
2236 \
2237 v_ = FETCH_OPND(n); \
2238 VALUE_TO_NUMBER(cx, n, v_, d); \
2239 JS_END_MACRO
2240
2241 #define FETCH_INT(cx, n, i) \
2242 JS_BEGIN_MACRO \
2243 jsval v_; \
2244 \
2245 v_= FETCH_OPND(n); \
2246 if (JSVAL_IS_INT(v_)) { \
2247 i = JSVAL_TO_INT(v_); \
2248 } else { \
2249 i = js_ValueToECMAInt32(cx, &regs.sp[n]); \
2250 if (JSVAL_IS_NULL(regs.sp[n])) \
2251 goto error; \
2252 } \
2253 JS_END_MACRO
2254
2255 #define FETCH_UINT(cx, n, ui) \
2256 JS_BEGIN_MACRO \
2257 jsval v_; \
2258 \
2259 v_= FETCH_OPND(n); \
2260 if (JSVAL_IS_INT(v_)) { \
2261 ui = (uint32) JSVAL_TO_INT(v_); \
2262 } else { \
2263 ui = js_ValueToECMAUint32(cx, &regs.sp[n]); \
2264 if (JSVAL_IS_NULL(regs.sp[n])) \
2265 goto error; \
2266 } \
2267 JS_END_MACRO
2268
2269 /*
2270 * Optimized conversion macros that test for the desired type in v before
2271 * homing sp and calling a conversion function.
2272 */
2273 #define VALUE_TO_NUMBER(cx, n, v, d) \
2274 JS_BEGIN_MACRO \
2275 JS_ASSERT(v == regs.sp[n]); \
2276 if (JSVAL_IS_INT(v)) { \
2277 d = (jsdouble)JSVAL_TO_INT(v); \
2278 } else if (JSVAL_IS_DOUBLE(v)) { \
2279 d = *JSVAL_TO_DOUBLE(v); \
2280 } else { \
2281 d = js_ValueToNumber(cx, &regs.sp[n]); \
2282 if (JSVAL_IS_NULL(regs.sp[n])) \
2283 goto error; \
2284 JS_ASSERT(JSVAL_IS_NUMBER(regs.sp[n]) || \
2285 regs.sp[n] == JSVAL_TRUE); \
2286 } \
2287 JS_END_MACRO
2288
2289 #define POP_BOOLEAN(cx, v, b) \
2290 JS_BEGIN_MACRO \
2291 v = FETCH_OPND(-1); \
2292 if (v == JSVAL_NULL) { \
2293 b = JS_FALSE; \
2294 } else if (JSVAL_IS_BOOLEAN(v)) { \
2295 b = JSVAL_TO_BOOLEAN(v); \
2296 } else { \
2297 b = js_ValueToBoolean(v); \
2298 } \
2299 regs.sp--; \
2300 JS_END_MACRO
2301
2302 #define VALUE_TO_OBJECT(cx, n, v, obj) \
2303 JS_BEGIN_MACRO \
2304 if (!JSVAL_IS_PRIMITIVE(v)) { \
2305 obj = JSVAL_TO_OBJECT(v); \
2306 } else { \
2307 obj = js_ValueToNonNullObject(cx, v); \
2308 if (!obj) \
2309 goto error; \
2310 STORE_OPND(n, OBJECT_TO_JSVAL(obj)); \
2311 } \
2312 JS_END_MACRO
2313
2314 #define FETCH_OBJECT(cx, n, v, obj) \
2315 JS_BEGIN_MACRO \
2316 v = FETCH_OPND(n); \
2317 VALUE_TO_OBJECT(cx, n, v, obj); \
2318 JS_END_MACRO
2319
2320 #define DEFAULT_VALUE(cx, n, hint, v) \
2321 JS_BEGIN_MACRO \
2322 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); \
2323 JS_ASSERT(v == regs.sp[n]); \
2324 if (!OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, &regs.sp[n])) \
2325 goto error; \
2326 v = regs.sp[n]; \
2327 JS_END_MACRO
2328
2329 /*
2330 * Quickly test if v is an int from the [-2**29, 2**29) range, that is, when
2331 * the lowest bit of v is 1 and the bits 30 and 31 are both either 0 or 1. For
2332 * such v we can do increment or decrement via adding or subtracting two
2333 * without checking that the result overflows JSVAL_INT_MIN or JSVAL_INT_MAX.
2334 */
2335 #define CAN_DO_FAST_INC_DEC(v) (((((v) << 1) ^ v) & 0x80000001) == 1)
2336
2337 JS_STATIC_ASSERT(JSVAL_INT == 1);
2338 JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL(JSVAL_INT_MIN)));
2339 JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL(JSVAL_INT_MAX)));
2340
2341 /*
2342 * Conditional assert to detect failure to clear a pending exception that is
2343 * suppressed (or unintentional suppression of a wanted exception).
2344 */
2345 #if defined DEBUG_brendan || defined DEBUG_mrbkap || defined DEBUG_shaver
2346 # define DEBUG_NOT_THROWING 1
2347 #endif
2348
2349 #ifdef DEBUG_NOT_THROWING
2350 # define ASSERT_NOT_THROWING(cx) JS_ASSERT(!(cx)->throwing)
2351 #else
2352 # define ASSERT_NOT_THROWING(cx) /* nothing */
2353 #endif
2354
2355 /*
2356 * Define JS_OPMETER to instrument bytecode succession, generating a .dot file
2357 * on shutdown that shows the graph of significant predecessor/successor pairs
2358 * executed, where the edge labels give the succession counts. The .dot file
2359 * is named by the JS_OPMETER_FILE envariable, and defaults to /tmp/ops.dot.
2360 *
2361 * Bonus feature: JS_OPMETER also enables counters for stack-addressing ops
2362 * such as JSOP_GETLOCAL, JSOP_INCARG, via METER_SLOT_OP. The resulting counts
2363 * are written to JS_OPMETER_HIST, defaulting to /tmp/ops.hist.
2364 */
2365 #ifndef JS_OPMETER
2366 # define METER_OP_INIT(op) /* nothing */
2367 # define METER_OP_PAIR(op1,op2) /* nothing */
2368 # define METER_SLOT_OP(op,slot) /* nothing */
2369 #else
2370
2371 /*
2372 * The second dimension is hardcoded at 256 because we know that many bits fit
2373 * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address
2374 * any particular row.
2375 */
2376 # define METER_OP_INIT(op) ((op) = JSOP_STOP)
2377 # define METER_OP_PAIR(op1,op2) (js_MeterOpcodePair(op1, op2))
2378 # define METER_SLOT_OP(op,slot) (js_MeterSlotOpcode(op, slot))
2379
2380 #endif
2381
2382 #define MAX_INLINE_CALL_COUNT 3000
2383
2384 /*
2385 * Threaded interpretation via computed goto appears to be well-supported by
2386 * GCC 3 and higher. IBM's C compiler when run with the right options (e.g.,
2387 * -qlanglvl=extended) also supports threading. Ditto the SunPro C compiler.
2388 * Currently it's broken for JS_VERSION < 160, though this isn't worth fixing.
2389 * Add your compiler support macros here.
2390 */
2391 #ifndef JS_THREADED_INTERP
2392 # if JS_VERSION >= 160 && ( \
2393 __GNUC__ >= 3 || \
2394 (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \
2395 __SUNPRO_C >= 0x570)
2396 # define JS_THREADED_INTERP 1
2397 # else
2398 # define JS_THREADED_INTERP 0
2399 # endif
2400 #endif
2401
2402 /*
2403 * Interpreter assumes the following to implement condition-free interrupt
2404 * implementation when !JS_THREADED_INTERP.
2405 */
2406 JS_STATIC_ASSERT(JSOP_INTERRUPT == 0);
2407
2408 /*
2409 * Ensure that the intrepreter switch can close call-bytecode cases in the
2410 * same way as non-call bytecodes.
2411 */
2412 JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
2413 JS_STATIC_ASSERT(JSOP_GETGVAR_LENGTH == JSOP_CALLGVAR_LENGTH);
2414 JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH);
2415 JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
2416 JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
2417 JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH);
2418
2419 /*
2420 * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
2421 * remain distinct for the decompiler. Ditto for JSOP_NULL{,THIS}.
2422 */
2423 JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
2424 JS_STATIC_ASSERT(JSOP_NULL_LENGTH == JSOP_NULLTHIS_LENGTH);
2425
2426 /* See TRY_BRANCH_AFTER_COND. */
2427 JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH);
2428 JS_STATIC_ASSERT(JSOP_IFNE == JSOP_IFEQ + 1);
2429
2430 /* For the fastest case inder JSOP_INCNAME, etc. */
2431 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_DECNAME_LENGTH);
2432 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEINC_LENGTH);
2433 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH);
2434
2435 JSBool
2436 js_Interpret(JSContext *cx)
2437 {
2438 JSRuntime *rt;
2439 JSStackFrame *fp;
2440 JSScript *script;
2441 uintN inlineCallCount;
2442 JSAtom **atoms;
2443 JSVersion currentVersion, originalVersion;
2444 JSFrameRegs regs;
2445 JSObject *obj, *obj2, *parent;
2446 JSBool ok, cond;
2447 jsint len;
2448 jsbytecode *endpc, *pc2;
2449 JSOp op, op2;
2450 jsatomid index;
2451 JSAtom *atom;
2452 uintN argc, attrs, flags;
2453 uint32 slot;
2454 jsval *vp, lval, rval, ltmp, rtmp;
2455 jsid id;
2456 JSProperty *prop;
2457 JSScopeProperty *sprop;
2458 JSString *str, *str2;
2459 jsint i, j;
2460 jsdouble d, d2;
2461 JSClass *clasp;
2462 JSFunction *fun;
2463 JSType type;
2464 #if JS_THREADED_INTERP
2465 register void * const *jumpTable;
2466 #else
2467 register uint32 switchMask;
2468 uintN switchOp;
2469 #endif
2470 jsint low, high, off, npairs;
2471 JSBool match;
2472 #if JS_HAS_GETTER_SETTER
2473 JSPropertyOp getter, setter;
2474 #endif
2475 JSAutoResolveFlags rf(cx, JSRESOLVE_INFER);
2476
2477 #ifdef __GNUC__
2478 # define JS_EXTENSION __extension__
2479 # define JS_EXTENSION_(s) __extension__ ({ s; })
2480 #else
2481 # define JS_EXTENSION
2482 # define JS_EXTENSION_(s) s
2483 #endif
2484
2485 #if JS_THREADED_INTERP
2486 static void *const normalJumpTable[] = {
2487 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2488 JS_EXTENSION &&L_##op,
2489 # include "jsopcode.tbl"
2490 # undef OPDEF
2491 };
2492
2493 #ifdef JS_TRACER
2494 static void *const recordingJumpTable[] = {
2495 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2496 JS_EXTENSION &&R_##op,
2497 # include "jsopcode.tbl"
2498 # undef OPDEF
2499 };
2500 #endif /* JS_TRACER */
2501
2502 static void *const interruptJumpTable[] = {
2503 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2504 JS_EXTENSION &&L_JSOP_INTERRUPT,
2505 # include "jsopcode.tbl"
2506 # undef OPDEF
2507 };
2508
2509 METER_OP_INIT(op); /* to nullify first METER_OP_PAIR */
2510
2511 # ifdef JS_TRACER
2512 # define CHECK_RECORDER() JS_BEGIN_MACRO \
2513 JS_ASSERT(!TRACE_RECORDER(cx) ^ \
2514 (jumpTable == recordingJumpTable)); \
2515 JS_END_MACRO
2516 # else
2517 # define CHECK_RECORDER() ((void)0)
2518 # endif
2519
2520 # define DO_OP() JS_BEGIN_MACRO \
2521 CHECK_RECORDER(); \
2522 JS_EXTENSION_(goto *jumpTable[op]); \
2523 JS_END_MACRO
2524 # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
2525 METER_OP_PAIR(op, regs.pc[n]); \
2526 op = (JSOp) *(regs.pc += (n)); \
2527 DO_OP(); \
2528 JS_END_MACRO
2529
2530 # define BEGIN_CASE(OP) L_##OP: \
2531 CHECK_RECORDER();
2532 # define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH);
2533 # define END_VARLEN_CASE DO_NEXT_OP(len);
2534 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP) \
2535 JS_ASSERT(js_CodeSpec[OP].length == 1); \
2536 op = (JSOp) *++regs.pc; \
2537 DO_OP();
2538
2539 # define END_EMPTY_CASES
2540
2541 #else /* !JS_THREADED_INTERP */
2542
2543 # define DO_OP() goto do_op
2544 # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
2545 JS_ASSERT((n) == len); \
2546 goto advance_pc; \
2547 JS_END_MACRO
2548
2549 # define BEGIN_CASE(OP) case OP:
2550 # define END_CASE(OP) END_CASE_LEN(OP##_LENGTH)
2551 # define END_CASE_LEN(n) END_CASE_LENX(n)
2552 # define END_CASE_LENX(n) END_CASE_LEN##n
2553
2554 /*
2555 * To share the code for all len == 1 cases we use the specialized label with
2556 * code that falls through to advance_pc: .
2557 */
2558 # define END_CASE_LEN1 goto advance_pc_by_one;
2559 # define END_CASE_LEN2 len = 2; goto advance_pc;
2560 # define END_CASE_LEN3 len = 3; goto advance_pc;
2561 # define END_CASE_LEN4 len = 4; goto advance_pc;
2562 # define END_CASE_LEN5 len = 5; goto advance_pc;
2563 # define END_VARLEN_CASE goto advance_pc;
2564 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)
2565 # define END_EMPTY_CASES goto advance_pc_by_one;
2566
2567 #endif /* !JS_THREADED_INTERP */
2568
2569 #ifdef JS_TRACER
2570 /* We had better not be entering the interpreter from JIT-compiled code. */
2571 TraceRecorder *tr = NULL;
2572 if (JS_ON_TRACE(cx)) {
2573 tr = TRACE_RECORDER(cx);
2574 SET_TRACE_RECORDER(cx, NULL);
2575 JS_TRACE_MONITOR(cx).onTrace = JS_FALSE;
2576 }
2577 #endif
2578
2579 /* Check for too deep of a native thread stack. */
2580 JS_CHECK_RECURSION(cx, return JS_FALSE);
2581
2582 rt = cx->runtime;
2583
2584 /* Set registerized frame pointer and derived script pointer. */
2585 fp = cx->fp;
2586 script = fp->script;
2587 JS_ASSERT(script->length != 0);
2588
2589 /* Count of JS function calls that nest in this C js_Interpret frame. */
2590 inlineCallCount = 0;
2591
2592 /*
2593 * Initialize the index segment register used by LOAD_ATOM and
2594 * GET_FULL_INDEX macros below. As a register we use a pointer based on
2595 * the atom map to turn frequently executed LOAD_ATOM into simple array
2596 * access. For less frequent object and regexp loads we have to recover
2597 * the segment from atoms pointer first.
2598 */
2599 atoms = script->atomMap.vector;
2600
2601 #define LOAD_ATOM(PCOFF) \
2602 JS_BEGIN_MACRO \
2603 JS_ASSERT((size_t)(atoms - script->atomMap.vector) < \
2604 (size_t)(script->atomMap.length - \
2605 GET_INDEX(regs.pc + PCOFF))); \
2606 atom = atoms[GET_INDEX(regs.pc + PCOFF)]; \
2607 JS_END_MACRO
2608
2609 #define GET_FULL_INDEX(PCOFF) \
2610 (atoms - script->atomMap.vector + GET_INDEX(regs.pc + PCOFF))
2611
2612 #define LOAD_OBJECT(PCOFF) \
2613 JS_GET_SCRIPT_OBJECT(script, GET_FULL_INDEX(PCOFF), obj)
2614
2615 #define LOAD_FUNCTION(PCOFF) \
2616 JS_GET_SCRIPT_FUNCTION(script, GET_FULL_INDEX(PCOFF), fun)
2617
2618 #ifdef JS_TRACER
2619
2620 #define MONITOR_BRANCH(oldpc) \
2621 JS_BEGIN_MACRO \
2622 if (TRACING_ENABLED(cx)) { \
2623 ENABLE_TRACER(js_MonitorLoopEdge(cx, oldpc, inlineCallCount)); \
2624 fp = cx->fp; \
2625 script = fp->script; \
2626 atoms = script->atomMap.vector; \
2627 currentVersion = (JSVersion) script->version; \
2628 JS_ASSERT(fp->regs == &regs); \
2629 } \
2630 JS_END_MACRO
2631
2632 #else /* !JS_TRACER */
2633
2634 #define MONITOR_BRANCH(oldpc) ((void) 0)
2635
2636 #endif /* !JS_TRACER */
2637
2638 /*
2639 * Prepare to call a user-supplied branch handler, and abort the script
2640 * if it returns false.
2641 */
2642 #define CHECK_BRANCH() \
2643 JS_BEGIN_MACRO \
2644 if ((cx->operationCount -= JSOW_SCRIPT_JUMP) <= 0) { \
2645 if (!js_ResetOperationCount(cx)) \
2646 goto error; \
2647 } \
2648 JS_END_MACRO
2649
2650 #define BRANCH(n) \
2651 JS_BEGIN_MACRO \
2652 regs.pc += n; \
2653 if (n <= 0) { \
2654 CHECK_BRANCH(); \
2655 MONITOR_BRANCH(regs.pc - n); \
2656 } \
2657 op = (JSOp) *regs.pc; \
2658 DO_OP(); \
2659 JS_END_MACRO
2660
2661 MUST_FLOW_THROUGH("exit");
2662 ++cx->interpLevel;
2663
2664 /*
2665 * Optimized Get and SetVersion for proper script language versioning.
2666 *
2667 * If any native method or JSClass/JSObjectOps hook calls js_SetVersion
2668 * and changes cx->version, the effect will "stick" and we will stop
2669 * maintaining currentVersion. This is relied upon by testsuites, for
2670 * the most part -- web browsers select version before compiling and not
2671 * at run-time.
2672 */
2673 currentVersion = (JSVersion) script->version;
2674 originalVersion = (JSVersion) cx->version;
2675 if (currentVersion != originalVersion)
2676 js_SetVersion(cx, currentVersion);
2677
2678 /* Update the static-link display. */
2679 if (script->staticDepth < JS_DISPLAY_SIZE) {
2680 JSStackFrame **disp = &cx->display[script->staticDepth];
2681 fp->displaySave = *disp;
2682 *disp = fp;
2683 }
2684 #ifdef DEBUG
2685 fp->pcDisabledSave = JS_PROPERTY_CACHE(cx).disabled;
2686 #endif
2687
2688 /*
2689 * Load the debugger's interrupt hook here and after calling out to native
2690 * functions (but not to getters, setters, or other native hooks), so we do
2691 * not have to reload it each time through the interpreter loop -- we hope
2692 * the compiler can keep it in a register when it is non-null.
2693 */
2694 #if JS_THREADED_INTERP
2695 #ifdef JS_TRACER
2696 # define LOAD_INTERRUPT_HANDLER(cx) \
2697 ((void) (jumpTable = (cx)->debugHooks->interruptHandler \
2698 ? interruptJumpTable \
2699 : TRACE_RECORDER(cx) \
2700 ? recordingJumpTable \
2701 : normalJumpTable))
2702 # define ENABLE_TRACER(flag) \
2703 JS_BEGIN_MACRO \
2704 bool flag_ = (flag); \
2705 JS_ASSERT(flag_ == !!TRACE_RECORDER(cx)); \
2706 jumpTable = flag_ ? recordingJumpTable : normalJumpTable; \
2707 JS_END_MACRO
2708 #else /* !JS_TRACER */
2709 # define LOAD_INTERRUPT_HANDLER(cx) \
2710 ((void) (jumpTable = (cx)->debugHooks->interruptHandler \
2711 ? interruptJumpTable \
2712 : normalJumpTable))
2713 # define ENABLE_TRACER(flag) ((void)0)
2714 #endif /* !JS_TRACER */
2715 #else /* !JS_THREADED_INTERP */
2716 #ifdef JS_TRACER
2717 # define LOAD_INTERRUPT_HANDLER(cx) \
2718 ((void) (switchMask = ((cx)->debugHooks->interruptHandler || \
2719 TRACE_RECORDER(cx)) ? 0 : 255))
2720 # define ENABLE_TRACER(flag) \
2721 JS_BEGIN_MACRO \
2722 bool flag_ = (flag); \
2723 JS_ASSERT(flag_ == !!TRACE_RECORDER(cx)); \
2724 switchMask = flag_ ? 0 : 255; \
2725 JS_END_MACRO
2726 #else /* !JS_TRACER */
2727 # define LOAD_INTERRUPT_HANDLER(cx) \
2728 ((void) (switchMask = ((cx)->debugHooks->interruptHandler \
2729 ? 0 : 255)))
2730 # define ENABLE_TRACER(flag) ((void)0)
2731 #endif /* !JS_TRACER */
2732 #endif /* !JS_THREADED_INTERP */
2733
2734 LOAD_INTERRUPT_HANDLER(cx);
2735
2736 /* Initialize the pc and pc registers unless we're resuming a generator. */
2737 if (JS_LIKELY(!fp->regs)) {
2738 ASSERT_NOT_THROWING(cx);
2739 regs.pc = script->code;
2740 regs.sp = StackBase(fp);
2741 fp->regs = &regs;
2742 } else {
2743 JSGenerator *gen;
2744
2745 JS_ASSERT(fp->flags & JSFRAME_GENERATOR);
2746 gen = FRAME_TO_GENERATOR(fp);
2747 JS_ASSERT(fp->regs == &gen->savedRegs);
2748 regs = gen->savedRegs;
2749 fp->regs = &regs;
2750 JS_ASSERT((size_t) (regs.pc - script->code) <= script->length);
2751 JS_ASSERT((size_t) (regs.sp - StackBase(fp)) <= StackDepth(script));
2752 JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
2753 JS_PROPERTY_CACHE(cx).disabled += js_CountWithBlocks(cx, fp);
2754
2755 /*
2756 * To support generator_throw and to catch ignored exceptions,
2757 * fail if cx->throwing is set.
2758 */
2759 if (cx->throwing) {
2760 #ifdef DEBUG_NOT_THROWING
2761 if (cx->exception != JSVAL_ARETURN) {
2762 printf("JS INTERPRETER CALLED WITH PENDING EXCEPTION %lx\n",
2763 (unsigned long) cx->exception);
2764 }
2765 #endif
2766 goto error;
2767 }
2768 }
2769
2770 /*
2771 * It is important that "op" be initialized before calling DO_OP because
2772 * it is possible for "op" to be specially assigned during the normal
2773 * processing of an opcode while looping. We rely on DO_NEXT_OP to manage
2774 * "op" correctly in all other cases.
2775 */
2776 len = 0;
2777 DO_NEXT_OP(len);
2778
2779 #if JS_THREADED_INTERP
2780 /*
2781 * This is a loop, but it does not look like a loop. The loop-closing
2782 * jump is distributed throughout goto *jumpTable[op] inside of DO_OP.
2783 * When interrupts are enabled, jumpTable is set to interruptJumpTable
2784 * where all jumps point to the JSOP_INTERRUPT case. The latter, after
2785 * calling the interrupt handler, dispatches through normalJumpTable to
2786 * continue the normal bytecode processing.
2787 */
2788 #else
2789 for (;;) {
2790 advance_pc_by_one:
2791 JS_ASSERT(js_CodeSpec[op].length == 1);
2792 len = 1;
2793 advance_pc:
2794 regs.pc += len;
2795 op = (JSOp) *regs.pc;
2796 #ifdef DEBUG
2797 if (cx->tracefp)
2798 js_TraceOpcode(cx, len);
2799 #endif
2800
2801 do_op:
2802 switchOp = op & switchMask;
2803 do_switch:
2804 switch (switchOp) {
2805 #endif /* !JS_THREADED_INTERP */
2806
2807 BEGIN_CASE(JSOP_INTERRUPT)
2808 {
2809 JSTrapHandler handler;
2810
2811 handler = cx->debugHooks->interruptHandler;
2812 if (handler) {
2813 switch (handler(cx, script, regs.pc, &rval,
2814 cx->debugHooks->interruptHandlerData)) {
2815 case JSTRAP_ERROR:
2816 goto error;
2817 case JSTRAP_CONTINUE:
2818 break;
2819 case JSTRAP_RETURN:
2820 fp->rval = rval;
2821 ok = JS_TRUE;
2822 goto forced_return;
2823 case JSTRAP_THROW:
2824 cx->throwing = JS_TRUE;
2825 cx->exception = rval;
2826 goto error;
2827 default:;
2828 }
2829 #if !JS_THREADED_INTERP
2830 } else {
2831 /* this was not a real interrupt, the tracer is trying to
2832 record a trace */
2833 switchOp = op + 256;
2834 goto do_switch;
2835 #endif
2836 }
2837 LOAD_INTERRUPT_HANDLER(cx);
2838
2839 #if JS_THREADED_INTERP
2840 JS_EXTENSION_(goto *normalJumpTable[op]);
2841 #else
2842 switchOp = op;
2843 goto do_switch;
2844 #endif
2845 }
2846
2847 /* No-ops for ease of decompilation. */
2848 ADD_EMPTY_CASE(JSOP_NOP)
2849 ADD_EMPTY_CASE(JSOP_GROUP)
2850 ADD_EMPTY_CASE(JSOP_CONDSWITCH)
2851 ADD_EMPTY_CASE(JSOP_TRY)
2852 ADD_EMPTY_CASE(JSOP_FINALLY)
2853 #if JS_HAS_XML_SUPPORT
2854 ADD_EMPTY_CASE(JSOP_STARTXML)
2855 ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
2856 #endif
2857 END_EMPTY_CASES
2858
2859 /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
2860 BEGIN_CASE(JSOP_LINENO)
2861 END_CASE(JSOP_LINENO)
2862
2863 BEGIN_CASE(JSOP_PUSH)
2864 PUSH_OPND(JSVAL_VOID);
2865 END_CASE(JSOP_PUSH)
2866
2867 BEGIN_CASE(JSOP_POP)
2868 regs.sp--;
2869 END_CASE(JSOP_POP)
2870
2871 BEGIN_CASE(JSOP_POPN)
2872 regs.sp -= GET_UINT16(regs.pc);
2873 #ifdef DEBUG
2874 JS_ASSERT(StackBase(fp) <= regs.sp);
2875 obj = fp->blockChain;
2876 JS_ASSERT_IF(obj,
2877 OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
2878 <= (size_t) (regs.sp - StackBase(fp)));
2879 for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
2880 clasp = OBJ_GET_CLASS(cx, obj);
2881 if (clasp != &js_BlockClass && clasp != &js_WithClass)
2882 continue;
2883 if (OBJ_GET_PRIVATE(cx, obj) != fp)
2884 break;
2885 JS_ASSERT(StackBase(fp) + OBJ_BLOCK_DEPTH(cx, obj)
2886 + ((clasp == &js_BlockClass)
2887 ? OBJ_BLOCK_COUNT(cx, obj)
2888 : 1)
2889 <= regs.sp);
2890 }
2891 #endif
2892 END_CASE(JSOP_POPN)
2893
2894 BEGIN_CASE(JSOP_SETRVAL)
2895 BEGIN_CASE(JSOP_POPV)
2896 ASSERT_NOT_THROWING(cx);
2897 fp->rval = POP_OPND();
2898 END_CASE(JSOP_POPV)
2899
2900 BEGIN_CASE(JSOP_ENTERWITH)
2901 if (!js_EnterWith(cx, -1))
2902 goto error;
2903
2904 /*
2905 * We must ensure that different "with" blocks have different
2906 * stack depth associated with them. This allows the try handler
2907 * search to properly recover the scope chain. Thus we must keep
2908 * the stack at least at the current level.
2909 *
2910 * We set sp[-1] to the current "with" object to help asserting
2911 * the enter/leave balance in [leavewith].
2912 */
2913 regs.sp[-1] = OBJECT_TO_JSVAL(fp->scopeChain);
2914 END_CASE(JSOP_ENTERWITH)
2915
2916 BEGIN_CASE(JSOP_LEAVEWITH)
2917 JS_ASSERT(regs.sp[-1] == OBJECT_TO_JSVAL(fp->scopeChain));
2918 regs.sp--;
2919 js_LeaveWith(cx);
2920 END_CASE(JSOP_LEAVEWITH)
2921
2922 BEGIN_CASE(JSOP_RETURN)
2923 CHECK_BRANCH();
2924 fp->rval = POP_OPND();
2925 /* FALL THROUGH */
2926
2927 BEGIN_CASE(JSOP_RETRVAL) /* fp->rval already set */
2928 BEGIN_CASE(JSOP_STOP)
2929 /*
2930 * When the inlined frame exits with an exception or an error, ok
2931 * will be false after the inline_return label.
2932 */
2933 ASSERT_NOT_THROWING(cx);
2934 JS_ASSERT(regs.sp == StackBase(fp));
2935 if ((fp->flags & JSFRAME_CONSTRUCTING) &&
2936 JSVAL_IS_PRIMITIVE(fp->rval)) {
2937 if (!fp->fun) {
2938 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2939 JSMSG_BAD_NEW_RESULT,
2940 js_ValueToPrintableString(cx, rval));
2941 goto error;
2942 }
2943 fp->rval = OBJECT_TO_JSVAL(fp->thisp);
2944 }
2945 ok = JS_TRUE;
2946 if (inlineCallCount)
2947 inline_return:
2948 {
2949 JSInlineFrame *ifp = (JSInlineFrame *) fp;
2950 void *hookData = ifp->hookData;
2951
2952 JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled == fp->pcDisabledSave);
2953 JS_ASSERT(!fp->blockChain);
2954 JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
2955
2956 if (script->staticDepth < JS_DISPLAY_SIZE)
2957 cx->display[script->staticDepth] = fp->displaySave;
2958
2959 if (hookData) {
2960 JSInterpreterHook hook;
2961 JSBool status;
2962
2963 hook = cx->debugHooks->callHook;
2964 if (hook) {
2965 /*
2966 * Do not pass &ok directly as exposing the address
2967 * inhibits optimizations and uninitialised warnings.
2968 */
2969 status = ok;
2970 hook(cx, fp, JS_FALSE, &status, hookData);
2971 ok = status;
2972 LOAD_INTERRUPT_HANDLER(cx);
2973 }
2974 }
2975
2976 /*
2977 * If fp has a call object, sync values and clear the back-
2978 * pointer. This can happen for a lightweight function if it
2979 * calls eval unexpectedly (in a way that is hidden from the
2980 * compiler). See bug 325540.
2981 */
2982 if (fp->callobj)
2983 ok &= js_PutCallObject(cx, fp);
2984
2985 if (fp->argsobj)
2986 ok &= js_PutArgsObject(cx, fp);
2987
2988 #ifdef INCLUDE_MOZILLA_DTRACE
2989 /* DTrace function return, inlines */
2990 if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
2991 jsdtrace_function_rval(cx, fp, fp->fun);
2992 if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
2993 jsdtrace_function_return(cx, fp, fp->fun);
2994 #endif
2995
2996 /* Restore context version only if callee hasn't set version. */
2997 if (JS_LIKELY(cx->version == currentVersion)) {
2998 currentVersion = ifp->callerVersion;
2999 if (currentVersion != cx->version)
3000 js_SetVersion(cx, currentVersion);
3001 }
3002
3003 /*
3004 * If inline-constructing, replace primitive rval with the new
3005 * object passed in via |this|, and instrument this constructor
3006 * invocation
3007 */
3008 if (fp->flags & JSFRAME_CONSTRUCTING) {
3009 if (JSVAL_IS_PRIMITIVE(fp->rval))
3010 fp->rval = OBJECT_TO_JSVAL(fp->thisp);
3011 JS_RUNTIME_METER(cx->runtime, constructs);
3012 }
3013
3014 /* Restore caller's registers. */
3015 regs = ifp->callerRegs;
3016
3017 /* Store the return value in the caller's operand frame. */
3018 regs.sp -= 1 + (size_t) ifp->frame.argc;
3019 regs.sp[-1] = fp->rval;
3020
3021 /* Restore cx->fp and release the inline frame's space. */
3022 cx->fp = fp = fp->down;
3023 JS_ASSERT(fp->regs == &ifp->callerRegs);
3024 fp->regs = &regs;
3025 JS_ARENA_RELEASE(&cx->stackPool, ifp->mark);
3026
3027 /* Restore the calling script's interpreter registers. */
3028 script = fp->script;
3029 atoms = script->atomMap.vector;
3030
3031 /* Resume execution in the calling frame. */
3032 inlineCallCount--;
3033 if (JS_LIKELY(ok)) {
3034 #ifdef JS_TRACER
3035 if (TRACE_RECORDER(cx))
3036 RECORD(LeaveFrame);
3037 #endif
3038 JS_ASSERT(js_CodeSpec[*regs.pc].length == JSOP_CALL_LENGTH);
3039 len = JSOP_CALL_LENGTH;
3040 DO_NEXT_OP(len);
3041 }
3042 goto error;
3043 }
3044 goto exit;
3045
3046 BEGIN_CASE(JSOP_DEFAULT)
3047 (void) POP();
3048 /* FALL THROUGH */
3049 BEGIN_CASE(JSOP_GOTO)
3050 len = GET_JUMP_OFFSET(regs.pc);
3051 BRANCH(len);
3052 END_CASE(JSOP_GOTO)
3053
3054 BEGIN_CASE(JSOP_IFEQ)
3055 POP_BOOLEAN(cx, rval, cond);
3056 if (cond == JS_FALSE) {
3057 len = GET_JUMP_OFFSET(regs.pc);
3058 BRANCH(len);
3059 }
3060 END_CASE(JSOP_IFEQ)
3061
3062 BEGIN_CASE(JSOP_IFNE)
3063 POP_BOOLEAN(cx, rval, cond);
3064 if (cond != JS_FALSE) {
3065 len = GET_JUMP_OFFSET(regs.pc);
3066 BRANCH(len);
3067 }
3068 END_CASE(JSOP_IFNE)
3069
3070 BEGIN_CASE(JSOP_OR)
3071 POP_BOOLEAN(cx, rval, cond);
3072 if (cond == JS_TRUE) {
3073 len = GET_JUMP_OFFSET(regs.pc);
3074 PUSH_OPND(rval);
3075 DO_NEXT_OP(len);
3076 }
3077 END_CASE(JSOP_OR)
3078
3079 BEGIN_CASE(JSOP_AND)
3080 POP_BOOLEAN(cx, rval, cond);
3081 if (cond == JS_FALSE) {
3082 len = GET_JUMP_OFFSET(regs.pc);
3083 PUSH_OPND(rval);
3084 DO_NEXT_OP(len);
3085 }
3086 END_CASE(JSOP_AND)
3087
3088 BEGIN_CASE(JSOP_DEFAULTX)
3089 (void) POP();
3090 /* FALL THROUGH */
3091 BEGIN_CASE(JSOP_GOTOX)
3092 len = GET_JUMPX_OFFSET(regs.pc);
3093 BRANCH(len);
3094 END_CASE(JSOP_GOTOX);
3095
3096 BEGIN_CASE(JSOP_IFEQX)
3097 POP_BOOLEAN(cx, rval, cond);
3098 if (cond == JS_FALSE) {
3099 len = GET_JUMPX_OFFSET(regs.pc);
3100 BRANCH(len);
3101 }
3102 END_CASE(JSOP_IFEQX)
3103
3104 BEGIN_CASE(JSOP_IFNEX)
3105 POP_BOOLEAN(cx, rval, cond);
3106 if (cond != JS_FALSE) {
3107 len = GET_JUMPX_OFFSET(regs.pc);
3108 BRANCH(len);
3109 }
3110 END_CASE(JSOP_IFNEX)
3111
3112 BEGIN_CASE(JSOP_ORX)
3113 POP_BOOLEAN(cx, rval, cond);
3114 if (cond == JS_TRUE) {
3115 len = GET_JUMPX_OFFSET(regs.pc);
3116 PUSH_OPND(rval);
3117 DO_NEXT_OP(len);
3118 }
3119 END_CASE(JSOP_ORX)
3120
3121 BEGIN_CASE(JSOP_ANDX)
3122 POP_BOOLEAN(cx, rval, cond);
3123 if (cond == JS_FALSE) {
3124 len = GET_JUMPX_OFFSET(regs.pc);
3125 PUSH_OPND(rval);
3126 DO_NEXT_OP(len);
3127 }
3128 END_CASE(JSOP_ANDX)
3129
3130 /*
3131 * If the index value at sp[n] is not an int that fits in a jsval, it could
3132 * be an object (an XML QName, AttributeName, or AnyName), but only if we are
3133 * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a
3134 * string atom id.
3135 */
3136 #define FETCH_ELEMENT_ID(obj, n, id) \
3137 JS_BEGIN_MACRO \
3138 jsval idval_ = FETCH_OPND(n); \
3139 if (JSVAL_IS_INT(idval_)) { \
3140 id = INT_JSVAL_TO_JSID(idval_); \
3141 } else { \
3142 if (!js_InternNonIntElementId(cx, obj, idval_, &id)) \
3143 goto error; \
3144 regs.sp[n] = ID_TO_VALUE(id); \
3145 } \
3146 JS_END_MACRO
3147
3148 #define TRY_BRANCH_AFTER_COND(cond,spdec) \
3149 JS_BEGIN_MACRO \
3150 uintN diff_; \
3151 JS_ASSERT(js_CodeSpec[op].length == 1); \
3152 diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ; \
3153 if (diff_ <= 1) { \
3154 regs.sp -= spdec; \
3155 if (cond == (diff_ != 0)) { \
3156 ++regs.pc; \
3157 len = GET_JUMP_OFFSET(regs.pc); \
3158 BRANCH(len); \
3159 } \
3160 len = 1 + JSOP_IFEQ_LENGTH; \
3161 DO_NEXT_OP(len); \
3162 } \
3163 JS_END_MACRO
3164
3165 BEGIN_CASE(JSOP_IN)
3166 rval = FETCH_OPND(-1);
3167 if (JSVAL_IS_PRIMITIVE(rval)) {
3168 js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rval, NULL);
3169 goto error;
3170 }
3171 obj = JSVAL_TO_OBJECT(rval);
3172 FETCH_ELEMENT_ID(obj, -2, id);
3173 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
3174 goto error;
3175 cond = prop != NULL;
3176 if (prop)
3177 OBJ_DROP_PROPERTY(cx, obj2, prop);
3178 TRY_BRANCH_AFTER_COND(cond, 2);
3179 regs.sp--;
3180 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));
3181 END_CASE(JSOP_IN)
3182
3183 BEGIN_CASE(JSOP_ITER)
3184 flags = regs.pc[1];
3185 JS_ASSERT(regs.sp > StackBase(fp));
3186 if (!js_ValueToIterator(cx, flags, &regs.sp[-1]))
3187 goto error;
3188 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));
3189 END_CASE(JSOP_ITER)
3190
3191 BEGIN_CASE(JSOP_FORPROP)
3192 /*
3193 * Handle JSOP_FORPROP first, so the cost of the goto do_forinloop
3194 * is not paid for the more common cases.
3195 */
3196 LOAD_ATOM(0);
3197 id = ATOM_TO_JSID(atom);
3198 i = -2;
3199 goto do_forinloop;
3200
3201 BEGIN_CASE(JSOP_FORNAME)
3202 LOAD_ATOM(0);
3203 id = ATOM_TO_JSID(atom);
3204 /* FALL THROUGH */
3205
3206 BEGIN_CASE(JSOP_FORARG)
3207 BEGIN_CASE(JSOP_FORCONST)
3208 BEGIN_CASE(JSOP_FORLOCAL)
3209 /*
3210 * These bytecodes don't require any lval computation here,
3211 * because they address slots on the stack (in fp->args or
3212 * fp->slots).
3213 */
3214 /* FALL THROUGH */
3215
3216 BEGIN_CASE(JSOP_FORELEM)
3217 /*
3218 * JSOP_FORELEM simply initializes or updates the iteration state
3219 * and leaves the index expression evaluation and assignment to the
3220 * enumerator until after the next property has been acquired, via
3221 * a JSOP_ENUMELEM bytecode.
3222 */
3223 i = -1;
3224
3225 do_forinloop:
3226 /*
3227 * Reach under the top of stack to find our property iterator, a
3228 * JSObject that contains the iteration state.
3229 */
3230 JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[i]));
3231 if (!js_CallIteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[i]), &rval))
3232 goto error;
3233 if (rval == JSVAL_HOLE) {
3234 rval = JSVAL_FALSE;
3235 #ifdef JS_TRACER
3236 if (TRACE_RECORDER(cx)) {
3237 js_AbortRecording(cx, regs.pc, "Untraceable for-in loop");
3238 ENABLE_TRACER(0);
3239 }
3240 #endif
3241 goto end_forinloop;
3242 }
3243
3244 switch (op) {
3245 case JSOP_FORARG:
3246 slot = GET_ARGNO(regs.pc);
3247 JS_ASSERT(slot < fp->fun->nargs);
3248 fp->argv[slot] = rval;
3249 break;
3250
3251 case JSOP_FORCONST:
3252 /* Don't update the const slot. */
3253 break;
3254
3255 case JSOP_FORLOCAL:
3256 slot = GET_SLOTNO(regs.pc);
3257 JS_ASSERT(slot < fp->script->nslots);
3258 vp = &fp->slots[slot];
3259 GC_POKE(cx, *vp);
3260 *vp = rval;
3261 break;
3262
3263 case JSOP_FORELEM:
3264 /* FORELEM is not a SET operation, it's more like BINDNAME. */
3265 PUSH_OPND(rval);
3266 break;
3267
3268 case JSOP_FORPROP:
3269 /*
3270 * We fetch object here to ensure that the iterator is called
3271 * even if lval is null or undefined that throws in
3272 * FETCH_OBJECT. See bug 372331.
3273 */
3274 FETCH_OBJECT(cx, -1, lval, obj);
3275 goto set_for_property;
3276
3277 default:
3278 JS_ASSERT(op == JSOP_FORNAME);
3279
3280 /*
3281 * We find property here after the iterator call to ensure
3282 * that we take into account side effects of the iterator
3283 * call. See bug 372331.
3284 */
3285 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
3286 goto error;
3287 if (prop)
3288 OBJ_DROP_PROPERTY(cx, obj2, prop);
3289
3290 set_for_property:
3291 /* Set the variable obj[id] to refer to rval. */
3292 fp->flags |= JSFRAME_ASSIGNING;
3293 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
3294 fp->flags &= ~JSFRAME_ASSIGNING;
3295 if (!ok)
3296 goto error;
3297 break;
3298 }
3299
3300 /* Push true to keep looping through properties. */
3301 rval = JSVAL_TRUE;
3302
3303 end_forinloop:
3304 regs.sp += i + 1;
3305 PUSH_OPND(rval);
3306 len = js_CodeSpec[op].length;
3307 DO_NEXT_OP(len);
3308
3309 BEGIN_CASE(JSOP_DUP)
3310 JS_ASSERT(regs.sp > StackBase(fp));
3311 rval = FETCH_OPND(-1);
3312 PUSH(rval);
3313 END_CASE(JSOP_DUP)
3314
3315 BEGIN_CASE(JSOP_DUP2)
3316 JS_ASSERT(regs.sp - 2 >= StackBase(fp));
3317 lval = FETCH_OPND(-2);
3318 rval = FETCH_OPND(-1);
3319 PUSH(lval);
3320 PUSH(rval);
3321 END_CASE(JSOP_DUP2)
3322
3323 #define PROPERTY_OP(n, call) \
3324 JS_BEGIN_MACRO \
3325 /* Fetch the left part and resolve it to a non-null object. */ \
3326 FETCH_OBJECT(cx, n, lval, obj); \
3327 \
3328 /* Get or set the property. */ \
3329 if (!call) \
3330 goto error; \
3331 JS_END_MACRO
3332
3333 #define ELEMENT_OP(n, call) \
3334 JS_BEGIN_MACRO \
3335 /* Fetch the left part and resolve it to a non-null object. */ \
3336 FETCH_OBJECT(cx, n - 1, lval, obj); \
3337 \
3338 /* Fetch index and convert it to id suitable for use with obj. */ \
3339 FETCH_ELEMENT_ID(obj, n, id); \
3340 \
3341 /* Get or set the element. */ \
3342 if (!call) \
3343 goto error; \
3344 JS_END_MACRO
3345
3346 #define NATIVE_GET(cx,obj,pobj,sprop,vp) \
3347 JS_BEGIN_MACRO \
3348 if (SPROP_HAS_STUB_GETTER(sprop)) { \
3349 /* Fast path for Object instance properties. */ \
3350 JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT || \
3351 !SPROP_HAS_STUB_SETTER(sprop)); \
3352 *vp = ((sprop)->slot != SPROP_INVALID_SLOT) \
3353 ? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot) \
3354 : JSVAL_VOID; \
3355 } else { \
3356 if (!js_NativeGet(cx, obj, pobj, sprop, vp)) \
3357 goto error; \
3358 } \
3359 JS_END_MACRO
3360
3361 #define NATIVE_SET(cx,obj,sprop,vp) \
3362 JS_BEGIN_MACRO \
3363 if (SPROP_HAS_STUB_SETTER(sprop) && \
3364 (sprop)->slot != SPROP_INVALID_SLOT) { \
3365 /* Fast path for, e.g., Object instance properties. */ \
3366 LOCKED_OBJ_WRITE_BARRIER(cx, obj, (sprop)->slot, *vp); \
3367 } else { \
3368 if (!js_NativeSet(cx, obj, sprop, vp)) \
3369 goto error; \
3370 } \
3371 JS_END_MACRO
3372
3373 /*
3374 * Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on
3375 * single-thread DEBUG js shell testing to verify property cache hits.
3376 */
3377 #if defined DEBUG && !defined JS_THREADSAFE
3378 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) \
3379 do { \
3380 JSAtom *atom_; \
3381 JSObject *obj_, *pobj_; \
3382 JSProperty *prop_; \
3383 JSScopeProperty *sprop_; \
3384 uint32 sample_ = rt->gcNumber; \
3385 if (pcoff >= 0) \
3386 GET_ATOM_FROM_BYTECODE(script, regs.pc, pcoff, atom_); \
3387 else \
3388 atom_ = rt->atomState.lengthAtom; \
3389 if (JOF_OPMODE(*regs.pc) == JOF_NAME) { \
3390 ok = js_FindProperty(cx, ATOM_TO_JSID(atom_), &obj_, &pobj_, \
3391 &prop_); \
3392 } else { \
3393 obj_ = obj; \
3394 ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(atom_), &pobj_, \
3395 &prop_); \
3396 } \
3397 if (!ok) \
3398 goto error; \
3399 if (rt->gcNumber != sample_) \
3400 break; \
3401 JS_ASSERT(prop_); \
3402 JS_ASSERT(pobj_ == pobj); \
3403 sprop_ = (JSScopeProperty *) prop_; \
3404 if (PCVAL_IS_SLOT(entry->vword)) { \
3405 JS_ASSERT(PCVAL_TO_SLOT(entry->vword) == sprop_->slot); \
3406 } else if (PCVAL_IS_SPROP(entry->vword)) { \
3407 JS_ASSERT(PCVAL_TO_SPROP(entry->vword) == sprop_); \
3408 } else { \
3409 jsval v_; \
3410 JS_ASSERT(PCVAL_IS_OBJECT(entry->vword)); \
3411 JS_ASSERT(entry->vword != PCVAL_NULL); \
3412 JS_ASSERT(SCOPE_IS_BRANDED(OBJ_SCOPE(pobj))); \
3413 JS_ASSERT(SPROP_HAS_STUB_GETTER(sprop_)); \
3414 JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop_, OBJ_SCOPE(pobj_))); \
3415 v_ = LOCKED_OBJ_GET_SLOT(pobj_, sprop_->slot); \
3416 JS_ASSERT(VALUE_IS_FUNCTION(cx, v_)); \
3417 JS_ASSERT(PCVAL_TO_OBJECT(entry->vword) == JSVAL_TO_OBJECT(v_)); \
3418 } \
3419 OBJ_DROP_PROPERTY(cx, pobj_, prop_); \
3420 } while (0)
3421 #else
3422 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0)
3423 #endif
3424
3425 /*
3426 * Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is
3427 * the constant length of the SET opcode sequence, and spdec is the constant
3428 * by which to decrease the stack pointer to pop all of the SET op's operands.
3429 *
3430 * NB: unlike macros that could conceivably be replaced by functions (ignoring
3431 * goto error), where a call should not have to be braced in order to expand
3432 * correctly (e.g., in if (cond) FOO(); else BAR()), these three macros lack
3433 * JS_{BEGIN,END}_MACRO brackets. They are also indented so as to align with
3434 * nearby opcode code.
3435 */
3436 #define SKIP_POP_AFTER_SET(oplen,spdec) \
3437 if (regs.pc[oplen] == JSOP_POP) { \
3438 regs.sp -= spdec; \
3439 regs.pc += oplen + JSOP_POP_LENGTH; \
3440 op = (JSOp) *regs.pc; \
3441 DO_OP(); \
3442 }
3443
3444 #define END_SET_CASE(OP) \
3445 SKIP_POP_AFTER_SET(OP##_LENGTH, 1); \
3446 END_CASE(OP)
3447
3448 #define END_SET_CASE_STORE_RVAL(OP,spdec) \
3449 SKIP_POP_AFTER_SET(OP##_LENGTH, spdec); \
3450 rval = FETCH_OPND(-1); \
3451 regs.sp -= (spdec) - 1; \
3452 STORE_OPND(-1, rval); \
3453 END_CASE(OP)
3454
3455 BEGIN_CASE(JSOP_SETCONST)
3456 LOAD_ATOM(0);
3457 obj = fp->varobj;
3458 rval = FETCH_OPND(-1);
3459 if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval,
3460 JS_PropertyStub, JS_PropertyStub,
3461 JSPROP_ENUMERATE | JSPROP_PERMANENT |
3462 JSPROP_READONLY,
3463 NULL)) {
3464 goto error;
3465 }
3466 END_SET_CASE(JSOP_SETCONST);
3467
3468 #if JS_HAS_DESTRUCTURING
3469 BEGIN_CASE(JSOP_ENUMCONSTELEM)
3470 rval = FETCH_OPND(-3);
3471 FETCH_OBJECT(cx, -2, lval, obj);
3472 FETCH_ELEMENT_ID(obj, -1, id);