Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / jsinterp.cpp
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sw=4 et tw=99:
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 <stdio.h>
45 #include <string.h>
46 #include <math.h>
47 #include "jstypes.h"
48 #include "jsstdint.h"
49 #include "jsarena.h"
50 #include "jsutil.h"
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 "jsdate.h"
58 #include "jsversion.h"
59 #include "jsdbgapi.h"
60 #include "jsfun.h"
61 #include "jsgc.h"
62 #include "jsinterp.h"
63 #include "jsiter.h"
64 #include "jslock.h"
65 #include "jsnum.h"
66 #include "jsobj.h"
67 #include "jsopcode.h"
68 #include "jspropertycache.h"
69 #include "jsscan.h"
70 #include "jsemit.h"
71 #include "jsscope.h"
72 #include "jsscript.h"
73 #include "jsstr.h"
74 #include "jsstaticcheck.h"
75 #include "jstracer.h"
76 #include "jslibmath.h"
77 #include "jsvector.h"
78 #include "methodjit/MethodJIT.h"
79 #include "methodjit/MethodJIT-inl.h"
80 #include "methodjit/Logging.h"
81
82 #include "jsatominlines.h"
83 #include "jscntxtinlines.h"
84 #include "jsinterpinlines.h"
85 #include "jsobjinlines.h"
86 #include "jsprobes.h"
87 #include "jspropertycacheinlines.h"
88 #include "jsscopeinlines.h"
89 #include "jsscriptinlines.h"
90 #include "jsstrinlines.h"
91 #include "jsopcodeinlines.h"
92
93 #if JS_HAS_XML_SUPPORT
94 #include "jsxml.h"
95 #endif
96
97 #include "jsautooplen.h"
98
99 #if defined(JS_METHODJIT) && defined(JS_MONOIC)
100 #include "methodjit/MonoIC.h"
101 #endif
102
103 using namespace js;
104 using namespace js::gc;
105
106 /* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */
107 #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___
108
109 #ifdef DEBUG
110 JSObject *const JSStackFrame::sInvalidScopeChain = (JSObject *)0xbeef;
111 #endif
112
113 jsbytecode *
114 JSStackFrame::pc(JSContext *cx, JSStackFrame *next)
115 {
116     JS_ASSERT_IF(next, next->prev_ == this);
117     JS_ASSERT(cx->containingSegment(this) != NULL);
118
119     JSFrameRegs *regs;
120     if (cx->regs) {
121         regs = cx->regs;
122     } else {
123         StackSegment *segment = cx->getCurrentSegment();
124         regs = segment->getSuspendedRegs();
125     }
126
127     if (this == regs->fp)
128         return regs->pc;
129
130     if (!next)
131         next = cx->computeNextFrame(this);
132
133     if (next->flags_ & JSFRAME_HAS_PREVPC)
134         return next->prevpc_;
135
136 #if defined(JS_METHODJIT) && defined(JS_MONOIC)
137     js::mjit::JITScript *jit = script()->getJIT(isConstructing());
138     return jit->nativeToPC(next->ncode_);
139 #else
140     JS_NOT_REACHED("Unknown PC for frame");
141     return NULL;
142 #endif
143 }
144
145 JSObject *
146 js::GetScopeChain(JSContext *cx)
147 {
148     JSStackFrame *fp = js_GetTopStackFrame(cx);
149     if (!fp) {
150         /*
151          * There is no code active on this context. In place of an actual
152          * scope chain, use the context's global object, which is set in
153          * js_InitFunctionAndObjectClasses, and which represents the default
154          * scope chain for the embedding. See also js_FindClassObject.
155          *
156          * For embeddings that use the inner and outer object hooks, the inner
157          * object represents the ultimate global object, with the outer object
158          * acting as a stand-in.
159          */
160         JSObject *obj = cx->globalObject;
161         if (!obj) {
162             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE);
163             return NULL;
164         }
165
166         OBJ_TO_INNER_OBJECT(cx, obj);
167         return obj;
168     }
169     return GetScopeChain(cx, fp);
170 }
171
172 /*
173  * This computes the blockChain by iterating through the bytecode
174  * of the current script until it reaches the PC. Each time it sees
175  * an ENTERBLOCK or LEAVEBLOCK instruction, it records the new
176  * blockChain. A faster variant of this function that doesn't
177  * require bytecode scanning appears below.
178  */
179 JSObject *
180 js::GetBlockChain(JSContext *cx, JSStackFrame *fp)
181 {
182     if (!fp->isScriptFrame())
183         return NULL;
184
185     /* Assume that imacros don't affect blockChain */
186     jsbytecode *target = fp->hasImacropc() ? fp->imacropc() : fp->pc(cx);
187
188     JSScript *script = fp->script();
189     jsbytecode *start = script->code;
190     JS_ASSERT(target >= start && target < start + script->length);
191
192     JSObject *blockChain = NULL;
193     uintN indexBase = 0;
194     ptrdiff_t oplen;
195     for (jsbytecode *pc = start; pc < target; pc += oplen) {
196         JSOp op = js_GetOpcode(cx, script, pc);
197         const JSCodeSpec *cs = &js_CodeSpec[op];
198         oplen = cs->length;
199         if (oplen < 0)
200             oplen = js_GetVariableBytecodeLength(pc);
201
202         if (op == JSOP_INDEXBASE)
203             indexBase = GET_INDEXBASE(pc);
204         else if (op == JSOP_INDEXBASE1 || op == JSOP_INDEXBASE2 || op == JSOP_INDEXBASE3)
205             indexBase = (op - JSOP_INDEXBASE1 + 1) << 16;
206         else if (op == JSOP_RESETBASE || op == JSOP_RESETBASE0)
207             indexBase = 0;
208         else if (op == JSOP_ENTERBLOCK)
209             blockChain = script->getObject(indexBase + GET_INDEX(pc));
210         else if (op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR)
211             blockChain = blockChain->getParent();
212         else if (op == JSOP_BLOCKCHAIN)
213             blockChain = script->getObject(indexBase + GET_INDEX(pc));
214         else if (op == JSOP_NULLBLOCKCHAIN)
215             blockChain = NULL;
216     }
217
218     return blockChain;
219 }
220
221 /*
222  * This function computes the current blockChain, but only in
223  * the special case where a BLOCKCHAIN or NULLBLOCKCHAIN
224  * instruction appears immediately after the current PC.
225  * We ensure this happens for a few important ops like DEFFUN.
226  * |oplen| is the length of opcode at the current PC.
227  */
228 JSObject *
229 js::GetBlockChainFast(JSContext *cx, JSStackFrame *fp, JSOp op, size_t oplen)
230 {
231     /* Assume that we're in a script frame. */
232     jsbytecode *pc = fp->pc(cx);
233     JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == op);
234
235     pc += oplen;
236     op = JSOp(*pc);
237     JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == op);
238
239     /* The fast paths assume no JSOP_RESETBASE/INDEXBASE noise. */
240     if (op == JSOP_NULLBLOCKCHAIN)
241         return NULL;
242     if (op == JSOP_BLOCKCHAIN)
243         return fp->script()->getObject(GET_INDEX(pc));
244
245     return GetBlockChain(cx, fp);
246 }
247
248 /*
249  * We can't determine in advance which local variables can live on the stack and
250  * be freed when their dynamic scope ends, and which will be closed over and
251  * need to live in the heap.  So we place variables on the stack initially, note
252  * when they are closed over, and copy those that are out to the heap when we
253  * leave their dynamic scope.
254  *
255  * The bytecode compiler produces a tree of block objects accompanying each
256  * JSScript representing those lexical blocks in the script that have let-bound
257  * variables associated with them.  These block objects are never modified, and
258  * never become part of any function's scope chain.  Their parent slots point to
259  * the innermost block that encloses them, or are NULL in the outermost blocks
260  * within a function or in eval or global code.
261  *
262  * When we are in the static scope of such a block, blockChain points to its
263  * compiler-allocated block object; otherwise, it is NULL.
264  *
265  * scopeChain is the current scope chain, including 'call' and 'block' objects
266  * for those function calls and lexical blocks whose static scope we are
267  * currently executing in, and 'with' objects for with statements; the chain is
268  * typically terminated by a global object.  However, as an optimization, the
269  * young end of the chain omits block objects we have not yet cloned.  To create
270  * a closure, we clone the missing blocks from blockChain (which is always
271  * current), place them at the head of scopeChain, and use that for the
272  * closure's scope chain.  If we never close over a lexical block, we never
273  * place a mutable clone of it on scopeChain.
274  *
275  * This lazy cloning is implemented in GetScopeChain, which is also used in
276  * some other cases --- entering 'with' blocks, for example.
277  */
278 static JSObject *
279 GetScopeChainFull(JSContext *cx, JSStackFrame *fp, JSObject *blockChain)
280 {
281     JSObject *sharedBlock = blockChain;
282
283     if (!sharedBlock) {
284         /*
285          * Don't force a call object for a lightweight function call, but do
286          * insist that there is a call object for a heavyweight function call.
287          */
288         JS_ASSERT_IF(fp->isFunctionFrame() && fp->fun()->isHeavyweight(),
289                      fp->hasCallObj());
290         return &fp->scopeChain();
291     }
292
293     /* We don't handle cloning blocks on trace.  */
294     LeaveTrace(cx);
295
296     /*
297      * We have one or more lexical scopes to reflect into fp->scopeChain, so
298      * make sure there's a call object at the current head of the scope chain,
299      * if this frame is a call frame.
300      *
301      * Also, identify the innermost compiler-allocated block we needn't clone.
302      */
303     JSObject *limitBlock, *limitClone;
304     if (fp->isFunctionFrame() && !fp->hasCallObj()) {
305         JS_ASSERT_IF(fp->scopeChain().isClonedBlock(),
306                      fp->scopeChain().getPrivate() != js_FloatingFrameIfGenerator(cx, fp));
307         if (!js_GetCallObject(cx, fp))
308             return NULL;
309
310         /* We know we must clone everything on blockChain. */
311         limitBlock = limitClone = NULL;
312     } else {
313         /*
314          * scopeChain includes all blocks whose static scope we're within that
315          * have already been cloned.  Find the innermost such block.  Its
316          * prototype should appear on blockChain; we'll clone blockChain up
317          * to, but not including, that prototype.
318          */
319         limitClone = &fp->scopeChain();
320         while (limitClone->getClass() == &js_WithClass)
321             limitClone = limitClone->getParent();
322         JS_ASSERT(limitClone);
323
324         /*
325          * It may seem like we don't know enough about limitClone to be able
326          * to just grab its prototype as we do here, but it's actually okay.
327          *
328          * If limitClone is a block object belonging to this frame, then its
329          * prototype is the innermost entry in blockChain that we have already
330          * cloned, and is thus the place to stop when we clone below.
331          *
332          * Otherwise, there are no blocks for this frame on scopeChain, and we
333          * need to clone the whole blockChain.  In this case, limitBlock can
334          * point to any object known not to be on blockChain, since we simply
335          * loop until we hit limitBlock or NULL.  If limitClone is a block, it
336          * isn't a block from this function, since blocks can't be nested
337          * within themselves on scopeChain (recursion is dynamic nesting, not
338          * static nesting).  If limitClone isn't a block, its prototype won't
339          * be a block either.  So we can just grab limitClone's prototype here
340          * regardless of its type or which frame it belongs to.
341          */
342         limitBlock = limitClone->getProto();
343
344         /* If the innermost block has already been cloned, we are done. */
345         if (limitBlock == sharedBlock)
346             return &fp->scopeChain();
347     }
348
349     /*
350      * Special-case cloning the innermost block; this doesn't have enough in
351      * common with subsequent steps to include in the loop.
352      *
353      * js_CloneBlockObject leaves the clone's parent slot uninitialized. We
354      * populate it below.
355      */
356     JSObject *innermostNewChild = js_CloneBlockObject(cx, sharedBlock, fp);
357     if (!innermostNewChild)
358         return NULL;
359     AutoObjectRooter tvr(cx, innermostNewChild);
360
361     /*
362      * Clone our way towards outer scopes until we reach the innermost
363      * enclosing function, or the innermost block we've already cloned.
364      */
365     JSObject *newChild = innermostNewChild;
366     for (;;) {
367         JS_ASSERT(newChild->getProto() == sharedBlock);
368         sharedBlock = sharedBlock->getParent();
369
370         /* Sometimes limitBlock will be NULL, so check that first.  */
371         if (sharedBlock == limitBlock || !sharedBlock)
372             break;
373
374         /* As in the call above, we don't know the real parent yet.  */
375         JSObject *clone = js_CloneBlockObject(cx, sharedBlock, fp);
376         if (!clone)
377             return NULL;
378
379         newChild->setParent(clone);
380         newChild = clone;
381     }
382     newChild->setParent(&fp->scopeChain());
383
384
385     /*
386      * If we found a limit block belonging to this frame, then we should have
387      * found it in blockChain.
388      */
389     JS_ASSERT_IF(limitBlock &&
390                  limitBlock->isBlock() &&
391                  limitClone->getPrivate() == js_FloatingFrameIfGenerator(cx, fp),
392                  sharedBlock);
393
394     /* Place our newly cloned blocks at the head of the scope chain.  */
395     fp->setScopeChainNoCallObj(*innermostNewChild);
396     return innermostNewChild;
397 }
398
399 JSObject *
400 js::GetScopeChain(JSContext *cx, JSStackFrame *fp)
401 {
402     return GetScopeChainFull(cx, fp, GetBlockChain(cx, fp));
403 }
404
405 JSObject *
406 js::GetScopeChainFast(JSContext *cx, JSStackFrame *fp, JSOp op, size_t oplen)
407 {
408     return GetScopeChainFull(cx, fp, GetBlockChainFast(cx, fp, op, oplen));
409 }
410
411 /* Some objects (e.g., With) delegate 'this' to another object. */
412 static inline JSObject *
413 CallThisObjectHook(JSContext *cx, JSObject *obj, Value *argv)
414 {
415     JSObject *thisp = obj->thisObject(cx);
416     if (!thisp)
417         return NULL;
418     argv[-1].setObject(*thisp);
419     return thisp;
420 }
421
422 /*
423  * ECMA requires "the global object", but in embeddings such as the browser,
424  * which have multiple top-level objects (windows, frames, etc. in the DOM),
425  * we prefer fun's parent.  An example that causes this code to run:
426  *
427  *   // in window w1
428  *   function f() { return this }
429  *   function g() { return f }
430  *
431  *   // in window w2
432  *   var h = w1.g()
433  *   alert(h() == w1)
434  *
435  * The alert should display "true".
436  */
437 JS_STATIC_INTERPRET bool
438 ComputeGlobalThis(JSContext *cx, Value *vp)
439 {
440     JSObject *thisp = vp[0].toObject().getGlobal()->thisObject(cx);
441     if (!thisp)
442         return false;
443     vp[1].setObject(*thisp);
444     return true;
445 }
446
447 namespace js {
448
449 void
450 ReportIncompatibleMethod(JSContext *cx, Value *vp, Class *clasp)
451 {
452     Value &thisv = vp[1];
453
454 #ifdef DEBUG
455     if (thisv.isObject()) {
456         JS_ASSERT(thisv.toObject().getClass() != clasp);
457     } else if (thisv.isString()) {
458         JS_ASSERT(clasp != &js_StringClass);
459     } else if (thisv.isNumber()) {
460         JS_ASSERT(clasp != &js_NumberClass);
461     } else if (thisv.isBoolean()) {
462         JS_ASSERT(clasp != &js_BooleanClass);
463     } else {
464         JS_ASSERT(thisv.isUndefined() || thisv.isNull());
465     }
466 #endif
467
468     if (JSFunction *fun = js_ValueToFunction(cx, &vp[0], 0)) {
469         const char *name = thisv.isObject()
470                            ? thisv.toObject().getClass()->name
471                            : thisv.isString()
472                            ? "string"
473                            : thisv.isNumber()
474                            ? "number"
475                            : thisv.isBoolean()
476                            ? "boolean"
477                            : thisv.isNull()
478                            ? js_null_str
479                            : thisv.isUndefined()
480                            ? js_undefined_str
481                            : "value";
482         JSAutoByteString funNameBytes;
483         if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
484             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
485                                  clasp->name, funName, name);
486         }
487     }
488 }
489
490 bool
491 BoxThisForVp(JSContext *cx, Value *vp)
492 {
493     /*
494      * Check for SynthesizeFrame poisoning and fast constructors which
495      * didn't check their vp properly.
496      */
497     JS_ASSERT(!vp[1].isMagic());
498 #ifdef DEBUG
499     JSFunction *fun = vp[0].toObject().isFunction() ? vp[0].toObject().getFunctionPrivate() : NULL;
500     JS_ASSERT_IF(fun && fun->isInterpreted(), !fun->inStrictMode());
501 #endif
502
503     if (vp[1].isNullOrUndefined())
504         return ComputeGlobalThis(cx, vp);
505
506     if (!vp[1].isObject())
507         return !!js_PrimitiveToObject(cx, &vp[1]);
508
509     return true;
510 }
511
512 }
513
514 #if JS_HAS_NO_SUCH_METHOD
515
516 const uint32 JSSLOT_FOUND_FUNCTION  = 0;
517 const uint32 JSSLOT_SAVED_ID        = 1;
518
519 Class js_NoSuchMethodClass = {
520     "NoSuchMethod",
521     JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
522     PropertyStub,         /* addProperty */
523     PropertyStub,         /* delProperty */
524     PropertyStub,         /* getProperty */
525     StrictPropertyStub,   /* setProperty */
526     EnumerateStub,
527     ResolveStub,
528     ConvertStub,
529 };
530
531 /*
532  * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of
533  * the base object, we search for the __noSuchMethod__ method in the base.
534  * If it exists, we store the method and the property's id into an object of
535  * NoSuchMethod class and store this object into the callee's stack slot.
536  * Later, js_Invoke will recognise such an object and transfer control to
537  * NoSuchMethod that invokes the method like:
538  *
539  *   this.__noSuchMethod__(id, args)
540  *
541  * where id is the name of the method that this invocation attempted to
542  * call by name, and args is an Array containing this invocation's actual
543  * parameters.
544  */
545 JSBool
546 js_OnUnknownMethod(JSContext *cx, Value *vp)
547 {
548     JS_ASSERT(!vp[1].isPrimitive());
549
550     JSObject *obj = &vp[1].toObject();
551     jsid id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
552     AutoValueRooter tvr(cx);
553     if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, tvr.addr()))
554         return false;
555     if (tvr.value().isPrimitive()) {
556         vp[0] = tvr.value();
557     } else {
558 #if JS_HAS_XML_SUPPORT
559         /* Extract the function name from function::name qname. */
560         if (vp[0].isObject()) {
561             obj = &vp[0].toObject();
562             if (!js_IsFunctionQName(cx, obj, &id))
563                 return false;
564             if (!JSID_IS_VOID(id))
565                 vp[0] = IdToValue(id);
566         }
567 #endif
568         obj = js_NewGCObject(cx, FINALIZE_OBJECT2);
569         if (!obj)
570             return false;
571
572         /*
573          * Null map to cause prompt and safe crash if this object were to
574          * escape due to a bug. This will make the object appear to be a
575          * stillborn instance that needs no finalization, which is sound:
576          * NoSuchMethod helper objects own no manually allocated resources.
577          */
578         obj->map = NULL;
579         obj->init(cx, &js_NoSuchMethodClass, NULL, NULL, NULL, false);
580         obj->setSlot(JSSLOT_FOUND_FUNCTION, tvr.value());
581         obj->setSlot(JSSLOT_SAVED_ID, vp[0]);
582         vp[0].setObject(*obj);
583     }
584     return true;
585 }
586
587 static JS_REQUIRES_STACK JSBool
588 NoSuchMethod(JSContext *cx, uintN argc, Value *vp, uint32 flags)
589 {
590     InvokeArgsGuard args;
591     if (!cx->stack().pushInvokeArgs(cx, 2, &args))
592         return JS_FALSE;
593
594     JS_ASSERT(vp[0].isObject());
595     JS_ASSERT(vp[1].isObject());
596     JSObject *obj = &vp[0].toObject();
597     JS_ASSERT(obj->getClass() == &js_NoSuchMethodClass);
598
599     args.callee() = obj->getSlot(JSSLOT_FOUND_FUNCTION);
600     args.thisv() = vp[1];
601     args[0] = obj->getSlot(JSSLOT_SAVED_ID);
602     JSObject *argsobj = NewDenseCopiedArray(cx, argc, vp + 2);
603     if (!argsobj)
604         return JS_FALSE;
605     args[1].setObject(*argsobj);
606     JSBool ok = (flags & JSINVOKE_CONSTRUCT)
607                 ? InvokeConstructor(cx, args)
608                 : Invoke(cx, args, flags);
609     vp[0] = args.rval();
610     return ok;
611 }
612
613 #endif /* JS_HAS_NO_SUCH_METHOD */
614
615 namespace js {
616
617 JS_REQUIRES_STACK bool
618 RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp)
619 {
620     JS_ASSERT(script);
621     JS_ASSERT(fp == cx->fp());
622     JS_ASSERT(fp->script() == script);
623 #ifdef JS_METHODJIT_SPEW
624     JMCheckLogging();
625 #endif
626
627     AutoInterpPreparer prepareInterp(cx, script);
628     bool ok;
629
630     /* FIXME: Once bug 470510 is fixed, make this an assert. */
631     if (script->compileAndGo) {
632         int32 flags = fp->scopeChain().getGlobal()->getReservedSlot(JSRESERVED_GLOBAL_FLAGS).toInt32();
633         if (flags & JSGLOBAL_FLAGS_CLEARED) {
634             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CLEARED_SCOPE);
635             goto error;
636         }
637     }
638
639 #ifdef JS_METHODJIT
640     mjit::CompileStatus status;
641     status = mjit::CanMethodJIT(cx, script, fp, mjit::CompileRequest_Interpreter);
642     if (status == mjit::Compile_Error)
643         goto error;
644
645     if (status == mjit::Compile_Okay) {
646         ok = mjit::JaegerShot(cx);
647         JS_ASSERT_IF(!fp->isYielding() && !(fp->isEvalFrame() && !fp->script()->strictModeCode),
648                      !fp->hasCallObj() && !fp->hasArgsObj());
649         return ok;
650     }
651 #endif
652
653     ok = Interpret(cx, fp);
654     JS_ASSERT_IF(!fp->isYielding() && !(fp->isEvalFrame() && !fp->script()->strictModeCode),
655                  !fp->hasCallObj() && !fp->hasArgsObj());
656     return ok;
657
658   error:
659     PutOwnedActivationObjects(cx, fp);
660     return false;
661 }
662
663 /*
664  * Find a function reference and its 'this' value implicit first parameter
665  * under argc arguments on cx's stack, and call the function.  Push missing
666  * required arguments, allocate declared local variables, and pop everything
667  * when done.  Then push the return value.
668  */
669 JS_REQUIRES_STACK bool
670 Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags)
671 {
672     /* N.B. Must be kept in sync with InvokeSessionGuard::start/invoke */
673
674     CallArgs args = argsRef;
675     JS_ASSERT(args.argc() <= JS_ARGS_LENGTH_MAX);
676
677     if (args.callee().isPrimitive()) {
678         js_ReportIsNotFunction(cx, &args.callee(), flags & JSINVOKE_FUNFLAGS);
679         return false;
680     }
681
682     JSObject &callee = args.callee().toObject();
683     Class *clasp = callee.getClass();
684
685     /* Invoke non-functions. */
686     if (JS_UNLIKELY(clasp != &js_FunctionClass)) {
687 #if JS_HAS_NO_SUCH_METHOD
688         if (JS_UNLIKELY(clasp == &js_NoSuchMethodClass))
689             return NoSuchMethod(cx, args.argc(), args.base(), 0);
690 #endif
691         JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !clasp->construct);
692         if (!clasp->call) {
693             js_ReportIsNotFunction(cx, &args.callee(), flags);
694             return false;
695         }
696         return CallJSNative(cx, clasp->call, args.argc(), args.base());
697     }
698
699     /* Invoke native functions. */
700     JSFunction *fun = callee.getFunctionPrivate();
701     JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !fun->isConstructor());
702     if (fun->isNative())
703         return CallJSNative(cx, fun->u.n.native, args.argc(), args.base());
704
705     /* Handle the empty-script special case. */
706     JSScript *script = fun->script();
707     if (JS_UNLIKELY(script->isEmpty())) {
708         if (flags & JSINVOKE_CONSTRUCT) {
709             JSObject *obj = js_CreateThisForFunction(cx, &callee);
710             if (!obj)
711                 return false;
712             args.rval().setObject(*obj);
713         } else {
714             args.rval().setUndefined();
715         }
716         return true;
717     }
718
719     /* Get pointer to new frame/slots, prepare arguments. */
720     InvokeFrameGuard frame;
721     if (JS_UNLIKELY(!cx->stack().getInvokeFrame(cx, args, fun, script, &flags, &frame)))
722         return false;
723
724     /* Initialize frame, locals. */
725     JSStackFrame *fp = frame.fp();
726     fp->initCallFrame(cx, callee, fun, args.argc(), flags);
727     SetValueRangeToUndefined(fp->slots(), script->nfixed);
728
729     /* Officially push fp. frame's destructor pops. */
730     cx->stack().pushInvokeFrame(cx, args, &frame);
731
732     /* Now that the new frame is rooted, maybe create a call object. */
733     if (fun->isHeavyweight() && !js_GetCallObject(cx, fp))
734         return false;
735
736     /* Run function until JSOP_STOP, JSOP_RETURN or error. */
737     JSBool ok;
738     {
739         AutoPreserveEnumerators preserve(cx);
740         ok = RunScript(cx, script, fp);
741     }
742
743     args.rval() = fp->returnValue();
744     JS_ASSERT_IF(ok && (flags & JSINVOKE_CONSTRUCT), !args.rval().isPrimitive());
745
746     return ok;
747 }
748
749 bool
750 InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &thisv, uintN argc)
751 {
752 #ifdef JS_TRACER
753     if (TRACE_RECORDER(cx))
754         AbortRecording(cx, "attempt to reenter VM while recording");
755     LeaveTrace(cx);
756 #endif
757
758     /* Always push arguments, regardless of optimized/normal invoke. */
759     StackSpace &stack = cx->stack();
760     if (!stack.pushInvokeArgs(cx, argc, &args_))
761         return false;
762
763     /* Callees may clobber 'this' or 'callee'. */
764     savedCallee_ = args_.callee() = calleev;
765     savedThis_ = args_.thisv() = thisv;
766
767     do {
768         /* Hoist dynamic checks from scripted Invoke. */
769         if (!calleev.isObject())
770             break;
771         JSObject &callee = calleev.toObject();
772         if (callee.getClass() != &js_FunctionClass)
773             break;
774         JSFunction *fun = callee.getFunctionPrivate();
775         if (fun->isNative())
776             break;
777         script_ = fun->script();
778         if (fun->isHeavyweight() || script_->isEmpty())
779             break;
780
781         /*
782          * The frame will remain pushed even when the callee isn't active which
783          * will affect the observable current global, so avoid any change.
784          */
785         if (callee.getGlobal() != GetGlobalForScopeChain(cx))
786             break;
787
788         /* Push the stack frame once for the session. */
789         uint32 flags = 0;
790         if (!stack.getInvokeFrame(cx, args_, fun, script_, &flags, &frame_))
791             return false;
792         JSStackFrame *fp = frame_.fp();
793         fp->initCallFrame(cx, calleev.toObject(), fun, argc, flags);
794         stack.pushInvokeFrame(cx, args_, &frame_);
795
796 #ifdef JS_METHODJIT
797         /* Hoist dynamic checks from RunScript. */
798         mjit::CompileStatus status = mjit::CanMethodJIT(cx, script_, fp, mjit::CompileRequest_JIT);
799         if (status == mjit::Compile_Error)
800             return false;
801         if (status != mjit::Compile_Okay)
802             break;
803         /* Cannot also cache the raw code pointer; it can change. */
804
805         /* Hoist dynamic checks from CheckStackAndEnterMethodJIT. */
806         JS_CHECK_RECURSION(cx, return JS_FALSE);
807         stackLimit_ = stack.getStackLimit(cx);
808         if (!stackLimit_)
809             return false;
810
811         stop_ = script_->code + script_->length - 1;
812         JS_ASSERT(*stop_ == JSOP_STOP);
813 #endif
814
815         /* Cached to avoid canonicalActualArg in InvokeSessionGuard::operator[]. */
816         nformals_ = fp->numFormalArgs();
817         formals_ = fp->formalArgs();
818         actuals_ = args_.argv();
819         JS_ASSERT(actuals_ == fp->actualArgs());
820         return true;
821     } while (0);
822
823     /*
824      * Use the normal invoke path.
825      *
826      * The callee slot gets overwritten during an unoptimized Invoke, so we
827      * cache it here and restore it before every Invoke call. The 'this' value
828      * does not get overwritten, so we can fill it here once.
829      */
830     if (frame_.pushed())
831         frame_.pop();
832     formals_ = actuals_ = args_.argv();
833     nformals_ = (unsigned)-1;
834     return true;
835 }
836
837 bool
838 ExternalInvoke(JSContext *cx, const Value &thisv, const Value &fval,
839                uintN argc, Value *argv, Value *rval)
840 {
841     LeaveTrace(cx);
842
843     InvokeArgsGuard args;
844     if (!cx->stack().pushInvokeArgs(cx, argc, &args))
845         return false;
846
847     args.callee() = fval;
848     args.thisv() = thisv;
849     memcpy(args.argv(), argv, argc * sizeof(Value));
850
851     if (args.thisv().isObject()) {
852         /*
853          * We must call the thisObject hook in case we are not called from the
854          * interpreter, where a prior bytecode has computed an appropriate
855          * |this| already.
856          */
857         JSObject *thisp = args.thisv().toObject().thisObject(cx);
858         if (!thisp)
859              return false;
860         args.thisv().setObject(*thisp);
861     }
862
863     if (!Invoke(cx, args, 0))
864         return false;
865
866     *rval = args.rval();
867     return true;
868 }
869
870 bool
871 ExternalInvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *argv,
872                           Value *rval)
873 {
874     LeaveTrace(cx);
875
876     InvokeArgsGuard args;
877     if (!cx->stack().pushInvokeArgs(cx, argc, &args))
878         return false;
879
880     args.callee() = fval;
881     args.thisv().setMagic(JS_THIS_POISON);
882     memcpy(args.argv(), argv, argc * sizeof(Value));
883
884     if (!InvokeConstructor(cx, args))
885         return false;
886
887     *rval = args.rval();
888     return true;
889 }
890
891 bool
892 ExternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, const Value &fval,
893                  JSAccessMode mode, uintN argc, Value *argv, Value *rval)
894 {
895     LeaveTrace(cx);
896
897     /*
898      * ExternalInvoke could result in another try to get or set the same id
899      * again, see bug 355497.
900      */
901     JS_CHECK_RECURSION(cx, return JS_FALSE);
902
903     return ExternalInvoke(cx, ObjectValue(*obj), fval, argc, argv, rval);
904 }
905
906 bool
907 Execute(JSContext *cx, JSObject *chain, JSScript *script,
908         JSStackFrame *prev, uintN flags, Value *result)
909 {
910     JS_ASSERT(chain);
911     JS_ASSERT_IF(prev, !prev->isDummyFrame());
912
913     if (script->isEmpty()) {
914         if (result)
915             result->setUndefined();
916         return true;
917     }
918
919     LeaveTrace(cx);
920
921     /*
922      * Get a pointer to new frame/slots. This memory is not "claimed", so the
923      * code before pushExecuteFrame must not reenter the interpreter.
924      */
925     ExecuteFrameGuard frame;
926     if (!cx->stack().getExecuteFrame(cx, script, &frame))
927         return false;
928
929     /* Initialize fixed slots (GVAR ops expect NULL). */
930     SetValueRangeToNull(frame.fp()->slots(), script->nfixed);
931
932     /* Initialize frame and locals. */
933     JSObject *initialVarObj;
934     if (prev) {
935         JS_ASSERT(chain == &prev->scopeChain());
936         frame.fp()->initEvalFrame(cx, script, prev, flags);
937
938         /*
939          * We want to call |prev->varobj()|, but this requires knowing the
940          * CallStackSegment of |prev|. If |prev == cx->fp()|, the callstack is
941          * simply the context's active callstack, so we can use
942          * |prev->varobj(cx)|.  When |prev != cx->fp()|, we need to do a slow
943          * linear search. Luckily, this only happens with EvaluateInFrame.
944          */
945         initialVarObj = (prev == cx->maybefp())
946                         ? &prev->varobj(cx)
947                         : &prev->varobj(cx->containingSegment(prev));
948     } else {
949         /* The scope chain could be anything, so innerize just in case. */
950         JSObject *innerizedChain = chain;
951         OBJ_TO_INNER_OBJECT(cx, innerizedChain);
952         if (!innerizedChain)
953             return false;
954
955         /* If we were handed a non-native object, complain bitterly. */
956         if (!innerizedChain->isNative()) {
957             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
958                                  JSMSG_NON_NATIVE_SCOPE);
959             return false;
960         }
961
962         /* Initialize frame. */
963         frame.fp()->initGlobalFrame(script, *innerizedChain, flags);
964
965         /* Compute 'this'. */
966         JSObject *thisp = chain->thisObject(cx);
967         if (!thisp)
968             return false;
969         frame.fp()->globalThis().setObject(*thisp);
970
971         initialVarObj = cx->hasRunOption(JSOPTION_VAROBJFIX) ? chain->getGlobal() : chain;
972     }
973
974     /*
975      * Strict mode eval code receives its own, fresh lexical environment; thus
976      * strict mode eval can't mutate its calling frame's binding set.
977      */
978     if ((flags & JSFRAME_EVAL) && script->strictModeCode) {
979         AutoScriptRooter root(cx, script);
980         initialVarObj = NewCallObject(cx, &script->bindings, *initialVarObj, NULL);
981         if (!initialVarObj)
982             return false;
983         initialVarObj->setPrivate(frame.fp());
984
985         /* Clear the Call object propagated from the previous frame, if any. */
986         if (frame.fp()->hasCallObj())
987             frame.fp()->clearCallObj();
988         frame.fp()->setScopeChainAndCallObj(*initialVarObj);
989     }
990     JS_ASSERT(!initialVarObj->getOps()->defineProperty);
991
992 #if JS_HAS_SHARP_VARS
993     JS_STATIC_ASSERT(SHARP_NSLOTS == 2);
994     if (script->hasSharps) {
995         JS_ASSERT(script->nfixed >= SHARP_NSLOTS);
996         Value *sharps = &frame.fp()->slots()[script->nfixed - SHARP_NSLOTS];
997         if (prev && prev->script()->hasSharps) {
998             JS_ASSERT(prev->numFixed() >= SHARP_NSLOTS);
999             int base = (prev->isFunctionFrame() && !prev->isEvalOrDebuggerFrame())
1000                        ? prev->fun()->script()->bindings.sharpSlotBase(cx)
1001                        : prev->numFixed() - SHARP_NSLOTS;
1002             if (base < 0)
1003                 return false;
1004             sharps[0] = prev->slots()[base];
1005             sharps[1] = prev->slots()[base + 1];
1006         } else {
1007             sharps[0].setUndefined();
1008             sharps[1].setUndefined();
1009         }
1010     }
1011 #endif
1012
1013     /* Officially push |fp|. |frame|'s destructor pops. */
1014     cx->stack().pushExecuteFrame(cx, initialVarObj, &frame);
1015
1016     /* Now that the frame has been pushed, we can call the thisObject hook. */
1017     if (!prev) {
1018         JSObject *thisp = chain->thisObject(cx);
1019         if (!thisp)
1020             return false;
1021         frame.fp()->globalThis().setObject(*thisp);
1022     }
1023
1024     Probes::startExecution(cx, script);
1025
1026     /* Run script until JSOP_STOP or error. */
1027     AutoPreserveEnumerators preserve(cx);
1028     JSBool ok = RunScript(cx, script, frame.fp());
1029     if (result)
1030         *result = frame.fp()->returnValue();
1031
1032     Probes::stopExecution(cx, script);
1033
1034     return !!ok;
1035 }
1036
1037 bool
1038 CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs)
1039 {
1040     JSObject *obj2;
1041     JSProperty *prop;
1042     uintN oldAttrs;
1043     bool isFunction;
1044     const char *type, *name;
1045
1046     if (!obj->lookupProperty(cx, id, &obj2, &prop))
1047         return false;
1048     if (!prop)
1049         return true;
1050     if (obj2->isNative()) {
1051         oldAttrs = ((Shape *) prop)->attributes();
1052     } else {
1053         if (!obj2->getAttributes(cx, id, &oldAttrs))
1054             return false;
1055     }
1056
1057     /* We allow redeclaring some non-readonly properties. */
1058     if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) {
1059         /* Allow redeclaration of variables and functions. */
1060         if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
1061             return true;
1062         
1063         /*
1064          * Allow adding a getter only if a property already has a setter
1065          * but no getter and similarly for adding a setter. That is, we
1066          * allow only the following transitions:
1067          *
1068          *   no-property --> getter --> getter + setter
1069          *   no-property --> setter --> getter + setter
1070          */
1071         if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0)
1072             return true;
1073
1074         /*
1075          * Allow redeclaration of an impermanent property (in which case
1076          * anyone could delete it and redefine it, willy-nilly).
1077          */
1078         if (!(oldAttrs & JSPROP_PERMANENT))
1079             return true;
1080     }
1081
1082     isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
1083     if (!isFunction) {
1084         Value value;
1085         if (!obj->getProperty(cx, id, &value))
1086             return JS_FALSE;
1087         isFunction = IsFunctionObject(value);
1088     }
1089
1090     type = (oldAttrs & attrs & JSPROP_GETTER)
1091            ? js_getter_str
1092            : (oldAttrs & attrs & JSPROP_SETTER)
1093            ? js_setter_str
1094            : (oldAttrs & JSPROP_READONLY)
1095            ? js_const_str
1096            : isFunction
1097            ? js_function_str
1098            : js_var_str;
1099     JSAutoByteString bytes;
1100     name = js_ValueToPrintable(cx, IdToValue(id), &bytes);
1101     if (!name)
1102         return false;
1103     JS_ALWAYS_FALSE(JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
1104                                                  JSMSG_REDECLARED_VAR, type, name));
1105     return false;
1106 }
1107
1108 JSBool
1109 HasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
1110 {
1111     Class *clasp = obj->getClass();
1112     if (clasp->hasInstance)
1113         return clasp->hasInstance(cx, obj, v, bp);
1114     js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
1115                         JSDVG_SEARCH_STACK, ObjectValue(*obj), NULL);
1116     return JS_FALSE;
1117 }
1118
1119 bool
1120 StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref, JSBool *equal)
1121 {
1122     Value lval = lref, rval = rref;
1123     if (SameType(lval, rval)) {
1124         if (lval.isString())
1125             return EqualStrings(cx, lval.toString(), rval.toString(), equal);
1126         if (lval.isDouble()) {
1127             *equal = JSDOUBLE_COMPARE(lval.toDouble(), ==, rval.toDouble(), JS_FALSE);
1128             return true;
1129         }
1130         if (lval.isObject()) {
1131             *equal = &lval.toObject() == &rval.toObject();
1132             return true;
1133         }
1134         if (lval.isUndefined()) {
1135             *equal = true;
1136             return true;
1137         }
1138         *equal = lval.payloadAsRawUint32() == rval.payloadAsRawUint32();
1139         return true;
1140     }
1141
1142     if (lval.isDouble() && rval.isInt32()) {
1143         double ld = lval.toDouble();
1144         double rd = rval.toInt32();
1145         *equal = JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1146         return true;
1147     }
1148     if (lval.isInt32() && rval.isDouble()) {
1149         double ld = lval.toInt32();
1150         double rd = rval.toDouble();
1151         *equal = JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1152         return true;
1153     }
1154
1155     *equal = false;
1156     return true;
1157 }
1158
1159 static inline bool
1160 IsNegativeZero(const Value &v)
1161 {
1162     return v.isDouble() && JSDOUBLE_IS_NEGZERO(v.toDouble());
1163 }
1164
1165 static inline bool
1166 IsNaN(const Value &v)
1167 {
1168     return v.isDouble() && JSDOUBLE_IS_NaN(v.toDouble());
1169 }
1170
1171 bool
1172 SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same)
1173 {
1174     if (IsNegativeZero(v1)) {
1175         *same = IsNegativeZero(v2);
1176         return true;
1177     }
1178     if (IsNegativeZero(v2)) {
1179         *same = false;
1180         return true;
1181     }
1182     if (IsNaN(v1) && IsNaN(v2)) {
1183         *same = true;
1184         return true;
1185     }
1186     return StrictlyEqual(cx, v1, v2, same);
1187 }
1188
1189 JSType
1190 TypeOfValue(JSContext *cx, const Value &vref)
1191 {
1192     Value v = vref;
1193     if (v.isNumber())
1194         return JSTYPE_NUMBER;
1195     if (v.isString())
1196         return JSTYPE_STRING;
1197     if (v.isNull())
1198         return JSTYPE_OBJECT;
1199     if (v.isUndefined())
1200         return JSTYPE_VOID;
1201     if (v.isObject())
1202         return v.toObject().typeOf(cx);
1203     JS_ASSERT(v.isBoolean());
1204     return JSTYPE_BOOLEAN;
1205 }
1206
1207 bool
1208 InstanceOfSlow(JSContext *cx, JSObject *obj, Class *clasp, Value *argv)
1209 {
1210     JS_ASSERT(!obj || obj->getClass() != clasp);
1211     if (argv) {
1212         JSFunction *fun = js_ValueToFunction(cx, &argv[-2], 0);
1213         if (fun) {
1214             JSAutoByteString funNameBytes;
1215             if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
1216                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
1217                                      clasp->name, funName,
1218                                      obj ? obj->getClass()->name : js_null_str);
1219             }
1220         }
1221     }
1222     return false;
1223 }
1224
1225 JS_REQUIRES_STACK bool
1226 InvokeConstructor(JSContext *cx, const CallArgs &argsRef)
1227 {
1228     JS_ASSERT(!js_FunctionClass.construct);
1229     CallArgs args = argsRef;
1230
1231     JSObject *callee;
1232     if (args.callee().isPrimitive() || !(callee = &args.callee().toObject())->getParent()) {
1233         js_ReportIsNotFunction(cx, &args.callee(), JSV2F_CONSTRUCT);
1234         return false;
1235     }
1236
1237     /* Handle the fast-constructors cases before falling into the general case . */
1238     Class *clasp = callee->getClass();
1239     JSFunction *fun = NULL;
1240     if (clasp == &js_FunctionClass) {
1241         fun = callee->getFunctionPrivate();
1242         if (fun->isConstructor()) {
1243             args.thisv().setMagicWithObjectOrNullPayload(NULL);
1244             return CallJSNativeConstructor(cx, fun->u.n.native, args.argc(), args.base());
1245         }
1246     } else if (clasp->construct) {
1247         args.thisv().setMagicWithObjectOrNullPayload(NULL);
1248         return CallJSNativeConstructor(cx, clasp->construct, args.argc(), args.base());
1249     }
1250
1251     /* Scripts create their own |this| in JSOP_BEGIN */
1252     if (!fun || !fun->isInterpreted()) {
1253         JSObject *obj = js_CreateThis(cx, callee);
1254         if (!obj)
1255             return false;
1256         args.thisv().setObject(*obj);
1257     }
1258
1259     if (!Invoke(cx, args, JSINVOKE_CONSTRUCT))
1260         return false;
1261
1262     if (args.rval().isPrimitive()) {
1263         if (clasp != &js_FunctionClass) {
1264             /* native [[Construct]] returning primitive is error */
1265             JSAutoByteString bytes;
1266             if (js_ValueToPrintable(cx, args.rval(), &bytes)) {
1267                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1268                                      JSMSG_BAD_NEW_RESULT, bytes.ptr());
1269                 return false;
1270             }
1271         }
1272
1273         /* The interpreter fixes rval for us. */
1274         JS_ASSERT(!fun->isInterpreted());
1275
1276         args.rval() = args.thisv();
1277     }
1278
1279     JS_RUNTIME_METER(cx->runtime, constructs);
1280     return true;
1281 }
1282
1283 bool
1284 InvokeConstructorWithGivenThis(JSContext *cx, JSObject *thisobj, const Value &fval,
1285                                uintN argc, Value *argv, Value *rval)
1286 {
1287     LeaveTrace(cx);
1288
1289     InvokeArgsGuard args;
1290     if (!cx->stack().pushInvokeArgs(cx, argc, &args))
1291         return JS_FALSE;
1292
1293     args.callee() = fval;
1294     /* Initialize args.thisv on all paths below. */
1295     memcpy(args.argv(), argv, argc * sizeof(Value));
1296
1297     /* Handle the fast-constructor cases before calling the general case. */
1298     JSObject &callee = fval.toObject();
1299     Class *clasp = callee.getClass();
1300     JSFunction *fun;
1301     bool ok;
1302     if (clasp == &js_FunctionClass && (fun = callee.getFunctionPrivate())->isConstructor()) {
1303         args.thisv().setMagicWithObjectOrNullPayload(thisobj);
1304         ok = CallJSNativeConstructor(cx, fun->u.n.native, args.argc(), args.base());
1305     } else if (clasp->construct) {
1306         args.thisv().setMagicWithObjectOrNullPayload(thisobj);
1307         ok = CallJSNativeConstructor(cx, clasp->construct, args.argc(), args.base());
1308     } else {
1309         args.thisv().setObjectOrNull(thisobj);
1310         ok = Invoke(cx, args, JSINVOKE_CONSTRUCT);
1311     }
1312
1313     *rval = args.rval();
1314     return ok;
1315 }
1316
1317 bool
1318 DirectEval(JSContext *cx, JSFunction *evalfun, uint32 argc, Value *vp)
1319 {
1320     JS_ASSERT(vp == cx->regs->sp - argc - 2);
1321     JS_ASSERT(vp[0].isObject());
1322     JS_ASSERT(vp[0].toObject().isFunction());
1323     JS_ASSERT(vp[0].toObject().getFunctionPrivate() == evalfun);
1324     JS_ASSERT(IsBuiltinEvalFunction(evalfun));
1325
1326     JSStackFrame *caller = cx->fp();
1327     JS_ASSERT(caller->isScriptFrame());
1328     AutoFunctionCallProbe callProbe(cx, evalfun, caller->script());
1329
1330     JSObject *scopeChain =
1331         GetScopeChainFast(cx, caller, JSOP_EVAL, JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH);
1332     if (!scopeChain || !EvalKernel(cx, argc, vp, DIRECT_EVAL, caller, scopeChain))
1333         return false;
1334     cx->regs->sp = vp + 1;
1335     return true;
1336 }
1337
1338 bool
1339 ValueToId(JSContext *cx, const Value &v, jsid *idp)
1340 {
1341     int32_t i;
1342     if (ValueFitsInInt32(v, &i) && INT_FITS_IN_JSID(i)) {
1343         *idp = INT_TO_JSID(i);
1344         return true;
1345     }
1346
1347 #if JS_HAS_XML_SUPPORT
1348     if (v.isObject()) {
1349         JSObject *obj = &v.toObject();
1350         if (obj->isXMLId()) {
1351             *idp = OBJECT_TO_JSID(obj);
1352             return JS_TRUE;
1353         }
1354     }
1355 #endif
1356
1357     return js_ValueToStringId(cx, v, idp);
1358 }
1359
1360 } /* namespace js */
1361
1362 /*
1363  * Enter the new with scope using an object at sp[-1] and associate the depth
1364  * of the with block with sp + stackIndex.
1365  */
1366 JS_STATIC_INTERPRET JS_REQUIRES_STACK JSBool
1367 js_EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen)
1368 {
1369     JSStackFrame *fp = cx->fp();
1370     Value *sp = cx->regs->sp;
1371     JS_ASSERT(stackIndex < 0);
1372     JS_ASSERT(fp->base() <= sp + stackIndex);
1373
1374     JSObject *obj;
1375     if (sp[-1].isObject()) {
1376         obj = &sp[-1].toObject();
1377     } else {
1378         obj = js_ValueToNonNullObject(cx, sp[-1]);
1379         if (!obj)
1380             return JS_FALSE;
1381         sp[-1].setObject(*obj);
1382     }
1383
1384     JSObject *parent = GetScopeChainFast(cx, fp, op, oplen);
1385     if (!parent)
1386         return JS_FALSE;
1387
1388     OBJ_TO_INNER_OBJECT(cx, obj);
1389     if (!obj)
1390         return JS_FALSE;
1391
1392     JSObject *withobj = js_NewWithObject(cx, obj, parent,
1393                                          sp + stackIndex - fp->base());
1394     if (!withobj)
1395         return JS_FALSE;
1396
1397     fp->setScopeChainNoCallObj(*withobj);
1398     return JS_TRUE;
1399 }
1400
1401 JS_STATIC_INTERPRET JS_REQUIRES_STACK void
1402 js_LeaveWith(JSContext *cx)
1403 {
1404     JSObject *withobj;
1405
1406     withobj = &cx->fp()->scopeChain();
1407     JS_ASSERT(withobj->getClass() == &js_WithClass);
1408     JS_ASSERT(withobj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()));
1409     JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);
1410     withobj->setPrivate(NULL);
1411     cx->fp()->setScopeChainNoCallObj(*withobj->getParent());
1412 }
1413
1414 JS_REQUIRES_STACK Class *
1415 js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth)
1416 {
1417     Class *clasp;
1418
1419     clasp = obj->getClass();
1420     if ((clasp == &js_WithClass || clasp == &js_BlockClass) &&
1421         obj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()) &&
1422         OBJ_BLOCK_DEPTH(cx, obj) >= stackDepth) {
1423         return clasp;
1424     }
1425     return NULL;
1426 }
1427
1428 /*
1429  * Unwind block and scope chains to match the given depth. The function sets
1430  * fp->sp on return to stackDepth.
1431  */
1432 JS_REQUIRES_STACK JSBool
1433 js_UnwindScope(JSContext *cx, jsint stackDepth, JSBool normalUnwind)
1434 {
1435     Class *clasp;
1436
1437     JS_ASSERT(stackDepth >= 0);
1438     JS_ASSERT(cx->fp()->base() + stackDepth <= cx->regs->sp);
1439
1440     JSStackFrame *fp = cx->fp();
1441     for (;;) {
1442         clasp = js_IsActiveWithOrBlock(cx, &fp->scopeChain(), stackDepth);
1443         if (!clasp)
1444             break;
1445         if (clasp == &js_BlockClass) {
1446             /* Don't fail until after we've updated all stacks. */
1447             normalUnwind &= js_PutBlockObject(cx, normalUnwind);
1448         } else {
1449             js_LeaveWith(cx);
1450         }
1451     }
1452
1453     cx->regs->sp = fp->base() + stackDepth;
1454     return normalUnwind;
1455 }
1456
1457 JS_STATIC_INTERPRET JSBool
1458 js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, Value *vp, Value *vp2)
1459 {
1460     if (cs->format & JOF_POST) {
1461         double d;
1462         if (!ValueToNumber(cx, *vp, &d))
1463             return JS_FALSE;
1464         vp->setNumber(d);
1465         (cs->format & JOF_INC) ? ++d : --d;
1466         vp2->setNumber(d);
1467         return JS_TRUE;
1468     }
1469
1470     double d;
1471     if (!ValueToNumber(cx, *vp, &d))
1472         return JS_FALSE;
1473     (cs->format & JOF_INC) ? ++d : --d;
1474     vp->setNumber(d);
1475     *vp2 = *vp;
1476     return JS_TRUE;
1477 }
1478
1479 const Value &
1480 js::GetUpvar(JSContext *cx, uintN closureLevel, UpvarCookie cookie)
1481 {
1482     JS_ASSERT(closureLevel >= cookie.level() && cookie.level() > 0);
1483     const uintN targetLevel = closureLevel - cookie.level();
1484     JS_ASSERT(targetLevel < UpvarCookie::UPVAR_LEVEL_LIMIT);
1485
1486     JSStackFrame *fp = cx->findFrameAtLevel(targetLevel);
1487     uintN slot = cookie.slot();
1488     Value *vp;
1489
1490     if (!fp->isFunctionFrame() || fp->isEvalFrame()) {
1491         vp = fp->slots() + fp->numFixed();
1492     } else if (slot < fp->numFormalArgs()) {
1493         vp = fp->formalArgs();
1494     } else if (slot == UpvarCookie::CALLEE_SLOT) {
1495         vp = &fp->calleeValue();
1496         slot = 0;
1497     } else {
1498         slot -= fp->numFormalArgs();
1499         JS_ASSERT(slot < fp->numSlots());
1500         vp = fp->slots();
1501     }
1502
1503     return vp[slot];
1504 }
1505
1506 #ifdef DEBUG
1507
1508 JS_STATIC_INTERPRET JS_REQUIRES_STACK void
1509 js_LogOpcode(JSContext *cx)
1510 {
1511     FILE *logfp;
1512     JSStackFrame *fp;
1513     JSFrameRegs *regs;
1514     intN ndefs, n, nuses;
1515     JSOp op;
1516
1517     logfp = (FILE *) cx->logfp;
1518     JS_ASSERT(logfp);
1519     fp = cx->fp();
1520     regs = cx->regs;
1521
1522     /*
1523      * Operations in prologues don't produce interesting values, and
1524      * js_DecompileValueGenerator isn't set up to handle them anyway.
1525      */
1526     if (cx->logPrevPc && regs->pc >= fp->script()->main) {
1527         JSOp logPrevOp = JSOp(*cx->logPrevPc);
1528         ndefs = js_GetStackDefs(cx, &js_CodeSpec[logPrevOp], logPrevOp,
1529                                 fp->script(), cx->logPrevPc);
1530
1531         /*
1532          * If there aren't that many elements on the stack, then we have
1533          * probably entered a new frame, and printing output would just be
1534          * misleading.
1535          */
1536         if (ndefs != 0 &&
1537             ndefs < regs->sp - fp->slots()) {
1538             for (n = -ndefs; n < 0; n++) {
1539                 char *bytes = DecompileValueGenerator(cx, n, regs->sp[n], NULL);
1540                 if (bytes) {
1541                     fprintf(logfp, "%s %s",
1542                             (n == -ndefs) ? "  output:" : ",",
1543                             bytes);
1544                     cx->free(bytes);
1545                 } else {
1546                     JS_ClearPendingException(cx);
1547                 }
1548             }
1549             fprintf(logfp, " @ %u\n", (uintN) (regs->sp - fp->base()));
1550         }
1551         fprintf(logfp, "  stack: ");
1552         for (Value *siter = fp->base(); siter < regs->sp; siter++) {
1553             if (siter->isObject() && siter->toObject().getClass() == &js_CallClass) {
1554                 /*
1555                  * Call objects have NULL convert ops so that we catch cases
1556                  * where they escape. So js_ValueToString doesn't work on them.
1557                  */
1558                 fputs("<call>", logfp);
1559             } else {
1560                 JSString *str = js_ValueToString(cx, *siter);
1561                 JSLinearString *linearStr = str ? str->ensureLinear(cx) : NULL;
1562                 if (!linearStr) {
1563                     fputs("<null>", logfp);
1564                     JS_ClearPendingException(cx);
1565                 } else {
1566                     FileEscapedString(logfp, linearStr, 0);
1567                 }
1568             }
1569             fputc(' ', logfp);
1570         }
1571         fputc('\n', logfp);
1572     }
1573
1574     fprintf(logfp, "%4u: ",
1575             js_PCToLineNumber(cx, fp->script(),
1576                               fp->hasImacropc() ? fp->imacropc() : regs->pc));
1577     js_Disassemble1(cx, fp->script(), regs->pc,
1578                     regs->pc - fp->script()->code,
1579                     JS_FALSE, logfp);
1580     op = (JSOp) *regs->pc;
1581     nuses = js_GetStackUses(&js_CodeSpec[op], op, regs->pc);
1582     if (nuses != 0) {
1583         for (n = -nuses; n < 0; n++) {
1584             char *bytes = DecompileValueGenerator(cx, n, regs->sp[n], NULL);
1585             if (bytes) {
1586                 fprintf(logfp, "%s %s",
1587                         (n == -nuses) ? "  inputs:" : ",",
1588                         bytes);
1589                 cx->free(bytes);
1590             } else {
1591                 JS_ClearPendingException(cx);
1592             }
1593         }
1594         fprintf(logfp, " @ %u\n", (uintN) (regs->sp - fp->base()));
1595     }
1596     cx->logPrevPc = regs->pc;
1597
1598     /* It's nice to have complete logs when debugging a crash.  */
1599     fflush(logfp);
1600 }
1601
1602 #endif /* DEBUG */
1603
1604 #ifdef JS_OPMETER
1605
1606 # include <stdlib.h>
1607
1608 # define HIST_NSLOTS            8
1609
1610 /*
1611  * The second dimension is hardcoded at 256 because we know that many bits fit
1612  * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address
1613  * any particular row.
1614  */
1615 static uint32 succeeds[JSOP_LIMIT][256];
1616 static uint32 slot_ops[JSOP_LIMIT][HIST_NSLOTS];
1617
1618 JS_STATIC_INTERPRET void
1619 js_MeterOpcodePair(JSOp op1, JSOp op2)
1620 {
1621     if (op1 != JSOP_STOP)
1622         ++succeeds[op1][op2];
1623 }
1624
1625 JS_STATIC_INTERPRET void
1626 js_MeterSlotOpcode(JSOp op, uint32 slot)
1627 {
1628     if (slot < HIST_NSLOTS)
1629         ++slot_ops[op][slot];
1630 }
1631
1632 typedef struct Edge {
1633     const char  *from;
1634     const char  *to;
1635     uint32      count;
1636 } Edge;
1637
1638 static int
1639 compare_edges(const void *a, const void *b)
1640 {
1641     const Edge *ea = (const Edge *) a;
1642     const Edge *eb = (const Edge *) b;
1643
1644     return (int32)eb->count - (int32)ea->count;
1645 }
1646
1647 void
1648 js_DumpOpMeters()
1649 {
1650     const char *name, *from, *style;
1651     FILE *fp;
1652     uint32 total, count;
1653     uint32 i, j, nedges;
1654     Edge *graph;
1655
1656     name = getenv("JS_OPMETER_FILE");
1657     if (!name)
1658         name = "/tmp/ops.dot";
1659     fp = fopen(name, "w");
1660     if (!fp) {
1661         perror(name);
1662         return;
1663     }
1664
1665     total = nedges = 0;
1666     for (i = 0; i < JSOP_LIMIT; i++) {
1667         for (j = 0; j < JSOP_LIMIT; j++) {
1668             count = succeeds[i][j];
1669             if (count != 0) {
1670                 total += count;
1671                 ++nedges;
1672             }
1673         }
1674     }
1675
1676 # define SIGNIFICANT(count,total) (200. * (count) >= (total))
1677
1678     graph = (Edge *) js_calloc(nedges * sizeof graph[0]);
1679     if (!graph)
1680         return;
1681     for (i = nedges = 0; i < JSOP_LIMIT; i++) {
1682         from = js_CodeName[i];
1683         for (j = 0; j < JSOP_LIMIT; j++) {
1684             count = succeeds[i][j];
1685             if (count != 0 && SIGNIFICANT(count, total)) {
1686                 graph[nedges].from = from;
1687                 graph[nedges].to = js_CodeName[j];
1688                 graph[nedges].count = count;
1689                 ++nedges;
1690             }
1691         }
1692     }
1693     qsort(graph, nedges, sizeof(Edge), compare_edges);
1694
1695 # undef SIGNIFICANT
1696
1697     fputs("digraph {\n", fp);
1698     for (i = 0, style = NULL; i < nedges; i++) {
1699         JS_ASSERT(i == 0 || graph[i-1].count >= graph[i].count);
1700         if (!style || graph[i-1].count != graph[i].count) {
1701             style = (i > nedges * .75) ? "dotted" :
1702                     (i > nedges * .50) ? "dashed" :
1703                     (i > nedges * .25) ? "solid" : "bold";
1704         }
1705         fprintf(fp, "  %s -> %s [label=\"%lu\" style=%s]\n",
1706                 graph[i].from, graph[i].to,
1707                 (unsigned long)graph[i].count, style);
1708     }
1709     js_free(graph);
1710     fputs("}\n", fp);
1711     fclose(fp);
1712
1713     name = getenv("JS_OPMETER_HIST");
1714     if (!name)
1715         name = "/tmp/ops.hist";
1716     fp = fopen(name, "w");
1717     if (!fp) {
1718         perror(name);
1719         return;
1720     }
1721     fputs("bytecode", fp);
1722     for (j = 0; j < HIST_NSLOTS; j++)
1723         fprintf(fp, "  slot %1u", (unsigned)j);
1724     putc('\n', fp);
1725     fputs("========", fp);
1726     for (j = 0; j < HIST_NSLOTS; j++)
1727         fputs(" =======", fp);
1728     putc('\n', fp);
1729     for (i = 0; i < JSOP_LIMIT; i++) {
1730         for (j = 0; j < HIST_NSLOTS; j++) {
1731             if (slot_ops[i][j] != 0) {
1732                 /* Reuse j in the next loop, since we break after. */
1733                 fprintf(fp, "%-8.8s", js_CodeName[i]);
1734                 for (j = 0; j < HIST_NSLOTS; j++)
1735                     fprintf(fp, " %7lu", (unsigned long)slot_ops[i][j]);
1736                 putc('\n', fp);
1737                 break;
1738             }
1739         }
1740     }
1741     fclose(fp);
1742 }
1743
1744 #endif /* JS_OPSMETER */
1745
1746 #endif /* !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___ */
1747
1748 #ifndef  jsinvoke_cpp___
1749
1750 #ifdef JS_REPRMETER
1751 // jsval representation metering: this measures the kinds of jsvals that
1752 // are used as inputs to each JSOp.
1753 namespace reprmeter {
1754     enum Repr {
1755         NONE,
1756         INT,
1757         DOUBLE,
1758         BOOLEAN_PROPER,
1759         BOOLEAN_OTHER,
1760         STRING,
1761         OBJECT_NULL,
1762         OBJECT_PLAIN,
1763         FUNCTION_INTERPRETED,
1764         FUNCTION_FASTNATIVE,
1765         ARRAY_SLOW,
1766         ARRAY_DENSE
1767     };
1768
1769     // Return the |repr| value giving the representation of the given jsval.
1770     static Repr
1771     GetRepr(jsval v)
1772     {
1773         if (JSVAL_IS_INT(v))
1774             return INT;
1775         if (JSVAL_IS_DOUBLE(v))
1776             return DOUBLE;
1777         if (JSVAL_IS_SPECIAL(v)) {
1778             return (v == JSVAL_TRUE || v == JSVAL_FALSE)
1779                    ? BOOLEAN_PROPER
1780                    : BOOLEAN_OTHER;
1781         }
1782         if (JSVAL_IS_STRING(v))
1783             return STRING;
1784
1785         JS_ASSERT(JSVAL_IS_OBJECT(v));
1786
1787         JSObject *obj = JSVAL_TO_OBJECT(v);
1788         if (VALUE_IS_FUNCTION(cx, v)) {
1789             JSFunction *fun = obj->getFunctionPrivate();
1790             if (FUN_INTERPRETED(fun))
1791                 return FUNCTION_INTERPRETED;
1792             return FUNCTION_FASTNATIVE;
1793         }
1794         // This must come before the general array test, because that
1795         // one subsumes this one.
1796         if (!obj)
1797             return OBJECT_NULL;
1798         if (obj->isDenseArray())
1799             return ARRAY_DENSE;
1800         if (obj->isArray())
1801             return ARRAY_SLOW;
1802         return OBJECT_PLAIN;
1803     }
1804
1805     static const char *reprName[] = { "invalid", "int", "double", "bool", "special",
1806                                       "string", "null", "object",
1807                                       "fun:interp", "fun:native"
1808                                       "array:slow", "array:dense" };
1809
1810     // Logically, a tuple of (JSOp, repr_1, ..., repr_n) where repr_i is
1811     // the |repr| of the ith input to the JSOp.
1812     struct OpInput {
1813         enum { max_uses = 16 };
1814
1815         JSOp op;
1816         Repr uses[max_uses];
1817
1818         OpInput() : op(JSOp(255)) {
1819             for (int i = 0; i < max_uses; ++i)
1820                 uses[i] = NONE;
1821         }
1822
1823         OpInput(JSOp op) : op(op) {
1824             for (int i = 0; i < max_uses; ++i)
1825                 uses[i] = NONE;
1826         }
1827
1828         // Hash function
1829         operator uint32() const {
1830             uint32 h = op;
1831             for (int i = 0; i < max_uses; ++i)
1832                 h = h * 7 + uses[i] * 13;
1833             return h;
1834         }
1835
1836         bool operator==(const OpInput &opinput) const {
1837             if (op != opinput.op)
1838                 return false;
1839             for (int i = 0; i < max_uses; ++i) {
1840                 if (uses[i] != opinput.uses[i])
1841                     return false;
1842             }
1843             return true;
1844         }
1845
1846         OpInput &operator=(const OpInput &opinput) {
1847             op = opinput.op;
1848             for (int i = 0; i < max_uses; ++i)
1849                 uses[i] = opinput.uses[i];
1850             return *this;
1851         }
1852     };
1853
1854     typedef HashMap<OpInput, uint64, DefaultHasher<OpInput>, SystemAllocPolicy> OpInputHistogram;
1855
1856     OpInputHistogram opinputs;
1857     bool             opinputsInitialized = false;
1858
1859     // Record an OpInput for the current op. This should be called just
1860     // before executing the op.
1861     static void
1862     MeterRepr(JSContext *cx)
1863     {
1864         // Note that we simply ignore the possibility of errors (OOMs)
1865         // using the hash map, since this is only metering code.
1866
1867         if (!opinputsInitialized) {
1868             opinputs.init();
1869             opinputsInitialized = true;
1870         }
1871
1872         JSOp op = JSOp(*cx->regs->pc);
1873         int nuses = js_GetStackUses(&js_CodeSpec[op], op, cx->regs->pc);
1874
1875         // Build the OpInput.
1876         OpInput opinput(op);
1877         for (int i = 0; i < nuses; ++i) {
1878             jsval v = cx->regs->sp[-nuses+i];
1879             opinput.uses[i] = GetRepr(v);
1880         }
1881
1882         OpInputHistogram::AddPtr p = opinputs.lookupForAdd(opinput);
1883         if (p)
1884             ++p->value;
1885         else
1886             opinputs.add(p, opinput, 1);
1887     }
1888
1889     void
1890     js_DumpReprMeter()
1891     {
1892         FILE *f = fopen("/tmp/reprmeter.txt", "w");
1893         JS_ASSERT(f);
1894         for (OpInputHistogram::Range r = opinputs.all(); !r.empty(); r.popFront()) {
1895             const OpInput &o = r.front().key;
1896             uint64 c = r.front().value;
1897             fprintf(f, "%3d,%s", o.op, js_CodeName[o.op]);
1898             for (int i = 0; i < OpInput::max_uses && o.uses[i] != NONE; ++i)
1899                 fprintf(f, ",%s", reprName[o.uses[i]]);
1900             fprintf(f, ",%llu\n", c);
1901         }
1902         fclose(f);
1903     }
1904 }
1905 #endif /* JS_REPRMETER */
1906
1907 #define PUSH_COPY(v)             do { *regs.sp++ = v; assertSameCompartment(cx, regs.sp[-1]); } while (0)
1908 #define PUSH_NULL()              regs.sp++->setNull()
1909 #define PUSH_UNDEFINED()         regs.sp++->setUndefined()
1910 #define PUSH_BOOLEAN(b)          regs.sp++->setBoolean(b)
1911 #define PUSH_DOUBLE(d)           regs.sp++->setDouble(d)
1912 #define PUSH_INT32(i)            regs.sp++->setInt32(i)
1913 #define PUSH_STRING(s)           do { regs.sp++->setString(s); assertSameCompartment(cx, regs.sp[-1]); } while (0)
1914 #define PUSH_OBJECT(obj)         do { regs.sp++->setObject(obj); assertSameCompartment(cx, regs.sp[-1]); } while (0)
1915 #define PUSH_OBJECT_OR_NULL(obj) do { regs.sp++->setObjectOrNull(obj); assertSameCompartment(cx, regs.sp[-1]); } while (0)
1916 #define PUSH_HOLE()              regs.sp++->setMagic(JS_ARRAY_HOLE)
1917 #define POP_COPY_TO(v)           v = *--regs.sp
1918 #define POP_RETURN_VALUE()       regs.fp->setReturnValue(*--regs.sp)
1919
1920 #define POP_BOOLEAN(cx, vp, b)                                                \
1921     JS_BEGIN_MACRO                                                            \
1922         vp = &regs.sp[-1];                                                    \
1923         if (vp->isNull()) {                                                   \
1924             b = false;                                                        \
1925         } else if (vp->isBoolean()) {                                         \
1926             b = vp->toBoolean();                                              \
1927         } else {                                                              \
1928             b = !!js_ValueToBoolean(*vp);                                     \
1929         }                                                                     \
1930         regs.sp--;                                                            \
1931     JS_END_MACRO
1932
1933 #define VALUE_TO_OBJECT(cx, vp, obj)                                          \
1934     JS_BEGIN_MACRO                                                            \
1935         if ((vp)->isObject()) {                                               \
1936             obj = &(vp)->toObject();                                          \
1937         } else {                                                              \
1938             obj = js_ValueToNonNullObject(cx, *(vp));                         \
1939             if (!obj)                                                         \
1940                 goto error;                                                   \
1941             (vp)->setObject(*obj);                                            \
1942         }                                                                     \
1943     JS_END_MACRO
1944
1945 #define FETCH_OBJECT(cx, n, obj)                                              \
1946     JS_BEGIN_MACRO                                                            \
1947         Value *vp_ = &regs.sp[n];                                             \
1948         VALUE_TO_OBJECT(cx, vp_, obj);                                        \
1949     JS_END_MACRO
1950
1951 #define DEFAULT_VALUE(cx, n, hint, v)                                         \
1952     JS_BEGIN_MACRO                                                            \
1953         JS_ASSERT(v.isObject());                                              \
1954         JS_ASSERT(v == regs.sp[n]);                                           \
1955         if (!DefaultValue(cx, &v.toObject(), hint, &regs.sp[n]))              \
1956             goto error;                                                       \
1957         v = regs.sp[n];                                                       \
1958     JS_END_MACRO
1959
1960 /* Test whether v is an int in the range [-2^31 + 1, 2^31 - 2] */
1961 static JS_ALWAYS_INLINE bool
1962 CanIncDecWithoutOverflow(int32_t i)
1963 {
1964     return (i > JSVAL_INT_MIN) && (i < JSVAL_INT_MAX);
1965 }
1966
1967 /*
1968  * Define JS_OPMETER to instrument bytecode succession, generating a .dot file
1969  * on shutdown that shows the graph of significant predecessor/successor pairs
1970  * executed, where the edge labels give the succession counts.  The .dot file
1971  * is named by the JS_OPMETER_FILE envariable, and defaults to /tmp/ops.dot.
1972  *
1973  * Bonus feature: JS_OPMETER also enables counters for stack-addressing ops
1974  * such as JSOP_GETLOCAL, JSOP_INCARG, via METER_SLOT_OP. The resulting counts
1975  * are written to JS_OPMETER_HIST, defaulting to /tmp/ops.hist.
1976  */
1977 #ifndef JS_OPMETER
1978 # define METER_OP_INIT(op)      /* nothing */
1979 # define METER_OP_PAIR(op1,op2) /* nothing */
1980 # define METER_SLOT_OP(op,slot) /* nothing */
1981 #else
1982
1983 /*
1984  * The second dimension is hardcoded at 256 because we know that many bits fit
1985  * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address
1986  * any particular row.
1987  */
1988 # define METER_OP_INIT(op)      ((op) = JSOP_STOP)
1989 # define METER_OP_PAIR(op1,op2) (js_MeterOpcodePair(op1, op2))
1990 # define METER_SLOT_OP(op,slot) (js_MeterSlotOpcode(op, slot))
1991
1992 #endif
1993
1994 #ifdef JS_REPRMETER
1995 # define METER_REPR(cx)         (reprmeter::MeterRepr(cx))
1996 #else
1997 # define METER_REPR(cx)         ((void) 0)
1998 #endif /* JS_REPRMETER */
1999
2000 /*
2001  * Threaded interpretation via computed goto appears to be well-supported by
2002  * GCC 3 and higher.  IBM's C compiler when run with the right options (e.g.,
2003  * -qlanglvl=extended) also supports threading.  Ditto the SunPro C compiler.
2004  * Currently it's broken for JS_VERSION < 160, though this isn't worth fixing.
2005  * Add your compiler support macros here.
2006  */
2007 #ifndef JS_THREADED_INTERP
2008 # if JS_VERSION >= 160 && (                                                   \
2009     __GNUC__ >= 3 ||                                                          \
2010     (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) ||                       \
2011     __SUNPRO_C >= 0x570)
2012 #  define JS_THREADED_INTERP 1
2013 # else
2014 #  define JS_THREADED_INTERP 0
2015 # endif
2016 #endif
2017
2018 /*
2019  * Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on
2020  * single-thread DEBUG js shell testing to verify property cache hits.
2021  */
2022 #if defined DEBUG && !defined JS_THREADSAFE
2023
2024 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry)                \
2025     JS_BEGIN_MACRO                                                            \
2026         if (!AssertValidPropertyCacheHit(cx, script, regs, pcoff, obj, pobj,  \
2027                                          entry)) {                            \
2028             goto error;                                                       \
2029         }                                                                     \
2030     JS_END_MACRO
2031
2032 static bool
2033 AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
2034                             ptrdiff_t pcoff, JSObject *start, JSObject *found,
2035                             PropertyCacheEntry *entry)
2036 {
2037     uint32 sample = cx->runtime->gcNumber;
2038
2039     JSAtom *atom;
2040     if (pcoff >= 0)
2041         GET_ATOM_FROM_BYTECODE(script, regs.pc, pcoff, atom);
2042     else
2043         atom = cx->runtime->atomState.lengthAtom;
2044
2045     JSObject *obj, *pobj;
2046     JSProperty *prop;
2047     JSBool ok;
2048
2049     if (JOF_OPMODE(*regs.pc) == JOF_NAME) {
2050         ok = js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &pobj, &prop);
2051     } else {
2052         obj = start;
2053         ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop);
2054     }
2055     if (!ok)
2056         return false;
2057     if (cx->runtime->gcNumber != sample || entry->vshape() != pobj->shape())
2058         return true;
2059     JS_ASSERT(prop);
2060     JS_ASSERT(pobj == found);
2061
2062     const Shape *shape = (Shape *) prop;
2063     if (entry->vword.isSlot()) {
2064         JS_ASSERT(entry->vword.toSlot() == shape->slot);
2065         JS_ASSERT(!shape->isMethod());
2066     } else if (entry->vword.isShape()) {
2067         JS_ASSERT(entry->vword.toShape() == shape);
2068         JS_ASSERT_IF(shape->isMethod(),
2069                      &shape->methodObject() == &pobj->nativeGetSlot(shape->slot).toObject());
2070     } else {
2071         Value v;
2072         JS_ASSERT(entry->vword.isFunObj());
2073         JS_ASSERT(!entry->vword.isNull());
2074         JS_ASSERT(pobj->brandedOrHasMethodBarrier());
2075         JS_ASSERT(shape->hasDefaultGetterOrIsMethod());
2076         JS_ASSERT(pobj->containsSlot(shape->slot));
2077         v = pobj->nativeGetSlot(shape->slot);
2078         JS_ASSERT(&entry->vword.toFunObj() == &v.toObject());
2079
2080         if (shape->isMethod()) {
2081             JS_ASSERT(js_CodeSpec[*regs.pc].format & JOF_CALLOP);
2082             JS_ASSERT(&shape->methodObject() == &v.toObject());
2083         }
2084     }
2085
2086     return true;
2087 }
2088
2089 #else
2090 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0)
2091 #endif
2092
2093 /*
2094  * Ensure that the intrepreter switch can close call-bytecode cases in the
2095  * same way as non-call bytecodes.
2096  */
2097 JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
2098 JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_GETFCSLOT_LENGTH);
2099 JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_CALLUPVAR_DBG_LENGTH);
2100 JS_STATIC_ASSERT(JSOP_GETFCSLOT_LENGTH == JSOP_CALLFCSLOT_LENGTH);
2101 JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
2102 JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
2103 JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH);
2104
2105 /*
2106  * Same for debuggable flat closures defined at top level in another function
2107  * or program fragment.
2108  */
2109 JS_STATIC_ASSERT(JSOP_DEFFUN_FC_LENGTH == JSOP_DEFFUN_DBGFC_LENGTH);
2110
2111 /*
2112  * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
2113  * remain distinct for the decompiler. Likewise for JSOP_INIT{PROP,METHOD}.
2114  */
2115 JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
2116 JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETMETHOD_LENGTH);
2117 JS_STATIC_ASSERT(JSOP_INITPROP_LENGTH == JSOP_INITMETHOD_LENGTH);
2118
2119 /* See TRY_BRANCH_AFTER_COND. */
2120 JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH);
2121 JS_STATIC_ASSERT(JSOP_IFNE == JSOP_IFEQ + 1);
2122
2123 /* For the fastest case inder JSOP_INCNAME, etc. */
2124 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_DECNAME_LENGTH);
2125 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEINC_LENGTH);
2126 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH);
2127
2128 #ifdef JS_TRACER
2129 # define ABORT_RECORDING(cx, reason)                                          \
2130     JS_BEGIN_MACRO                                                            \
2131         if (TRACE_RECORDER(cx))                                               \
2132             AbortRecording(cx, reason);                                       \
2133     JS_END_MACRO
2134 #else
2135 # define ABORT_RECORDING(cx, reason)    ((void) 0)
2136 #endif
2137
2138 /*
2139  * Inline fast paths for iteration. js_IteratorMore and js_IteratorNext handle
2140  * all cases, but we inline the most frequently taken paths here.
2141  */
2142 static inline bool
2143 IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, Value *rval)
2144 {
2145     if (iterobj->getClass() == &js_IteratorClass) {
2146         NativeIterator *ni = (NativeIterator *) iterobj->getPrivate();
2147         if (ni->isKeyIter()) {
2148             *cond = (ni->props_cursor < ni->props_end);
2149             return true;
2150         }
2151     }
2152     if (!js_IteratorMore(cx, iterobj, rval))
2153         return false;
2154     *cond = rval->isTrue();
2155     return true;
2156 }
2157
2158 static inline bool
2159 IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
2160 {
2161     if (iterobj->getClass() == &js_IteratorClass) {
2162         NativeIterator *ni = (NativeIterator *) iterobj->getPrivate();
2163         if (ni->isKeyIter()) {
2164             JS_ASSERT(ni->props_cursor < ni->props_end);
2165             jsid id = *ni->current();
2166             if (JSID_IS_ATOM(id)) {
2167                 rval->setString(JSID_TO_STRING(id));
2168                 ni->incCursor();
2169                 return true;
2170             }
2171             /* Take the slow path if we have to stringify a numeric property name. */
2172         }
2173     }
2174     return js_IteratorNext(cx, iterobj, rval);
2175 }
2176
2177 static inline bool
2178 ScriptPrologue(JSContext *cx, JSStackFrame *fp)
2179 {
2180     if (fp->isConstructing()) {
2181         JSObject *obj = js_CreateThisForFunction(cx, &fp->callee());
2182         if (!obj)
2183             return false;
2184         fp->functionThis().setObject(*obj);
2185     }
2186     if (fp->isExecuteFrame()) {
2187         if (JSInterpreterHook hook = cx->debugHooks->executeHook)
2188             fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->executeHookData));
2189     } else {
2190         if (JSInterpreterHook hook = cx->debugHooks->callHook)
2191             fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData));
2192         Probes::enterJSFun(cx, fp->maybeFun(), fp->maybeScript());
2193     }
2194
2195     return true;
2196 }
2197
2198 namespace js {
2199
2200 #ifdef __APPLE__
2201 static JS_NEVER_INLINE bool
2202 NEVER_INLINE_ComputeImplicitThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp)
2203 {
2204     return ComputeImplicitThis(cx, obj, funval, vp);
2205 }
2206 #define ComputeImplicitThis(cx, obj, funval, vp) NEVER_INLINE_ComputeImplicitThis(cx, obj, funval, vp)
2207 #endif
2208
2209 JS_REQUIRES_STACK JS_NEVER_INLINE bool
2210 Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInterpMode interpMode)
2211 {
2212 #ifdef MOZ_TRACEVIS
2213     TraceVisStateObj tvso(cx, S_INTERP);
2214 #endif
2215     JSAutoResolveFlags rf(cx, JSRESOLVE_INFER);
2216
2217 # ifdef DEBUG
2218     /*
2219      * We call this macro from BEGIN_CASE in threaded interpreters,
2220      * and before entering the switch in non-threaded interpreters.
2221      * However, reaching such points doesn't mean we've actually
2222      * fetched an OP from the instruction stream: some opcodes use
2223      * 'op=x; DO_OP()' to let another opcode's implementation finish
2224      * their work, and many opcodes share entry points with a run of
2225      * consecutive BEGIN_CASEs.
2226      *
2227      * Take care to trace OP only when it is the opcode fetched from
2228      * the instruction stream, so the trace matches what one would
2229      * expect from looking at the code.  (We do omit POPs after SETs;
2230      * unfortunate, but not worth fixing.)
2231      */
2232 #  define LOG_OPCODE(OP)    JS_BEGIN_MACRO                                    \
2233                                 if (JS_UNLIKELY(cx->logfp != NULL) &&         \
2234                                     (OP) == *regs.pc)                         \
2235                                     js_LogOpcode(cx);                         \
2236                             JS_END_MACRO
2237 # else
2238 #  define LOG_OPCODE(OP)    ((void) 0)
2239 # endif
2240
2241     /*
2242      * Macros for threaded interpreter loop
2243      */
2244 #if JS_THREADED_INTERP
2245     static void *const normalJumpTable[] = {
2246 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2247         JS_EXTENSION &&L_##op,
2248 # include "jsopcode.tbl"
2249 # undef OPDEF
2250     };
2251
2252     static void *const interruptJumpTable[] = {
2253 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format)              \
2254         JS_EXTENSION &&interrupt,
2255 # include "jsopcode.tbl"
2256 # undef OPDEF
2257     };
2258
2259     register void * const *jumpTable = normalJumpTable;
2260
2261     METER_OP_INIT(op);      /* to nullify first METER_OP_PAIR */
2262
2263 # define ENABLE_INTERRUPTS() ((void) (jumpTable = interruptJumpTable))
2264
2265 # ifdef JS_TRACER
2266 #  define CHECK_RECORDER()                                                    \
2267     JS_ASSERT_IF(TRACE_RECORDER(cx), jumpTable == interruptJumpTable)
2268 # else
2269 #  define CHECK_RECORDER()  ((void)0)
2270 # endif
2271
2272 # define DO_OP()            JS_BEGIN_MACRO                                    \
2273                                 CHECK_RECORDER();                             \
2274                                 JS_EXTENSION_(goto *jumpTable[op]);           \
2275                             JS_END_MACRO
2276 # define DO_NEXT_OP(n)      JS_BEGIN_MACRO                                    \
2277                                 METER_OP_PAIR(op, JSOp(regs.pc[n]));          \
2278                                 op = (JSOp) *(regs.pc += (n));                \
2279                                 METER_REPR(cx);                               \
2280                                 DO_OP();                                      \
2281                             JS_END_MACRO
2282
2283 # define BEGIN_CASE(OP)     L_##OP: LOG_OPCODE(OP); CHECK_RECORDER();
2284 # define END_CASE(OP)       DO_NEXT_OP(OP##_LENGTH);
2285 # define END_VARLEN_CASE    DO_NEXT_OP(len);
2286 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)                                    \
2287                                 JS_ASSERT(js_CodeSpec[OP].length == 1);       \
2288                                 op = (JSOp) *++regs.pc;                       \
2289                                 DO_OP();
2290
2291 # define END_EMPTY_CASES
2292
2293 #else /* !JS_THREADED_INTERP */
2294
2295     register intN switchMask = 0;
2296     intN switchOp;
2297
2298 # define ENABLE_INTERRUPTS() ((void) (switchMask = -1))
2299
2300 # ifdef JS_TRACER
2301 #  define CHECK_RECORDER()                                                    \
2302     JS_ASSERT_IF(TRACE_RECORDER(cx), switchMask == -1)
2303 # else
2304 #  define CHECK_RECORDER()  ((void)0)
2305 # endif
2306
2307 # define DO_OP()            goto do_op
2308 # define DO_NEXT_OP(n)      JS_BEGIN_MACRO                                    \
2309                                 JS_ASSERT((n) == len);                        \
2310                                 goto advance_pc;                              \
2311                             JS_END_MACRO
2312
2313 # define BEGIN_CASE(OP)     case OP: CHECK_RECORDER();
2314 # define END_CASE(OP)       END_CASE_LEN(OP##_LENGTH)
2315 # define END_CASE_LEN(n)    END_CASE_LENX(n)
2316 # define END_CASE_LENX(n)   END_CASE_LEN##n
2317
2318 /*
2319  * To share the code for all len == 1 cases we use the specialized label with
2320  * code that falls through to advance_pc: .
2321  */
2322 # define END_CASE_LEN1      goto advance_pc_by_one;
2323 # define END_CASE_LEN2      len = 2; goto advance_pc;
2324 # define END_CASE_LEN3      len = 3; goto advance_pc;
2325 # define END_CASE_LEN4      len = 4; goto advance_pc;
2326 # define END_CASE_LEN5      len = 5; goto advance_pc;
2327 # define END_VARLEN_CASE    goto advance_pc;
2328 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)
2329 # define END_EMPTY_CASES    goto advance_pc_by_one;
2330
2331 #endif /* !JS_THREADED_INTERP */
2332
2333 #define LOAD_ATOM(PCOFF, atom)                                                \
2334     JS_BEGIN_MACRO                                                            \
2335         JS_ASSERT(regs.fp->hasImacropc()                                      \
2336                   ? atoms == COMMON_ATOMS_START(&rt->atomState) &&            \
2337                     GET_INDEX(regs.pc + PCOFF) < js_common_atom_count         \
2338                   : (size_t)(atoms - script->atomMap.vector) <                \
2339                     (size_t)(script->atomMap.length -                         \
2340                              GET_INDEX(regs.pc + PCOFF)));                    \
2341         atom = atoms[GET_INDEX(regs.pc + PCOFF)];                             \
2342     JS_END_MACRO
2343
2344 #define GET_FULL_INDEX(PCOFF)                                                 \
2345     (atoms - script->atomMap.vector + GET_INDEX(regs.pc + (PCOFF)))
2346
2347 #define LOAD_OBJECT(PCOFF, obj)                                               \
2348     (obj = script->getObject(GET_FULL_INDEX(PCOFF)))
2349
2350 #define LOAD_FUNCTION(PCOFF)                                                  \
2351     (fun = script->getFunction(GET_FULL_INDEX(PCOFF)))
2352
2353 #define LOAD_DOUBLE(PCOFF, dbl)                                               \
2354     (dbl = script->getConst(GET_FULL_INDEX(PCOFF)).toDouble())
2355
2356     bool useMethodJIT = false;
2357     
2358 #ifdef JS_METHODJIT
2359
2360 #define RESET_USE_METHODJIT()                                                 \
2361     JS_BEGIN_MACRO                                                            \
2362         useMethodJIT = cx->methodJitEnabled &&                                \
2363             interpMode == JSINTERP_NORMAL &&                                  \
2364             script->getJITStatus(regs.fp->isConstructing()) != JITScript_Invalid; \
2365     JS_END_MACRO
2366
2367 #define MONITOR_BRANCH_METHODJIT()                                            \
2368     JS_BEGIN_MACRO                                                            \
2369         mjit::CompileStatus status =                                          \
2370             mjit::CanMethodJITAtBranch(cx, script, regs.fp, regs.pc);         \
2371         if (status == mjit::Compile_Error)                                    \
2372             goto error;                                                       \
2373         if (status == mjit::Compile_Okay) {                                   \
2374             void *ncode =                                                     \
2375                 script->nativeCodeForPC(regs.fp->isConstructing(), regs.pc);  \
2376             interpReturnOK = mjit::JaegerShotAtSafePoint(cx, ncode);          \
2377             if (inlineCallCount)                                              \
2378                 goto jit_return;                                              \
2379             regs.fp->setFinishedInInterpreter();                              \
2380             goto leave_on_safe_point;                                         \
2381         }                                                                     \
2382         if (status == mjit::Compile_Abort) {                                  \
2383             useMethodJIT = false;                                             \
2384         }                                                                     \
2385     JS_END_MACRO
2386
2387 #else
2388
2389 #define RESET_USE_METHODJIT() ((void) 0)
2390
2391 #define MONITOR_BRANCH_METHODJIT() ((void) 0)
2392
2393 #endif
2394
2395 #ifdef JS_TRACER
2396
2397 #ifdef MOZ_TRACEVIS
2398 #if JS_THREADED_INTERP
2399 #define MONITOR_BRANCH_TRACEVIS                                               \
2400     JS_BEGIN_MACRO                                                            \
2401         if (jumpTable != interruptJumpTable)                                  \
2402             EnterTraceVisState(cx, S_RECORD, R_NONE);                         \
2403     JS_END_MACRO
2404 #else /* !JS_THREADED_INTERP */
2405 #define MONITOR_BRANCH_TRACEVIS                                               \
2406     JS_BEGIN_MACRO                                                            \
2407         EnterTraceVisState(cx, S_RECORD, R_NONE);                             \
2408     JS_END_MACRO
2409 #endif
2410 #else
2411 #define MONITOR_BRANCH_TRACEVIS
2412 #endif
2413
2414 #define RESTORE_INTERP_VARS()                                                 \
2415     JS_BEGIN_MACRO                                                            \
2416         script = regs.fp->script();                                           \
2417         argv = regs.fp->maybeFormalArgs();                                    \
2418         atoms = FrameAtomBase(cx, regs.fp);                                   \
2419         JS_ASSERT(cx->regs == &regs);                                         \
2420         if (cx->isExceptionPending())                                         \
2421             goto error;                                                       \
2422     JS_END_MACRO
2423
2424 #define MONITOR_BRANCH()                                                      \
2425     JS_BEGIN_MACRO                                                            \
2426         if (TRACING_ENABLED(cx)) {                                            \
2427             if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && useMethodJIT) { \
2428                 MONITOR_BRANCH_METHODJIT();                                   \
2429             } else {                                                          \
2430                 MonitorResult r = MonitorLoopEdge(cx, inlineCallCount, interpMode); \
2431                 if (r == MONITOR_RECORDING) {                                 \
2432                     JS_ASSERT(TRACE_RECORDER(cx));                            \
2433                     JS_ASSERT(!TRACE_PROFILER(cx));                           \
2434                     MONITOR_BRANCH_TRACEVIS;                                  \
2435                     ENABLE_INTERRUPTS();                                      \
2436                     CLEAR_LEAVE_ON_TRACE_POINT();                             \
2437                 }                                                             \
2438                 RESTORE_INTERP_VARS();                                        \
2439                 JS_ASSERT_IF(cx->isExceptionPending(), r == MONITOR_ERROR);   \
2440                 if (r == MONITOR_ERROR)                                       \
2441                     goto error;                                               \
2442             }                                                                 \
2443         } else {                                                              \
2444             MONITOR_BRANCH_METHODJIT();                                       \
2445         }                                                                     \
2446     JS_END_MACRO
2447
2448 #else /* !JS_TRACER */
2449
2450 #define MONITOR_BRANCH()                                                      \
2451     JS_BEGIN_MACRO                                                            \
2452         MONITOR_BRANCH_METHODJIT();                                           \
2453     JS_END_MACRO
2454
2455 #endif /* !JS_TRACER */
2456
2457     /*
2458      * Prepare to call a user-supplied branch handler, and abort the script
2459      * if it returns false.
2460      */
2461 #define CHECK_BRANCH()                                                        \
2462     JS_BEGIN_MACRO                                                            \
2463         if (JS_THREAD_DATA(cx)->interruptFlags && !js_HandleExecutionInterrupt(cx)) \
2464             goto error;                                                       \
2465     JS_END_MACRO
2466
2467 #if defined(JS_TRACER) && defined(JS_METHODJIT)
2468 # define LEAVE_ON_SAFE_POINT()                                                \
2469     do {                                                                      \
2470         JS_ASSERT_IF(leaveOnSafePoint, !TRACE_RECORDER(cx));                  \
2471         JS_ASSERT_IF(leaveOnSafePoint, !TRACE_PROFILER(cx));                  \
2472         JS_ASSERT_IF(leaveOnSafePoint, interpMode != JSINTERP_NORMAL);        \
2473         if (leaveOnSafePoint && !regs.fp->hasImacropc() &&                    \
2474             script->maybeNativeCodeForPC(regs.fp->isConstructing(), regs.pc)) { \
2475             JS_ASSERT(!TRACE_RECORDER(cx));                                   \
2476             interpReturnOK = true;                                            \
2477             goto leave_on_safe_point;                                         \
2478         }                                                                     \
2479     } while (0)
2480 #else
2481 # define LEAVE_ON_SAFE_POINT() /* nop */
2482 #endif
2483
2484 #define BRANCH(n)                                                             \
2485     JS_BEGIN_MACRO                                                            \
2486         regs.pc += (n);                                                       \
2487         op = (JSOp) *regs.pc;                                                 \
2488         if ((n) <= 0) {                                                       \
2489             CHECK_BRANCH();                                                   \
2490             if (op == JSOP_NOTRACE) {                                         \
2491                 if (TRACE_RECORDER(cx) || TRACE_PROFILER(cx)) {               \
2492                     MONITOR_BRANCH();                                         \
2493                     op = (JSOp) *regs.pc;                                     \
2494                 }                                                             \
2495             } else if (op == JSOP_TRACE) {                                    \
2496                 MONITOR_BRANCH();                                             \
2497                 op = (JSOp) *regs.pc;                                         \
2498             }                                                                 \
2499         }                                                                     \
2500         LEAVE_ON_SAFE_POINT();                                                \
2501         DO_OP();                                                              \
2502     JS_END_MACRO
2503
2504 #define CHECK_INTERRUPT_HANDLER()                                             \
2505     JS_BEGIN_MACRO                                                            \
2506         if (cx->debugHooks->interruptHook)                                    \
2507             ENABLE_INTERRUPTS();                                              \
2508     JS_END_MACRO
2509
2510     JSFrameRegs regs = *cx->regs;
2511
2512     /* Repoint cx->regs to a local variable for faster access. */
2513     struct InterpExitGuard {
2514         JSContext *cx;
2515         const JSFrameRegs &regs;
2516         JSFrameRegs *prevContextRegs;
2517         InterpExitGuard(JSContext *cx, JSFrameRegs &regs)
2518           : cx(cx), regs(regs), prevContextRegs(cx->regs) {
2519             cx->setCurrentRegs(&regs);
2520             ++cx->interpLevel;
2521         }
2522         ~InterpExitGuard() {
2523             --cx->interpLevel;
2524             JS_ASSERT(cx->regs == &regs);
2525             *prevContextRegs = regs;
2526             cx->setCurrentRegs(prevContextRegs);
2527         }
2528     } interpGuard(cx, regs);
2529
2530     /* Copy in hot values that change infrequently. */
2531     JSRuntime *const rt = cx->runtime;
2532     JSScript *script = regs.fp->script();
2533     Value *argv = regs.fp->maybeFormalArgs();
2534     CHECK_INTERRUPT_HANDLER();
2535
2536 #if defined(JS_TRACER) && defined(JS_METHODJIT)
2537     bool leaveOnSafePoint = (interpMode == JSINTERP_SAFEPOINT);
2538 # define CLEAR_LEAVE_ON_TRACE_POINT() ((void) (leaveOnSafePoint = false))
2539 #else
2540 # define CLEAR_LEAVE_ON_TRACE_POINT() ((void) 0)
2541 #endif
2542
2543     if (!entryFrame)
2544         entryFrame = regs.fp;
2545
2546     /*
2547      * Initialize the index segment register used by LOAD_ATOM and
2548      * GET_FULL_INDEX macros below. As a register we use a pointer based on
2549      * the atom map to turn frequently executed LOAD_ATOM into simple array
2550      * access. For less frequent object and regexp loads we have to recover
2551      * the segment from atoms pointer first.
2552      */
2553     JSAtom **atoms = script->atomMap.vector;
2554
2555 #if JS_HAS_GENERATORS
2556     if (JS_UNLIKELY(regs.fp->isGeneratorFrame())) {
2557         JS_ASSERT(interpGuard.prevContextRegs == &cx->generatorFor(regs.fp)->regs);
2558         JS_ASSERT((size_t) (regs.pc - script->code) <= script->length);
2559         JS_ASSERT((size_t) (regs.sp - regs.fp->base()) <= StackDepth(script));
2560
2561         /*
2562          * To support generator_throw and to catch ignored exceptions,
2563          * fail if cx->isExceptionPending() is true.
2564          */
2565         if (cx->isExceptionPending())
2566             goto error;
2567     }
2568 #endif
2569
2570 #ifdef JS_TRACER
2571     /*
2572      * The method JIT may have already initiated a recording, in which case
2573      * there should already be a valid recorder. Otherwise...
2574      * we cannot reenter the interpreter while recording.
2575      */
2576     if (interpMode == JSINTERP_RECORD) {
2577         JS_ASSERT(TRACE_RECORDER(cx));
2578         JS_ASSERT(!TRACE_PROFILER(cx));
2579         ENABLE_INTERRUPTS();
2580     } else if (interpMode == JSINTERP_PROFILE) {
2581         ENABLE_INTERRUPTS();
2582     } else if (TRACE_RECORDER(cx)) {
2583         AbortRecording(cx, "attempt to reenter interpreter while recording");
2584     }
2585
2586     if (regs.fp->hasImacropc())
2587         atoms = COMMON_ATOMS_START(&rt->atomState);
2588 #endif
2589
2590     /* Don't call the script prologue if executing between Method and Trace JIT. */
2591     if (interpMode == JSINTERP_NORMAL) {
2592         JS_ASSERT_IF(!regs.fp->isGeneratorFrame(), regs.pc == script->code);
2593         if (!ScriptPrologue(cx, regs.fp))
2594             goto error;
2595     }
2596
2597     CHECK_INTERRUPT_HANDLER();
2598
2599     RESET_USE_METHODJIT();
2600
2601     /* State communicated between non-local jumps: */
2602     JSBool interpReturnOK;
2603     JSAtom *atomNotDefined;
2604
2605     /*
2606      * It is important that "op" be initialized before calling DO_OP because
2607      * it is possible for "op" to be specially assigned during the normal
2608      * processing of an opcode while looping. We rely on DO_NEXT_OP to manage
2609      * "op" correctly in all other cases.
2610      */
2611     JSOp op;
2612     jsint len;
2613     len = 0;
2614
2615     /* Check for too deep of a native thread stack. */
2616 #ifdef JS_TRACER
2617 #ifdef JS_METHODJIT
2618     JS_CHECK_RECURSION(cx, do {
2619             if (TRACE_RECORDER(cx))
2620                 AbortRecording(cx, "too much recursion");
2621             if (TRACE_PROFILER(cx))
2622                 AbortProfiling(cx);
2623             goto error;
2624         } while (0););
2625 #else
2626     JS_CHECK_RECURSION(cx, do {
2627             if (TRACE_RECORDER(cx))
2628                 AbortRecording(cx, "too much recursion");
2629             goto error;
2630         } while (0););
2631 #endif
2632 #else
2633     JS_CHECK_RECURSION(cx, return JS_FALSE);
2634 #endif
2635
2636 #if JS_THREADED_INTERP
2637     DO_NEXT_OP(len);
2638 #else
2639     DO_NEXT_OP(len);
2640 #endif
2641
2642 #if JS_THREADED_INTERP
2643     /*
2644      * This is a loop, but it does not look like a loop. The loop-closing
2645      * jump is distributed throughout goto *jumpTable[op] inside of DO_OP.
2646      * When interrupts are enabled, jumpTable is set to interruptJumpTable
2647      * where all jumps point to the interrupt label. The latter, after
2648      * calling the interrupt handler, dispatches through normalJumpTable to
2649      * continue the normal bytecode processing.
2650      */
2651
2652 #else /* !JS_THREADED_INTERP */
2653     for (;;) {
2654       advance_pc_by_one:
2655         JS_ASSERT(js_CodeSpec[op].length == 1);
2656         len = 1;
2657       advance_pc:
2658         regs.pc += len;
2659         op = (JSOp) *regs.pc;
2660
2661       do_op:
2662         CHECK_RECORDER();
2663         LOG_OPCODE(op);
2664         switchOp = intN(op) | switchMask;
2665       do_switch:
2666         switch (switchOp) {
2667 #endif
2668
2669 #if JS_THREADED_INTERP
2670   interrupt:
2671 #else /* !JS_THREADED_INTERP */
2672   case -1:
2673     JS_ASSERT(switchMask == -1);
2674 #endif /* !JS_THREADED_INTERP */
2675     {
2676         bool moreInterrupts = false;
2677         JSInterruptHook hook = cx->debugHooks->interruptHook;
2678         if (hook) {
2679 #ifdef JS_TRACER
2680             if (TRACE_RECORDER(cx))
2681                 AbortRecording(cx, "interrupt hook");
2682 #ifdef JS_METHODJIT
2683             if (TRACE_PROFILER(cx))
2684                 AbortProfiling(cx);
2685 #endif
2686 #endif
2687             Value rval;
2688             switch (hook(cx, script, regs.pc, Jsvalify(&rval),
2689                          cx->debugHooks->interruptHookData)) {
2690               case JSTRAP_ERROR:
2691                 goto error;
2692               case JSTRAP_CONTINUE:
2693                 break;
2694               case JSTRAP_RETURN:
2695                 regs.fp->setReturnValue(rval);
2696                 interpReturnOK = JS_TRUE;
2697                 goto forced_return;
2698               case JSTRAP_THROW:
2699                 cx->setPendingException(rval);
2700                 goto error;
2701               default:;
2702             }
2703             moreInterrupts = true;
2704         }
2705
2706 #ifdef JS_TRACER
2707 #ifdef JS_METHODJIT
2708         if (TRACE_PROFILER(cx) && interpMode == JSINTERP_PROFILE) {
2709             LoopProfile *prof = TRACE_PROFILER(cx);
2710             JS_ASSERT(!TRACE_RECORDER(cx));
2711             LoopProfile::ProfileAction act = prof->profileOperation(cx, op);
2712             switch (act) {
2713                 case LoopProfile::ProfComplete:
2714                     if (interpMode != JSINTERP_NORMAL) {
2715                         leaveOnSafePoint = true;
2716                         LEAVE_ON_SAFE_POINT();
2717                     }
2718                     break;
2719                 default:
2720                     moreInterrupts = true;
2721                     break;
2722             }
2723         }
2724 #endif
2725         if (TraceRecorder* tr = TRACE_RECORDER(cx)) {
2726             JS_ASSERT(!TRACE_PROFILER(cx));
2727             AbortableRecordingStatus status = tr->monitorRecording(op);
2728             JS_ASSERT_IF(cx->isExceptionPending(), status == ARECORD_ERROR);
2729
2730             if (interpMode != JSINTERP_NORMAL) {
2731                 JS_ASSERT(interpMode == JSINTERP_RECORD || JSINTERP_SAFEPOINT);
2732                 switch (status) {
2733                   case ARECORD_IMACRO_ABORTED:
2734                   case ARECORD_ABORTED:
2735                   case ARECORD_COMPLETED:
2736                   case ARECORD_STOP:
2737 #ifdef JS_METHODJIT
2738                     leaveOnSafePoint = true;
2739                     LEAVE_ON_SAFE_POINT();
2740 #endif
2741                     break;
2742                   default:
2743                     break;
2744                 }
2745             }
2746
2747             switch (status) {
2748               case ARECORD_CONTINUE:
2749                 moreInterrupts = true;
2750                 break;
2751               case ARECORD_IMACRO:
2752               case ARECORD_IMACRO_ABORTED:
2753                 atoms = COMMON_ATOMS_START(&rt->atomState);
2754                 op = JSOp(*regs.pc);
2755                 CLEAR_LEAVE_ON_TRACE_POINT();
2756                 if (status == ARECORD_IMACRO)
2757                     DO_OP();    /* keep interrupting for op. */
2758                 break;
2759               case ARECORD_ERROR:
2760                 // The code at 'error:' aborts the recording.
2761                 goto error;
2762               case ARECORD_ABORTED:
2763               case ARECORD_COMPLETED:
2764                 break;
2765               case ARECORD_STOP:
2766                 /* A 'stop' error should have already aborted recording. */
2767               default:
2768                 JS_NOT_REACHED("Bad recording status");
2769             }
2770         }
2771 #endif /* !JS_TRACER */
2772
2773 #if JS_THREADED_INTERP
2774 #ifdef MOZ_TRACEVIS
2775         if (!moreInterrupts)
2776             ExitTraceVisState(cx, R_ABORT);
2777 #endif
2778         jumpTable = moreInterrupts ? interruptJumpTable : normalJumpTable;
2779         JS_EXTENSION_(goto *normalJumpTable[op]);
2780 #else
2781         switchMask = moreInterrupts ? -1 : 0;
2782         switchOp = intN(op);
2783         goto do_switch;
2784 #endif
2785     }
2786
2787 /* No-ops for ease of decompilation. */
2788 ADD_EMPTY_CASE(JSOP_NOP)
2789 ADD_EMPTY_CASE(JSOP_CONDSWITCH)
2790 ADD_EMPTY_CASE(JSOP_TRY)
2791 #if JS_HAS_XML_SUPPORT
2792 ADD_EMPTY_CASE(JSOP_STARTXML)
2793 ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
2794 #endif
2795 ADD_EMPTY_CASE(JSOP_NULLBLOCKCHAIN)
2796 END_EMPTY_CASES
2797
2798 BEGIN_CASE(JSOP_TRACE)
2799 BEGIN_CASE(JSOP_NOTRACE)
2800     LEAVE_ON_SAFE_POINT();
2801 END_CASE(JSOP_TRACE)
2802
2803 /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
2804 BEGIN_CASE(JSOP_LINENO)
2805 END_CASE(JSOP_LINENO)
2806
2807 BEGIN_CASE(JSOP_BLOCKCHAIN)
2808 END_CASE(JSOP_BLOCKCHAIN)
2809
2810 BEGIN_CASE(JSOP_PUSH)
2811     PUSH_UNDEFINED();
2812 END_CASE(JSOP_PUSH)
2813
2814 BEGIN_CASE(JSOP_POP)
2815     regs.sp--;
2816 END_CASE(JSOP_POP)
2817
2818 BEGIN_CASE(JSOP_POPN)
2819 {
2820     regs.sp -= GET_UINT16(regs.pc);
2821 #ifdef DEBUG
2822     JS_ASSERT(regs.fp->base() <= regs.sp);
2823     JSObject *obj = GetBlockChain(cx, regs.fp);
2824     JS_ASSERT_IF(obj,
2825                  OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
2826                  <= (size_t) (regs.sp - regs.fp->base()));
2827     for (obj = &regs.fp->scopeChain(); obj; obj = obj->getParent()) {
2828         Class *clasp = obj->getClass();
2829         if (clasp != &js_BlockClass && clasp != &js_WithClass)
2830             continue;
2831         if (obj->getPrivate() != js_FloatingFrameIfGenerator(cx, regs.fp))
2832             break;
2833         JS_ASSERT(regs.fp->base() + OBJ_BLOCK_DEPTH(cx, obj)
2834                              + ((clasp == &js_BlockClass)
2835                                 ? OBJ_BLOCK_COUNT(cx, obj)
2836                                 : 1)
2837                   <= regs.sp);
2838     }
2839 #endif
2840 }
2841 END_CASE(JSOP_POPN)
2842
2843 BEGIN_CASE(JSOP_SETRVAL)
2844 BEGIN_CASE(JSOP_POPV)
2845     POP_RETURN_VALUE();
2846 END_CASE(JSOP_POPV)
2847
2848 BEGIN_CASE(JSOP_ENTERWITH)
2849     if (!js_EnterWith(cx, -1, JSOP_ENTERWITH, JSOP_ENTERWITH_LENGTH))
2850         goto error;
2851
2852     /*
2853      * We must ensure that different "with" blocks have different stack depth
2854      * associated with them. This allows the try handler search to properly
2855      * recover the scope chain. Thus we must keep the stack at least at the
2856      * current level.
2857      *
2858      * We set sp[-1] to the current "with" object to help asserting the
2859      * enter/leave balance in [leavewith].
2860      */
2861     regs.sp[-1].setObject(regs.fp->scopeChain());
2862 END_CASE(JSOP_ENTERWITH)
2863
2864 BEGIN_CASE(JSOP_LEAVEWITH)
2865     JS_ASSERT(&regs.sp[-1].toObject() == &regs.fp->scopeChain());
2866     regs.sp--;
2867     js_LeaveWith(cx);
2868 END_CASE(JSOP_LEAVEWITH)
2869
2870 BEGIN_CASE(JSOP_RETURN)
2871     POP_RETURN_VALUE();
2872     /* FALL THROUGH */
2873
2874 BEGIN_CASE(JSOP_RETRVAL)    /* fp return value already set */
2875 BEGIN_CASE(JSOP_STOP)
2876 {
2877     /*
2878      * When the inlined frame exits with an exception or an error, ok will be
2879      * false after the inline_return label.
2880      */
2881     CHECK_BRANCH();
2882
2883 #ifdef JS_TRACER
2884     if (regs.fp->hasImacropc()) {
2885         /*
2886          * If we are at the end of an imacro, return to its caller in the
2887          * current frame.
2888          */
2889         JS_ASSERT(op == JSOP_STOP);
2890         JS_ASSERT((uintN)(regs.sp - regs.fp->slots()) <= script->nslots);
2891         jsbytecode *imacpc = regs.fp->imacropc();
2892         regs.pc = imacpc + js_CodeSpec[*imacpc].length;
2893         regs.fp->clearImacropc();
2894         LEAVE_ON_SAFE_POINT();
2895         atoms = script->atomMap.vector;
2896         op = JSOp(*regs.pc);
2897         DO_OP();
2898     }
2899 #endif
2900
2901     interpReturnOK = true;
2902     if (entryFrame != regs.fp)
2903   inline_return:
2904     {
2905         JS_ASSERT(!js_IsActiveWithOrBlock(cx, &regs.fp->scopeChain(), 0));
2906         interpReturnOK = ScriptEpilogue(cx, regs.fp, interpReturnOK);
2907         CHECK_INTERRUPT_HANDLER();
2908
2909         /* The JIT inlines ScriptEpilogue. */
2910   jit_return:
2911         Value *newsp = regs.fp->actualArgs() - 1;
2912         newsp[-1] = regs.fp->returnValue();
2913         cx->stack().popInlineFrame(cx, regs.fp->prev(), newsp);
2914
2915         /* Sync interpreter registers. */
2916         script = regs.fp->script();
2917         argv = regs.fp->maybeFormalArgs();
2918         atoms = FrameAtomBase(cx, regs.fp);
2919
2920         /* Resume execution in the calling frame. */
2921         RESET_USE_METHODJIT();
2922         JS_ASSERT(inlineCallCount);
2923         inlineCallCount--;
2924         if (JS_LIKELY(interpReturnOK)) {
2925             JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, script, regs.pc)].length
2926                       == JSOP_CALL_LENGTH);
2927             TRACE_0(LeaveFrame);
2928             len = JSOP_CALL_LENGTH;
2929             DO_NEXT_OP(len);
2930         }
2931         goto error;
2932     } else {
2933         JS_ASSERT(regs.sp == regs.fp->base());
2934     }
2935     interpReturnOK = true;
2936     goto exit;
2937 }
2938
2939 BEGIN_CASE(JSOP_DEFAULT)
2940     regs.sp--;
2941     /* FALL THROUGH */
2942 BEGIN_CASE(JSOP_GOTO)
2943 {
2944     len = GET_JUMP_OFFSET(regs.pc);
2945     BRANCH(len);
2946 }
2947 END_CASE(JSOP_GOTO)
2948
2949 BEGIN_CASE(JSOP_IFEQ)
2950 {
2951     bool cond;
2952     Value *_;
2953     POP_BOOLEAN(cx, _, cond);
2954     if (cond == false) {
2955         len = GET_JUMP_OFFSET(regs.pc);
2956         BRANCH(len);
2957     }
2958 }
2959 END_CASE(JSOP_IFEQ)
2960
2961 BEGIN_CASE(JSOP_IFNE)
2962 {
2963     bool cond;
2964     Value *_;
2965     POP_BOOLEAN(cx, _, cond);
2966     if (cond != false) {
2967         len = GET_JUMP_OFFSET(regs.pc);
2968         BRANCH(len);
2969     }
2970 }
2971 END_CASE(JSOP_IFNE)
2972
2973 BEGIN_CASE(JSOP_OR)
2974 {
2975     bool cond;
2976     Value *vp;
2977     POP_BOOLEAN(cx, vp, cond);
2978     if (cond == true) {
2979         len = GET_JUMP_OFFSET(regs.pc);
2980         PUSH_COPY(*vp);
2981         DO_NEXT_OP(len);
2982     }
2983 }
2984 END_CASE(JSOP_OR)
2985
2986 BEGIN_CASE(JSOP_AND)
2987 {
2988     bool cond;
2989     Value *vp;
2990     POP_BOOLEAN(cx, vp, cond);
2991     if (cond == false) {
2992         len = GET_JUMP_OFFSET(regs.pc);
2993         PUSH_COPY(*vp);
2994         DO_NEXT_OP(len);
2995     }
2996 }
2997 END_CASE(JSOP_AND)
2998
2999 BEGIN_CASE(JSOP_DEFAULTX)
3000     regs.sp--;
3001     /* FALL THROUGH */
3002 BEGIN_CASE(JSOP_GOTOX)
3003 {
3004     len = GET_JUMPX_OFFSET(regs.pc);
3005     BRANCH(len);
3006 }
3007 END_CASE(JSOP_GOTOX);
3008
3009 BEGIN_CASE(JSOP_IFEQX)
3010 {
3011     bool cond;
3012     Value *_;
3013     POP_BOOLEAN(cx, _, cond);
3014     if (cond == false) {
3015         len = GET_JUMPX_OFFSET(regs.pc);
3016         BRANCH(len);
3017     }
3018 }
3019 END_CASE(JSOP_IFEQX)
3020
3021 BEGIN_CASE(JSOP_IFNEX)
3022 {
3023     bool cond;
3024     Value *_;
3025     POP_BOOLEAN(cx, _, cond);
3026     if (cond != false) {
3027         len = GET_JUMPX_OFFSET(regs.pc);
3028         BRANCH(len);
3029     }
3030 }
3031 END_CASE(JSOP_IFNEX)
3032
3033 BEGIN_CASE(JSOP_ORX)
3034 {
3035     bool cond;
3036     Value *vp;
3037     POP_BOOLEAN(cx, vp, cond);
3038     if (cond == true) {
3039         len = GET_JUMPX_OFFSET(regs.pc);
3040         PUSH_COPY(*vp);
3041         DO_NEXT_OP(len);
3042     }
3043 }
3044 END_CASE(JSOP_ORX)
3045
3046 BEGIN_CASE(JSOP_ANDX)
3047 {
3048     bool cond;
3049     Value *vp;
3050     POP_BOOLEAN(cx, vp, cond);
3051     if (cond == JS_FALSE) {
3052         len = GET_JUMPX_OFFSET(regs.pc);
3053         PUSH_COPY(*vp);
3054         DO_NEXT_OP(len);
3055     }
3056 }
3057 END_CASE(JSOP_ANDX)
3058
3059 /*
3060  * If the index value at sp[n] is not an int that fits in a jsval, it could
3061  * be an object (an XML QName, AttributeName, or AnyName), but only if we are
3062  * compiling with JS_HAS_XML_SUPPORT.  Otherwise convert the index value to a
3063  * string atom id.
3064  */
3065 #define FETCH_ELEMENT_ID(obj, n, id)                                          \
3066     JS_BEGIN_MACRO                                                            \
3067         const Value &idval_ = regs.sp[n];                                     \
3068         int32_t i_;                                                           \
3069         if (ValueFitsInInt32(idval_, &i_) && INT_FITS_IN_JSID(i_)) {          \
3070             id = INT_TO_JSID(i_);                                             \
3071         } else {                                                              \
3072             if (!js_InternNonIntElementId(cx, obj, idval_, &id, &regs.sp[n])) \
3073                 goto error;                                                   \
3074         }                                                                     \
3075     JS_END_MACRO
3076
3077 #define TRY_BRANCH_AFTER_COND(cond,spdec)                                     \
3078     JS_BEGIN_MACRO                                                            \
3079         JS_ASSERT(js_CodeSpec[op].length == 1);                               \
3080         uintN diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ;                 \
3081         if (diff_ <= 1) {                                                     \
3082             regs.sp -= spdec;                                                 \
3083             if (cond == (diff_ != 0)) {                                       \
3084                 ++regs.pc;                                                    \
3085                 len = GET_JUMP_OFFSET(regs.pc);                               \
3086                 BRANCH(len);                                                  \
3087             }                                                                 \
3088             len = 1 + JSOP_IFEQ_LENGTH;                                       \
3089             DO_NEXT_OP(len);                                                  \
3090         }                                                                     \
3091     JS_END_MACRO
3092
3093 BEGIN_CASE(JSOP_IN)
3094 {
3095     const Value &rref = regs.sp[-1];
3096     if (!rref.isObject()) {
3097         js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rref, NULL);
3098         goto error;
3099     }
3100     JSObject *obj = &rref.toObject();
3101     jsid id;
3102     FETCH_ELEMENT_ID(obj, -2, id);
3103     JSObject *obj2;
3104     JSProperty *prop;
3105     if (!obj->lookupProperty(cx, id, &obj2, &prop))
3106         goto error;
3107     bool cond = prop != NULL;
3108     TRY_BRANCH_AFTER_COND(cond, 2);
3109     regs.sp--;
3110     regs.sp[-1].setBoolean(cond);
3111 }
3112 END_CASE(JSOP_IN)
3113
3114 BEGIN_CASE(JSOP_ITER)
3115 {
3116     JS_ASSERT(regs.sp > regs.fp->base());
3117     uintN flags = regs.pc[1];
3118     if (!js_ValueToIterator(cx, flags, &regs.sp[-1]))
3119         goto error;
3120     CHECK_INTERRUPT_HANDLER();
3121     JS_ASSERT(!regs.sp[-1].isPrimitive());
3122 }
3123 END_CASE(JSOP_ITER)
3124
3125 BEGIN_CASE(JSOP_MOREITER)
3126 {
3127     JS_ASSERT(regs.sp - 1 >= regs.fp->base());
3128     JS_ASSERT(regs.sp[-1].isObject());
3129     PUSH_NULL();
3130     bool cond;
3131     if (!IteratorMore(cx, &regs.sp[-2].toObject(), &cond, &regs.sp[-1]))
3132         goto error;
3133     CHECK_INTERRUPT_HANDLER();
3134     regs.sp[-1].setBoolean(cond);
3135 }
3136 END_CASE(JSOP_MOREITER)
3137
3138 BEGIN_CASE(JSOP_ENDITER)
3139 {
3140     JS_ASSERT(regs.sp - 1 >= regs.fp->base());
3141     bool ok = !!js_CloseIterator(cx, &regs.sp[-1].toObject());
3142     regs.sp--;
3143     if (!ok)
3144         goto error;
3145 }
3146 END_CASE(JSOP_ENDITER)
3147
3148 BEGIN_CASE(JSOP_FORARG)
3149 {
3150     JS_ASSERT(regs.sp - 1 >= regs.fp->base());
3151     uintN slot = GET_ARGNO(regs.pc);
3152     JS_ASSERT(slot < regs.fp->numFormalArgs());
3153     JS_ASSERT(regs.sp[-1].isObject());
3154     if (!IteratorNext(cx, &regs.sp[-1].toObject(), &argv[slot]))
3155         goto error;
3156 }
3157 END_CASE(JSOP_FORARG)
3158
3159 BEGIN_CASE(JSOP_FORLOCAL)
3160 {
3161     JS_ASSERT(regs.sp - 1 >= regs.fp->base());
3162     uintN slot = GET_SLOTNO(regs.pc);
3163     JS_ASSERT(slot < regs.fp->numSlots());
3164     JS_ASSERT(regs.sp[-1].isObject());
3165     if (!IteratorNext(cx, &regs.sp[-1].toObject(), &regs.fp->slots()[slot]))
3166         goto error;
3167 }
3168 END_CASE(JSOP_FORLOCAL)
3169
3170 BEGIN_CASE(JSOP_FORNAME)
3171 BEGIN_CASE(JSOP_FORGNAME)
3172 {
3173     JS_ASSERT(regs.sp - 1 >= regs.fp->base());
3174     JSAtom *atom;
3175     LOAD_ATOM(0, atom);
3176     jsid id = ATOM_TO_JSID(atom);
3177     JSObject *obj, *obj2;
3178     JSProperty *prop;
3179     if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
3180         goto error;
3181
3182     {
3183         AutoValueRooter tvr(cx);
3184         JS_ASSERT(regs.sp[-1].isObject());
3185         if (!IteratorNext(cx, &regs.sp[-1].toObject(), tvr.addr()))
3186             goto error;
3187         if (!obj->setProperty(cx, id, tvr.addr(), script->strictModeCode))
3188             goto error;
3189     }
3190 }
3191 END_CASE(JSOP_FORNAME)
3192
3193 BEGIN_CASE(JSOP_FORPROP)
3194 {
3195     JS_ASSERT(regs.sp - 2 >= regs.fp->base());
3196     JSAtom *atom;
3197     LOAD_ATOM(0, atom);
3198     jsid id = ATOM_TO_JSID(atom);
3199     JSObject *obj;
3200     FETCH_OBJECT(cx, -1, obj);
3201     {
3202         AutoValueRooter tvr(cx);
3203         JS_ASSERT(regs.sp[-2].isObject());
3204         if (!IteratorNext(cx, &regs.sp[-2].toObject(), tvr.addr()))
3205             goto error;
3206         if (!obj->setProperty(cx, id, tvr.addr(), script->strictModeCode))
3207             goto error;
3208     }
3209     regs.sp--;
3210 }
3211 END_CASE(JSOP_FORPROP)
3212
3213 BEGIN_CASE(JSOP_FORELEM)
3214     /*
3215      * JSOP_FORELEM simply dups the property identifier at top of stack and
3216      * lets the subsequent JSOP_ENUMELEM opcode sequence handle the left-hand
3217      * side expression evaluation and assignment. This opcode exists solely to
3218      * help the decompiler.
3219      */
3220     JS_ASSERT(regs.sp - 1 >= regs.fp->base());
3221     JS_ASSERT(regs.sp[-1].isObject());
3222     PUSH_NULL();
3223     if (!IteratorNext(cx, &regs.sp[-2].toObject(), &regs.sp[-1]))
3224         goto error;
3225 END_CASE(JSOP_FORELEM)
3226
3227 BEGIN_CASE(JSOP_DUP)
3228 {
3229     JS_ASSERT(regs.sp > regs.fp->base());
3230     const Value &rref = regs.sp[-1];
3231     PUSH_COPY(rref);
3232 }
3233 END_CASE(JSOP_DUP)
3234
3235 BEGIN_CASE(JSOP_DUP2)
3236 {
3237     JS_ASSERT(regs.sp - 2 >= regs.fp->base());
3238     const Value &lref = regs.sp[-2];
3239     const Value &rref = regs.sp[-1];
3240     PUSH_COPY(lref);
3241     PUSH_COPY(rref);
3242 }
3243 END_CASE(JSOP_DUP2)
3244
3245 BEGIN_CASE(JSOP_SWAP)
3246 {
3247     JS_ASSERT(regs.sp - 2 >= regs.fp->base());
3248     Value &lref = regs.sp[-2];
3249     Value &rref = regs.sp[-1];
3250     lref.swap(rref);
3251 }
3252 END_CASE(JSOP_SWAP)
3253
3254 BEGIN_CASE(JSOP_PICK)
3255 {
3256     jsint i = regs.pc[1];
3257     JS_ASSERT(regs.sp - (i+1) >= regs.fp->base());
3258     Value lval = regs.sp[-(i+1)];
3259     memmove(regs.sp - (i+1), regs.sp - i, sizeof(Value)*i);
3260     regs.sp[-1] = lval;
3261 }
3262 END_CASE(JSOP_PICK)
3263
3264 #define NATIVE_GET(cx,obj,pobj,shape,getHow,vp)                               \
3265     JS_BEGIN_MACRO                                                            \
3266         if (shape->isDataDescriptor() && shape->hasDefaultGetter()) {         \
3267             /* Fast path for Object instance properties. */                   \
3268             JS_ASSERT((shape)->slot != SHAPE_INVALID_SLOT ||                  \
3269                       !shape->hasDefaultSetter());                            \
3270             if (((shape)->slot != SHAPE_INVALID_SLOT))                        \
3271                 *(vp) = (pobj)->nativeGetSlot((shape)->slot);                 \
3272             else                                                              \
3273                 (vp)->setUndefined();                                         \
3274         } else {                                                              \
3275             if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp))              \
3276                 goto error;                                                   \
3277         }                                                                     \
3278     JS_END_MACRO
3279
3280 #define NATIVE_SET(cx,obj,shape,entry,strict,vp)                              \
3281     JS_BEGIN_MACRO                                                            \
3282         if (shape->hasDefaultSetter() &&                                      \
3283             (shape)->slot != SHAPE_INVALID_SLOT &&                            \
3284             !(obj)->brandedOrHasMethodBarrier()) {                            \
3285             /* Fast path for, e.g., plain Object instance properties. */      \
3286             (obj)->nativeSetSlot((shape)->slot, *vp);                         \
3287         } else {                                                              \
3288             if (!js_NativeSet(cx, obj, shape, false, strict, vp))             \
3289                 goto error;                                                   \
3290         }                                                                     \
3291     JS_END_MACRO
3292
3293 /*
3294  * Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is
3295  * the constant length of the SET opcode sequence, and spdec is the constant
3296  * by which to decrease the stack pointer to pop all of the SET op's operands.
3297  *
3298  * NB: unlike macros that could conceivably be replaced by functions (ignoring
3299  * goto error), where a call should not have to be braced in order to expand
3300  * correctly (e.g., in if (cond) FOO(); else BAR()), these three macros lack
3301  * JS_{BEGIN,END}_MACRO brackets. They are also indented so as to align with
3302  * nearby opcode code.
3303  */
3304 #define SKIP_POP_AFTER_SET(oplen,spdec)                                       \
3305             if (regs.pc[oplen] == JSOP_POP) {                                 \
3306                 regs.sp -= spdec;                                             \
3307                 regs.pc += oplen + JSOP_POP_LENGTH;                           \
3308                 op = (JSOp) *regs.pc;                                         \
3309                 DO_OP();                                                      \
3310             }
3311
3312 #define END_SET_CASE(OP)                                                      \
3313             SKIP_POP_AFTER_SET(OP##_LENGTH, 1);                               \
3314           END_CASE(OP)
3315
3316 #define END_SET_CASE_STORE_RVAL(OP,spdec)                                     \
3317             SKIP_POP_AFTER_SET(OP##_LENGTH, spdec);                           \
3318             {                                                                 \
3319                 Value *newsp = regs.sp - ((spdec) - 1);                       \
3320                 newsp[-1] = regs.sp[-1];                                      \
3321                 regs.sp = newsp;                                              \
3322             }                                                                 \
3323           END_CASE(OP)
3324
3325 BEGIN_CASE(JSOP_SETCONST)
3326 {
3327     JSAtom *atom;
3328     LOAD_ATOM(0, atom);
3329     JSObject &obj = regs.fp->varobj(cx);
3330     const Value &ref = regs.sp[-1];
3331     if (!obj.defineProperty(cx, ATOM_TO_JSID(atom), ref,
3332                             PropertyStub, StrictPropertyStub,
3333                             JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) {
3334         goto error;
3335     }
3336 }
3337 END_SET_CASE(JSOP_SETCONST);
3338
3339 #if JS_HAS_DESTRUCTURING
3340 BEGIN_CASE(JSOP_ENUMCONSTELEM)
3341 {
3342     const Value &ref = regs.sp[-3];
3343     JSObject *obj;
3344     FETCH_OBJECT(cx, -2, obj);
3345     jsid id;
3346     FETCH_ELEMENT_ID(obj, -1, id);
3347     if (!obj->defineProperty(cx, id, ref,
3348                              PropertyStub, StrictPropertyStub,
3349                              JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) {
3350         goto error;
3351     }
3352     regs.sp -= 3;
3353 }
3354 END_CASE(JSOP_ENUMCONSTELEM)
3355 #endif
3356
3357 BEGIN_CASE(JSOP_BINDGNAME)
3358     PUSH_OBJECT(*regs.fp->scopeChain().getGlobal());
3359 END_CASE(JSOP_BINDGNAME)
3360
3361 BEGIN_CASE(JSOP_BINDNAME)
3362 {
3363     JSObject *obj;
3364     do {
3365         /*
3366          * We can skip the property lookup for the global object. If the
3367          * property does not exist anywhere on the scope chain, JSOP_SETNAME
3368          * adds the property to the global.
3369          *
3370          * As a consequence of this optimization for the global object we run
3371          * its JSRESOLVE_ASSIGNING-tolerant resolve hooks only in JSOP_SETNAME,
3372          * after the interpreter evaluates the right- hand-side of the
3373          * assignment, and not here.
3374          *
3375          * This should be transparent to the hooks because the script, instead
3376          * of name = rhs, could have used global.name = rhs given a global
3377          * object reference, which also calls the hooks only after evaluating
3378          * the rhs. We desire such resolve hook equivalence between the two
3379          * forms.
3380          */
3381         obj = &regs.fp->scopeChain();
3382         if (!obj->getParent())
3383             break;
3384
3385         PropertyCacheEntry *entry;
3386         JSObject *obj2;
3387         JSAtom *atom;
3388         JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
3389         if (!atom) {
3390             ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
3391             break;
3392         }
3393
3394         jsid id = ATOM_TO_JSID(atom);
3395         obj = js_FindIdentifierBase(cx, &regs.fp->scopeChain(), id);
3396         if (!obj)
3397             goto error;
3398     } while (0);
3399     PUSH_OBJECT(*obj);
3400 }
3401 END_CASE(JSOP_BINDNAME)
3402
3403 BEGIN_CASE(JSOP_IMACOP)
3404     JS_ASSERT(JS_UPTRDIFF(regs.fp->imacropc(), script->code) < script->length);
3405     op = JSOp(*regs.fp->imacropc());
3406     DO_OP();
3407
3408 #define BITWISE_OP(OP)                                                        \
3409     JS_BEGIN_MACRO                                                            \
3410         int32_t i, j;                                                         \
3411         if (!ValueToECMAInt32(cx, regs.sp[-2], &i))                           \
3412             goto error;                                                       \
3413         if (!ValueToECMAInt32(cx, regs.sp[-1], &j))                           \
3414             goto error;                                                       \
3415         i = i OP j;                                                           \
3416         regs.sp--;                                                            \
3417         regs.sp[-1].setInt32(i);                                              \
3418     JS_END_MACRO
3419
3420 BEGIN_CASE(JSOP_BITOR)
3421     BITWISE_OP(|);
3422 END_CASE(JSOP_BITOR)
3423
3424 BEGIN_CASE(JSOP_BITXOR)
3425     BITWISE_OP(^);
3426 END_CASE(JSOP_BITXOR)
3427
3428 BEGIN_CASE(JSOP_BITAND)
3429     BITWISE_OP(&);
3430 END_CASE(JSOP_BITAND)
3431
3432 #undef BITWISE_OP
3433
3434 /*
3435  * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies
3436  * because they begin if/else chains, so callers must not put semicolons after
3437  * the call expressions!
3438  */
3439 #if JS_HAS_XML_SUPPORT
3440 #define XML_EQUALITY_OP(OP)                                                   \
3441     if ((lval.isObject() && lval.toObject().isXML()) ||                       \
3442         (rval.isObject() && rval.toObject().isXML())) {                       \
3443         if (!js_TestXMLEquality(cx, lval, rval, &cond))                       \
3444             goto error;                                                       \
3445         cond = cond OP JS_TRUE;                                               \
3446     } else
3447 #else
3448 #define XML_EQUALITY_OP(OP)             /* nothing */
3449 #endif
3450
3451 #define EQUALITY_OP(OP, IFNAN)                                                \
3452     JS_BEGIN_MACRO                                                            \
3453         JSBool cond;                                                          \
3454         Value rval = regs.sp[-1];                                             \
3455         Value lval = regs.sp[-2];                                             \
3456         XML_EQUALITY_OP(OP)                                                   \
3457         if (SameType(lval, rval)) {                                           \
3458             if (lval.isString()) {                                            \
3459                 JSString *l = lval.toString(), *r = rval.toString();          \
3460                 JSBool equal;                                                 \
3461                 if (!EqualStrings(cx, l, r, &equal))                          \
3462                     goto error;                                               \
3463                 cond = equal OP JS_TRUE;                                      \
3464             } else if (lval.isDouble()) {                                     \
3465                 double l = lval.toDouble(), r = rval.toDouble();              \
3466                 cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN);                     \
3467             } else if (lval.isObject()) {                                     \
3468                 JSObject *l = &lval.toObject(), *r = &rval.toObject();        \
3469                 l->assertSpecialEqualitySynced();                             \
3470                 if (EqualityOp eq = l->getClass()->ext.equality) {            \
3471                     if (!eq(cx, l, &rval, &cond))                             \
3472                         goto error;                                           \
3473                     cond = cond OP JS_TRUE;                                   \
3474                 } else {                                                      \
3475                     cond = l OP r;                                            \
3476                 }                                                             \
3477             } else {                                                          \
3478                 cond = lval.payloadAsRawUint32() OP rval.payloadAsRawUint32();\
3479             }                                                                 \
3480         } else {                                                              \
3481             if (lval.isNullOrUndefined()) {                                   \
3482                 cond = rval.isNullOrUndefined() OP true;                      \
3483             } else if (rval.isNullOrUndefined()) {                            \
3484                 cond = true OP false;                                         \
3485             } else {                                                          \
3486                 if (lval.isObject())                                          \
3487                     DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval);                 \
3488                 if (rval.isObject())                                          \
3489                     DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval);                 \
3490                 if (lval.isString() && rval.isString()) {                     \
3491                     JSString *l = lval.toString(), *r = rval.toString();      \
3492                     JSBool equal;                                             \
3493                     if (!EqualStrings(cx, l, r, &equal))                      \
3494                         goto error;                                           \
3495                     cond = equal OP JS_TRUE;                                  \
3496                 } else {                                                      \
3497                     double l, r;                                              \
3498                     if (!ValueToNumber(cx, lval, &l) ||                       \
3499                         !ValueToNumber(cx, rval, &r)) {                       \
3500                         goto error;                                           \
3501                     }                                                         \
3502                     cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN);                 \
3503                 }                                                             \
3504             }                                                                 \
3505         }                                                                     \
3506         TRY_BRANCH_AFTER_COND(cond, 2);                                       \
3507         regs.sp--;                                                            \
3508         regs.sp[-1].setBoolean(cond);                                         \
3509     JS_END_MACRO
3510
3511 BEGIN_CASE(JSOP_EQ)
3512     EQUALITY_OP(==, false);
3513 END_CASE(JSOP_EQ)
3514
3515 BEGIN_CASE(JSOP_NE)
3516     EQUALITY_OP(!=, true);
3517 END_CASE(JSOP_NE)
3518
3519 #undef EQUALITY_OP
3520 #undef XML_EQUALITY_OP
3521 #undef EXTENDED_EQUALITY_OP
3522
3523 #define STRICT_EQUALITY_OP(OP, COND)                                          \
3524     JS_BEGIN_MACRO                                                            \
3525         const Value &rref = regs.sp[-1];                                      \
3526         const Value &lref = regs.sp[-2];                                      \
3527         JSBool equal;                                                         \
3528         if (!StrictlyEqual(cx, lref, rref, &equal))                           \
3529             goto error;                                                       \
3530         COND = equal OP JS_TRUE;                                              \
3531         regs.sp--;                                                            \
3532     JS_END_MACRO
3533
3534 BEGIN_CASE(JSOP_STRICTEQ)
3535 {
3536     bool cond;
3537     STRICT_EQUALITY_OP(==, cond);
3538     regs.sp[-1].setBoolean(cond);
3539 }
3540 END_CASE(JSOP_STRICTEQ)
3541
3542 BEGIN_CASE(JSOP_STRICTNE)
3543 {
3544     bool cond;
3545     STRICT_EQUALITY_OP(!=, cond);
3546     regs.sp[-1].setBoolean(cond);
3547 }
3548 END_CASE(JSOP_STRICTNE)
3549
3550 BEGIN_CASE(JSOP_CASE)
3551 {
3552     bool cond;
3553     STRICT_EQUALITY_OP(==, cond);
3554     if (cond) {
3555         regs.sp--;
3556         len = GET_JUMP_OFFSET(regs.pc);
3557         BRANCH(len);
3558     }
3559 }
3560 END_CASE(JSOP_CASE)
3561
3562 BEGIN_CASE(JSOP_CASEX)
3563 {
3564     bool cond;
3565     STRICT_EQUALITY_OP(==, cond);
3566     if (cond) {
3567         regs.sp--;
3568         len = GET_JUMPX_OFFSET(regs.pc);
3569         BRANCH(len);
3570     }
3571 }
3572 END_CASE(JSOP_CASEX)
3573
3574 #undef STRICT_EQUALITY_OP
3575
3576 #define RELATIONAL_OP(OP)                                                     \
3577     JS_BEGIN_MACRO                                                            \
3578         Value rval = regs.sp[-1];                                             \
3579         Value lval = regs.sp[-2];                                             \
3580         bool cond;                                                            \
3581         /* Optimize for two int-tagged operands (typical loop control). */    \
3582         if (lval.isInt32() && rval.isInt32()) {                               \
3583             cond = lval.toInt32() OP rval.toInt32();                          \
3584         } else {                                                              \
3585             if (lval.isObject())                                              \
3586                 DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval);                   \
3587             if (rval.isObject())                                              \
3588                 DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval);                   \
3589             if (lval.isString() && rval.isString()) {                         \
3590                 JSString *l = lval.toString(), *r = rval.toString();          \
3591                 int32 result;                                                 \
3592                 if (!CompareStrings(cx, l, r, &result))                       \
3593                     goto error;                                               \
3594                 cond = result OP 0;                                           \
3595             } else {                                                          \
3596                 double l, r;                                                  \
3597                 if (!ValueToNumber(cx, lval, &l) ||                           \
3598                     !ValueToNumber(cx, rval, &r)) {                           \
3599                     goto error;                                               \
3600                 }                                                             \
3601                 cond = JSDOUBLE_COMPARE(l, OP, r, false);                     \
3602             }                                                                 \
3603         }                                                                     \
3604         TRY_BRANCH_AFTER_COND(cond, 2);                                       \
3605         regs.sp--;                                                            \
3606         regs.sp[-1].setBoolean(cond);                                         \
3607     JS_END_MACRO
3608
3609 BEGIN_CASE(JSOP_LT)
3610     RELATIONAL_OP(<);
3611 END_CASE(JSOP_LT)
3612
3613 BEGIN_CASE(JSOP_LE)
3614     RELATIONAL_OP(<=);
3615 END_CASE(JSOP_LE)
3616
3617 BEGIN_CASE(JSOP_GT)
3618     RELATIONAL_OP(>);
3619 END_CASE(JSOP_GT)
3620
3621 BEGIN_CASE(JSOP_GE)
3622     RELATIONAL_OP(>=);
3623 END_CASE(JSOP_GE)
3624
3625 #undef RELATIONAL_OP
3626
3627 #define SIGNED_SHIFT_OP(OP)                                                   \
3628     JS_BEGIN_MACRO                                                            \
3629         int32_t i, j;                                                         \
3630         if (!ValueToECMAInt32(cx, regs.sp[-2], &i))                           \
3631             goto error;                                                       \
3632         if (!ValueToECMAInt32(cx, regs.sp[-1], &j))                           \
3633             goto error;                                                       \
3634         i = i OP (j & 31);                                                    \
3635         regs.sp--;                                                            \
3636         regs.sp[-1].setInt32(i);                                              \
3637     JS_END_MACRO
3638
3639 BEGIN_CASE(JSOP_LSH)
3640     SIGNED_SHIFT_OP(<<);
3641 END_CASE(JSOP_LSH)
3642
3643 BEGIN_CASE(JSOP_RSH)
3644     SIGNED_SHIFT_OP(>>);
3645 END_CASE(JSOP_RSH)
3646
3647 #undef SIGNED_SHIFT_OP
3648
3649 BEGIN_CASE(JSOP_URSH)
3650 {
3651     uint32_t u;
3652     if (!ValueToECMAUint32(cx, regs.sp[-2], &u))
3653         goto error;
3654     int32_t j;
3655     if (!ValueToECMAInt32(cx, regs.sp[-1], &j))
3656         goto error;
3657
3658     u >>= (j & 31);
3659
3660     regs.sp--;
3661     regs.sp[-1].setNumber(uint32(u));
3662 }
3663 END_CASE(JSOP_URSH)
3664
3665 BEGIN_CASE(JSOP_ADD)
3666 {
3667     Value rval = regs.sp[-1];
3668     Value lval = regs.sp[-2];
3669
3670     if (lval.isInt32() && rval.isInt32()) {
3671         int32_t l = lval.toInt32(), r = rval.toInt32();
3672         int32_t sum = l + r;
3673         regs.sp--;
3674         if (JS_UNLIKELY(bool((l ^ sum) & (r ^ sum) & 0x80000000)))
3675             regs.sp[-1].setDouble(double(l) + double(r));
3676         else
3677             regs.sp[-1].setInt32(sum);
3678     } else
3679 #if JS_HAS_XML_SUPPORT
3680     if (IsXML(lval) && IsXML(rval)) {
3681         if (!js_ConcatenateXML(cx, &lval.toObject(), &rval.toObject(), &rval))
3682             goto error;
3683         regs.sp--;
3684         regs.sp[-1] = rval;
3685     } else
3686 #endif
3687     {
3688         if (lval.isObject())
3689             DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval);
3690         if (rval.isObject())
3691             DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval);
3692         bool lIsString, rIsString;
3693         if ((lIsString = lval.isString()) | (rIsString = rval.isString())) {
3694             JSString *lstr, *rstr;
3695             if (lIsString) {
3696                 lstr = lval.toString();
3697             } else {
3698                 lstr = js_ValueToString(cx, lval);
3699                 if (!lstr)
3700                     goto error;
3701                 regs.sp[-2].setString(lstr);
3702             }
3703             if (rIsString) {
3704                 rstr = rval.toString();
3705             } else {
3706                 rstr = js_ValueToString(cx, rval);
3707                 if (!rstr)
3708                     goto error;
3709                 regs.sp[-1].setString(rstr);
3710             }
3711             JSString *str = js_ConcatStrings(cx, lstr, rstr);
3712             if (!str)
3713                 goto error;
3714             regs.sp--;
3715             regs.sp[-1].setString(str);
3716         } else {
3717             double l, r;
3718             if (!ValueToNumber(cx, lval, &l) || !ValueToNumber(cx, rval, &r))
3719                 goto error;
3720             l += r;
3721             regs.sp--;
3722             regs.sp[-1].setNumber(l);
3723         }
3724     }
3725 }
3726 END_CASE(JSOP_ADD)
3727
3728 #define BINARY_OP(OP)                                                         \
3729     JS_BEGIN_MACRO                                                            \
3730         double d1, d2;                                                        \
3731         if (!ValueToNumber(cx, regs.sp[-2], &d1) ||                           \
3732             !ValueToNumber(cx, regs.sp[-1], &d2)) {                           \
3733             goto error;                                                       \
3734         }                                                                     \
3735         double d = d1 OP d2;                                                  \
3736         regs.sp--;                                                            \
3737         regs.sp[-1].setNumber(d);                                             \
3738     JS_END_MACRO
3739
3740 BEGIN_CASE(JSOP_SUB)
3741     BINARY_OP(-);
3742 END_CASE(JSOP_SUB)
3743
3744 BEGIN_CASE(JSOP_MUL)
3745     BINARY_OP(*);
3746 END_CASE(JSOP_MUL)
3747
3748 #undef BINARY_OP
3749
3750 BEGIN_CASE(JSOP_DIV)
3751 {
3752     double d1, d2;
3753     if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
3754         !ValueToNumber(cx, regs.sp[-1], &d2)) {
3755         goto error;
3756     }
3757     regs.sp--;
3758     if (d2 == 0) {
3759         const Value *vp;
3760 #ifdef XP_WIN
3761         /* XXX MSVC miscompiles such that (NaN == 0) */
3762         if (JSDOUBLE_IS_NaN(d2))
3763             vp = &rt->NaNValue;
3764         else
3765 #endif
3766         if (d1 == 0 || JSDOUBLE_IS_NaN(d1))
3767             vp = &rt->NaNValue;
3768         else if (JSDOUBLE_IS_NEG(d1) != JSDOUBLE_IS_NEG(d2))
3769             vp = &rt->negativeInfinityValue;
3770         else
3771             vp = &rt->positiveInfinityValue;
3772         regs.sp[-1] = *vp;
3773     } else {
3774         d1 /= d2;
3775         regs.sp[-1].setNumber(d1);
3776     }
3777 }
3778 END_CASE(JSOP_DIV)
3779
3780 BEGIN_CASE(JSOP_MOD)
3781 {
3782     Value &lref = regs.sp[-2];
3783     Value &rref = regs.sp[-1];
3784     int32_t l, r;
3785     if (lref.isInt32() && rref.isInt32() &&
3786         (l = lref.toInt32()) >= 0 && (r = rref.toInt32()) > 0) {
3787         int32_t mod = l % r;
3788         regs.sp--;
3789         regs.sp[-1].setInt32(mod);
3790     } else {
3791         double d1, d2;
3792         if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
3793             !ValueToNumber(cx, regs.sp[-1], &d2)) {
3794             goto error;
3795         }
3796         regs.sp--;
3797         if (d2 == 0) {
3798             regs.sp[-1].setDouble(js_NaN);
3799         } else {
3800             d1 = js_fmod(d1, d2);
3801             regs.sp[-1].setDouble(d1);
3802         }
3803     }
3804 }
3805 END_CASE(JSOP_MOD)
3806
3807 BEGIN_CASE(JSOP_NOT)
3808 {
3809     Value *_;
3810     bool cond;
3811     POP_BOOLEAN(cx, _, cond);
3812     PUSH_BOOLEAN(!cond);
3813 }
3814 END_CASE(JSOP_NOT)
3815
3816 BEGIN_CASE(JSOP_BITNOT)
3817 {
3818     int32_t i;
3819     if (!ValueToECMAInt32(cx, regs.sp[-1], &i))
3820         goto error;
3821     i = ~i;
3822     regs.sp[-1].setInt32(i);
3823 }
3824 END_CASE(JSOP_BITNOT)
3825
3826 BEGIN_CASE(JSOP_NEG)
3827 {
3828     /*
3829      * When the operand is int jsval, INT32_FITS_IN_JSVAL(i) implies
3830      * INT32_FITS_IN_JSVAL(-i) unless i is 0 or INT32_MIN when the
3831      * results, -0.0 or INT32_MAX + 1, are jsdouble values.
3832      */
3833     const Value &ref = regs.sp[-1];
3834     int32_t i;
3835     if (ref.isInt32() && (i = ref.toInt32()) != 0 && i != INT32_MIN) {
3836         i = -i;
3837         regs.sp[-1].setInt32(i);
3838     } else {
3839         double d;
3840         if (!ValueToNumber(cx, regs.sp[-1], &d))
3841             goto error;
3842         d = -d;
3843         regs.sp[-1].setDouble(d);
3844     }
3845 }
3846 END_CASE(JSOP_NEG)
3847
3848 BEGIN_CASE(JSOP_POS)
3849     if (!ValueToNumber(cx, &regs.sp[-1]))
3850         goto error;
3851 END_CASE(JSOP_POS)
3852
3853 BEGIN_CASE(JSOP_DELNAME)
3854 {
3855     JSAtom *atom;
3856     LOAD_ATOM(0, atom);
3857     jsid id = ATOM_TO_JSID(atom);
3858     JSObject *obj, *obj2;
3859     JSProperty *prop;
3860     if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
3861         goto error;
3862
3863     /* Strict mode code should never contain JSOP_DELNAME opcodes. */
3864     JS_ASSERT(!script->strictModeCode);
3865
3866     /* ECMA says to return true if name is undefined or inherited. */
3867     PUSH_BOOLEAN(true);
3868     if (prop) {
3869         if (!obj->deleteProperty(cx, id, &regs.sp[-1], false))
3870             goto error;
3871     }
3872 }
3873 END_CASE(JSOP_DELNAME)
3874
3875 BEGIN_CASE(JSOP_DELPROP)
3876 {
3877     JSAtom *atom;
3878     LOAD_ATOM(0, atom);
3879     jsid id = ATOM_TO_JSID(atom);
3880
3881     JSObject *obj;
3882     FETCH_OBJECT(cx, -1, obj);
3883
3884     Value rval;
3885     if (!obj->deleteProperty(cx, id, &rval, script->strictModeCode))
3886         goto error;
3887
3888     regs.sp[-1] = rval;
3889 }
3890 END_CASE(JSOP_DELPROP)
3891
3892 BEGIN_CASE(JSOP_DELELEM)
3893 {
3894     /* Fetch the left part and resolve it to a non-null object. */
3895     JSObject *obj;
3896     FETCH_OBJECT(cx, -2, obj);
3897
3898     /* Fetch index and convert it to id suitable for use with obj. */
3899     jsid id;
3900     FETCH_ELEMENT_ID(obj, -1, id);
3901
3902     /* Get or set the element. */
3903     if (!obj->deleteProperty(cx, id, &regs.sp[-2], script->strictModeCode))
3904         goto error;
3905
3906     regs.sp--;
3907 }
3908 END_CASE(JSOP_DELELEM)
3909
3910 BEGIN_CASE(JSOP_TYPEOFEXPR)
3911 BEGIN_CASE(JSOP_TYPEOF)
3912 {
3913     const Value &ref = regs.sp[-1];
3914     JSType type = JS_TypeOfValue(cx, Jsvalify(ref));
3915     JSAtom *atom = rt->atomState.typeAtoms[type];
3916     regs.sp[-1].setString(ATOM_TO_STRING(atom));
3917 }
3918 END_CASE(JSOP_TYPEOF)
3919
3920 BEGIN_CASE(JSOP_VOID)
3921     regs.sp[-1].setUndefined();
3922 END_CASE(JSOP_VOID)
3923
3924 {
3925     JSObject *obj;
3926     JSAtom *atom;
3927     jsid id;
3928     jsint i;
3929
3930 BEGIN_CASE(JSOP_INCELEM)
3931 BEGIN_CASE(JSOP_DECELEM)
3932 BEGIN_CASE(JSOP_ELEMINC)
3933 BEGIN_CASE(JSOP_ELEMDEC)
3934
3935     /*
3936      * Delay fetching of id until we have the object to ensure the proper
3937      * evaluation order. See bug 372331.
3938      */
3939     id = JSID_VOID;
3940     i = -2;
3941     goto fetch_incop_obj;
3942
3943 BEGIN_CASE(JSOP_INCPROP)
3944 BEGIN_CASE(JSOP_DECPROP)
3945 BEGIN_CASE(JSOP_PROPINC)
3946 BEGIN_CASE(JSOP_PROPDEC)
3947     LOAD_ATOM(0, atom);
3948     id = ATOM_TO_JSID(atom);
3949     i = -1;
3950
3951   fetch_incop_obj:
3952     FETCH_OBJECT(cx, i, obj);
3953     if (JSID_IS_VOID(id))
3954         FETCH_ELEMENT_ID(obj, -1, id);
3955     goto do_incop;
3956
3957 BEGIN_CASE(JSOP_INCNAME)
3958 BEGIN_CASE(JSOP_DECNAME)
3959 BEGIN_CASE(JSOP_NAMEINC)
3960 BEGIN_CASE(JSOP_NAMEDEC)
3961 BEGIN_CASE(JSOP_INCGNAME)
3962 BEGIN_CASE(JSOP_DECGNAME)
3963 BEGIN_CASE(JSOP_GNAMEINC)
3964 BEGIN_CASE(JSOP_GNAMEDEC)
3965 {
3966     obj = &regs.fp->scopeChain();
3967     if (js_CodeSpec[op].format & JOF_GNAME)
3968         obj = obj->getGlobal();
3969
3970     JSObject *obj2;
3971     PropertyCacheEntry *entry;
3972     JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
3973     if (!atom) {
3974         ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
3975         if (obj == obj2 && entry->vword.isSlot()) {
3976             uint32 slot = entry->vword.toSlot();
3977             Value &rref = obj->nativeGetSlotRef(slot);
3978             int32_t tmp;
3979             if (JS_LIKELY(rref.isInt32() && CanIncDecWithoutOverflow(tmp = rref.toInt32()))) {
3980                 int32_t inc = tmp + ((js_CodeSpec[op].format & JOF_INC) ? 1 : -1);
3981                 if (!(js_CodeSpec[op].format & JOF_POST))
3982                     tmp = inc;
3983                 rref.getInt32Ref() = inc;
3984                 PUSH_INT32(tmp);
3985                 len = JSOP_INCNAME_LENGTH;
3986                 DO_NEXT_OP(len);
3987             }
3988         }
3989         LOAD_ATOM(0, atom);
3990     }
3991
3992     id = ATOM_TO_JSID(atom);
3993     JSProperty *prop;
3994     if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
3995         goto error;
3996     if (!prop) {
3997         atomNotDefined = atom;
3998         goto atom_not_defined;
3999     }
4000 }
4001
4002 do_incop:
4003 {
4004     /*
4005      * We need a root to store the value to leave on the stack until
4006      * we have done with obj->setProperty.
4007      */
4008     PUSH_NULL();
4009     if (!obj->getProperty(cx, id, &regs.sp[-1]))
4010         goto error;
4011
4012     const JSCodeSpec *cs = &js_CodeSpec[op];
4013     JS_ASSERT(cs->ndefs == 1);
4014     JS_ASSERT((cs->format & JOF_TMPSLOT_MASK) >= JOF_TMPSLOT2);
4015     Value &ref = regs.sp[-1];
4016     int32_t tmp;
4017     if (JS_LIKELY(ref.isInt32() && CanIncDecWithoutOverflow(tmp = ref.toInt32()))) {
4018         int incr = (cs->format & JOF_INC) ? 1 : -1;
4019         if (cs->format & JOF_POST)
4020             ref.getInt32Ref() = tmp + incr;
4021         else
4022             ref.getInt32Ref() = tmp += incr;
4023         regs.fp->setAssigning();
4024         JSBool ok = obj->setProperty(cx, id, &ref, script->strictModeCode);
4025         regs.fp->clearAssigning();
4026         if (!ok)
4027             goto error;
4028
4029         /*
4030          * We must set regs.sp[-1] to tmp for both post and pre increments
4031          * as the setter overwrites regs.sp[-1].
4032          */
4033         ref.setInt32(tmp);
4034     } else {
4035         /* We need an extra root for the result. */
4036         PUSH_NULL();
4037         if (!js_DoIncDec(cx, cs, &regs.sp[-2], &regs.sp[-1]))
4038             goto error;
4039         regs.fp->setAssigning();
4040         JSBool ok = obj->setProperty(cx, id, &regs.sp[-1], script->strictModeCode);
4041         regs.fp->clearAssigning();
4042         if (!ok)
4043             goto error;
4044         regs.sp--;
4045     }
4046
4047     if (cs->nuses == 0) {
4048         /* regs.sp[-1] already contains the result of name increment. */
4049     } else {
4050         regs.sp[-1 - cs->nuses] = regs.sp[-1];
4051         regs.sp -= cs->nuses;
4052     }
4053     len = cs->length;
4054     DO_NEXT_OP(len);
4055 }
4056 }
4057
4058 {
4059     int incr, incr2;
4060     uint32 slot;
4061     Value *vp;
4062
4063     /* Position cases so the most frequent i++ does not need a jump. */
4064 BEGIN_CASE(JSOP_DECARG)
4065     incr = -1; incr2 = -1; goto do_arg_incop;
4066 BEGIN_CASE(JSOP_ARGDEC)
4067     incr = -1; incr2 =  0; goto do_arg_incop;
4068 BEGIN_CASE(JSOP_INCARG)
4069     incr =  1; incr2 =  1; goto do_arg_incop;
4070 BEGIN_CASE(JSOP_ARGINC)
4071     incr =  1; incr2 =  0;
4072
4073   do_arg_incop:
4074     slot = GET_ARGNO(regs.pc);
4075     JS_ASSERT(slot < regs.fp->numFormalArgs());
4076     METER_SLOT_OP(op, slot);
4077     vp = argv + slot;
4078     goto do_int_fast_incop;
4079
4080 BEGIN_CASE(JSOP_DECLOCAL)
4081     incr = -1; incr2 = -1; goto do_local_incop;
4082 BEGIN_CASE(JSOP_LOCALDEC)
4083     incr = -1; incr2 =  0; goto do_local_incop;
4084 BEGIN_CASE(JSOP_INCLOCAL)
4085     incr =  1; incr2 =  1; goto do_local_incop;
4086 BEGIN_CASE(JSOP_LOCALINC)
4087     incr =  1; incr2 =  0;
4088
4089   /*
4090    * do_local_incop comes right before do_int_fast_incop as we want to
4091    * avoid an extra jump for variable cases as local++ is more frequent
4092    * than arg++.
4093    */
4094   do_local_incop:
4095     slot = GET_SLOTNO(regs.pc);
4096     JS_ASSERT(slot < regs.fp->numSlots());
4097     METER_SLOT_OP(op, slot);
4098     vp = regs.fp->slots() + slot;
4099
4100   do_int_fast_incop:
4101     int32_t tmp;
4102     if (JS_LIKELY(vp->isInt32() && CanIncDecWithoutOverflow(tmp = vp->toInt32()))) {
4103         vp->getInt32Ref() = tmp + incr;
4104         JS_ASSERT(JSOP_INCARG_LENGTH == js_CodeSpec[op].length);
4105         SKIP_POP_AFTER_SET(JSOP_INCARG_LENGTH, 0);
4106         PUSH_INT32(tmp + incr2);
4107     } else {
4108         PUSH_COPY(*vp);
4109         if (!js_DoIncDec(cx, &js_CodeSpec[op], &regs.sp[-1], vp))
4110             goto error;
4111     }
4112     len = JSOP_INCARG_LENGTH;
4113     JS_ASSERT(len == js_CodeSpec[op].length);
4114     DO_NEXT_OP(len);
4115 }
4116
4117 BEGIN_CASE(JSOP_THIS)
4118     if (!regs.fp->computeThis(cx))
4119         goto error;
4120     PUSH_COPY(regs.fp->thisValue());
4121 END_CASE(JSOP_THIS)
4122
4123 BEGIN_CASE(JSOP_UNBRANDTHIS)
4124 {
4125     if (!regs.fp->computeThis(cx))
4126         goto error;
4127     Value &thisv = regs.fp->thisValue();
4128     if (thisv.isObject()) {
4129         JSObject *obj = &thisv.toObject();
4130         if (obj->isNative())
4131             obj->unbrand(cx);
4132     }
4133 }
4134 END_CASE(JSOP_UNBRANDTHIS)
4135
4136 {
4137     JSObject *obj;
4138     Value *vp;
4139     jsint i;
4140
4141 BEGIN_CASE(JSOP_GETTHISPROP)
4142     if (!regs.fp->computeThis(cx))
4143         goto error;
4144     i = 0;
4145     PUSH_COPY(regs.fp->thisValue());
4146     goto do_getprop_body;
4147
4148 BEGIN_CASE(JSOP_GETARGPROP)
4149 {
4150     i = ARGNO_LEN;
4151     uint32 slot = GET_ARGNO(regs.pc);
4152     JS_ASSERT(slot < regs.fp->numFormalArgs());
4153     PUSH_COPY(argv[slot]);
4154     goto do_getprop_body;
4155 }
4156
4157 BEGIN_CASE(JSOP_GETLOCALPROP)
4158 {
4159     i = SLOTNO_LEN;
4160     uint32 slot = GET_SLOTNO(regs.pc);
4161     JS_ASSERT(slot < script->nslots);
4162     PUSH_COPY(regs.fp->slots()[slot]);
4163     goto do_getprop_body;
4164 }
4165
4166 BEGIN_CASE(JSOP_GETPROP)
4167 BEGIN_CASE(JSOP_GETXPROP)
4168     i = 0;
4169
4170   do_getprop_body:
4171     vp = &regs.sp[-1];
4172
4173   do_getprop_with_lval:
4174     VALUE_TO_OBJECT(cx, vp, obj);
4175
4176     {
4177         Value rval;
4178         do {
4179             /*
4180              * We do not impose the method read barrier if in an imacro,
4181              * assuming any property gets it does (e.g., for 'toString'
4182              * from JSOP_NEW) will not be leaked to the calling script.
4183              */
4184             JSObject *aobj = js_GetProtoIfDenseArray(obj);
4185
4186             PropertyCacheEntry *entry;
4187             JSObject *obj2;
4188             JSAtom *atom;
4189             JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
4190             if (!atom) {
4191                 ASSERT_VALID_PROPERTY_CACHE_HIT(i, aobj, obj2, entry);
4192                 if (entry->vword.isFunObj()) {
4193                     rval.setObject(entry->vword.toFunObj());
4194                 } else if (entry->vword.isSlot()) {
4195                     uint32 slot = entry->vword.toSlot();
4196                     rval = obj2->nativeGetSlot(slot);
4197                 } else {
4198                     JS_ASSERT(entry->vword.isShape());
4199                     const Shape *shape = entry->vword.toShape();
4200                     NATIVE_GET(cx, obj, obj2, shape,
4201                                regs.fp->hasImacropc() ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER,
4202                                &rval);
4203                 }
4204                 break;
4205             }
4206
4207             jsid id = ATOM_TO_JSID(atom);
4208             if (JS_LIKELY(!aobj->getOps()->getProperty)
4209                 ? !js_GetPropertyHelper(cx, obj, id,
4210                                         (regs.fp->hasImacropc() ||
4211                                          regs.pc[JSOP_GETPROP_LENGTH + i] == JSOP_IFEQ)
4212                                         ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
4213                                         : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER,
4214                                         &rval)
4215                 : !obj->getProperty(cx, id, &rval)) {
4216                 goto error;
4217             }
4218         } while (0);
4219
4220         regs.sp[-1] = rval;
4221         assertSameCompartment(cx, regs.sp[-1]);
4222         JS_ASSERT(JSOP_GETPROP_LENGTH + i == js_CodeSpec[op].length);
4223         len = JSOP_GETPROP_LENGTH + i;
4224     }
4225 END_VARLEN_CASE
4226
4227 BEGIN_CASE(JSOP_LENGTH)
4228     vp = &regs.sp[-1];
4229     if (vp->isString()) {
4230         vp->setInt32(vp->toString()->length());
4231     } else if (vp->isObject()) {
4232         JSObject *obj = &vp->toObject();
4233         if (obj->isArray()) {
4234             jsuint length = obj->getArrayLength();
4235             regs.sp[-1].setNumber(length);
4236         } else if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
4237             uint32 length = obj->getArgsInitialLength();
4238             JS_ASSERT(length < INT32_MAX);
4239             regs.sp[-1].setInt32(int32_t(length));
4240         } else {
4241             i = -2;
4242             goto do_getprop_with_lval;
4243         }
4244     } else {
4245         i = -2;
4246         goto do_getprop_with_lval;
4247     }
4248 END_CASE(JSOP_LENGTH)
4249
4250 }
4251
4252 BEGIN_CASE(JSOP_CALLPROP)
4253 {
4254     Value lval = regs.sp[-1];
4255
4256     Value objv;
4257     if (lval.isObject()) {
4258         objv = lval;
4259     } else {
4260         JSProtoKey protoKey;
4261         if (lval.isString()) {
4262             protoKey = JSProto_String;
4263         } else if (lval.isNumber()) {
4264             protoKey = JSProto_Number;
4265         } else if (lval.isBoolean()) {
4266             protoKey = JSProto_Boolean;
4267         } else {
4268             JS_ASSERT(lval.isNull() || lval.isUndefined());
4269             js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
4270             goto error;
4271         }
4272         JSObject *pobj;
4273         if (!js_GetClassPrototype(cx, NULL, protoKey, &pobj))
4274             goto error;
4275         objv.setObject(*pobj);
4276     }
4277
4278     JSObject *aobj = js_GetProtoIfDenseArray(&objv.toObject());
4279     Value rval;
4280
4281     PropertyCacheEntry *entry;
4282     JSObject *obj2;
4283     JSAtom *atom;
4284     JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
4285     if (!atom) {
4286         ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry);
4287         if (entry->vword.isFunObj()) {
4288             rval.setObject(entry->vword.toFunObj());
4289         } else if (entry->vword.isSlot()) {
4290             uint32 slot = entry->vword.toSlot();
4291             rval = obj2->nativeGetSlot(slot);
4292         } else {
4293             JS_ASSERT(entry->vword.isShape());
4294             const Shape *shape = entry->vword.toShape();
4295             NATIVE_GET(cx, &objv.toObject(), obj2, shape, JSGET_NO_METHOD_BARRIER, &rval);
4296         }
4297         regs.sp[-1] = rval;
4298         assertSameCompartment(cx, regs.sp[-1]);
4299         PUSH_COPY(lval);
4300     } else {
4301         /*
4302          * Cache miss: use the immediate atom that was loaded for us under
4303          * PropertyCache::test.
4304          */
4305         jsid id;
4306         id = ATOM_TO_JSID(atom);
4307
4308         PUSH_NULL();
4309         if (lval.isObject()) {
4310             if (!js_GetMethod(cx, &objv.toObject(), id,
4311                               JS_LIKELY(!aobj->getOps()->getProperty)
4312                               ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
4313                               : JSGET_NO_METHOD_BARRIER,
4314                               &rval)) {
4315                 goto error;
4316             }
4317             regs.sp[-1] = objv;
4318             regs.sp[-2] = rval;
4319             assertSameCompartment(cx, regs.sp[-1], regs.sp[-2]);
4320         } else {
4321             JS_ASSERT(!objv.toObject().getOps()->getProperty);
4322             if (!js_GetPropertyHelper(cx, &objv.toObject(), id,
4323                                       JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER,
4324                                       &rval)) {
4325                 goto error;
4326             }
4327             regs.sp[-1] = lval;
4328             regs.sp[-2] = rval;
4329             assertSameCompartment(cx, regs.sp[-1], regs.sp[-2]);
4330         }
4331     }
4332 #if JS_HAS_NO_SUCH_METHOD
4333     if (JS_UNLIKELY(rval.isUndefined()) && regs.sp[-1].isObject()) {
4334         LOAD_ATOM(0, atom);
4335         regs.sp[-2].setString(ATOM_TO_STRING(atom));
4336         if (!js_OnUnknownMethod(cx, regs.sp - 2))
4337             goto error;
4338     }
4339 #endif
4340 }
4341 END_CASE(JSOP_CALLPROP)
4342
4343 BEGIN_CASE(JSOP_UNBRAND)
4344     JS_ASSERT(regs.sp - regs.fp->slots() >= 1);
4345     regs.sp[-1].toObject().unbrand(cx);
4346 END_CASE(JSOP_UNBRAND)
4347
4348 BEGIN_CASE(JSOP_SETGNAME)
4349 BEGIN_CASE(JSOP_SETNAME)
4350 BEGIN_CASE(JSOP_SETPROP)
4351 BEGIN_CASE(JSOP_SETMETHOD)
4352 {
4353     Value rval = regs.sp[-1];
4354     JS_ASSERT_IF(op == JSOP_SETMETHOD, IsFunctionObject(rval));
4355     Value &lref = regs.sp[-2];
4356     JS_ASSERT_IF(op == JSOP_SETNAME, lref.isObject());
4357     JSObject *obj;
4358     VALUE_TO_OBJECT(cx, &lref, obj);
4359
4360     JS_ASSERT_IF(op == JSOP_SETGNAME, obj == regs.fp->scopeChain().getGlobal());
4361
4362     do {
4363         PropertyCache *cache = &JS_PROPERTY_CACHE(cx);
4364
4365         /*
4366          * Probe the property cache, specializing for two important
4367          * set-property cases. First:
4368          *
4369          *   function f(a, b, c) {
4370          *     var o = {p:a, q:b, r:c};
4371          *     return o;
4372          *   }
4373          *
4374          * or similar real-world cases, which evolve a newborn native
4375          * object predicatably through some bounded number of property
4376          * additions. And second:
4377          *
4378          *   o.p = x;
4379          *
4380          * in a frequently executed method or loop body, where p will
4381          * (possibly after the first iteration) always exist in native
4382          * object o.
4383          */
4384         PropertyCacheEntry *entry;
4385         JSObject *obj2;
4386         JSAtom *atom;
4387         if (cache->testForSet(cx, regs.pc, obj, &entry, &obj2, &atom)) {
4388             /*
4389              * Property cache hit, only partially confirmed by testForSet. We
4390              * know that the entry applies to regs.pc and that obj's shape
4391              * matches.
4392              *
4393              * The entry predicts either a new property to be added directly to
4394              * obj by this set, or on an existing "own" property, or on a
4395              * prototype property that has a setter.
4396              */
4397             const Shape *shape = entry->vword.toShape();
4398             JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable());
4399             JS_ASSERT_IF(shape->hasSlot(), entry->vcapTag() == 0);
4400
4401             /*
4402              * Fastest path: check whether obj already has the cached shape and
4403              * call NATIVE_SET and break to get out of the do-while(0). But we
4404              * can call NATIVE_SET only for a direct or proto-setter hit.
4405              */
4406             if (!entry->adding()) {
4407                 if (entry->vcapTag() == 0 ||
4408                     ((obj2 = obj->getProto()) && obj2->shape() == entry->vshape()))
4409                 {
4410 #ifdef DEBUG
4411                     if (entry->directHit()) {
4412                         JS_ASSERT(obj->nativeContains(*shape));
4413                     } else {
4414                         JS_ASSERT(obj2->nativeContains(*shape));
4415                         JS_ASSERT(entry->vcapTag() == 1);
4416                         JS_ASSERT(entry->kshape != entry->vshape());
4417                         JS_ASSERT(!shape->hasSlot());
4418                     }
4419 #endif
4420
4421                     PCMETER(cache->pchits++);
4422                     PCMETER(cache->setpchits++);
4423                     NATIVE_SET(cx, obj, shape, entry, script->strictModeCode, &rval);
4424                     break;
4425                 }
4426             } else {
4427                 JS_ASSERT(obj->isExtensible());
4428
4429                 if (obj->nativeEmpty()) {
4430                     if (!obj->ensureClassReservedSlotsForEmptyObject(cx))
4431                         goto error;
4432                 }
4433
4434                 uint32 slot;
4435                 if (shape->previous() == obj->lastProperty() &&
4436                     entry->vshape() == rt->protoHazardShape &&
4437                     shape->hasDefaultSetter()) {
4438                     slot = shape->slot;
4439                     JS_ASSERT(slot == obj->slotSpan());
4440
4441                     /*
4442                      * Fast path: adding a plain old property that was once at
4443                      * the frontier of the property tree, whose slot is next to
4444                      * claim among the already-allocated slots in obj, where
4445                      * shape->table has not been created yet.
4446                      */
4447                     PCMETER(cache->pchits++);
4448                     PCMETER(cache->addpchits++);
4449
4450                     if (slot < obj->numSlots()) {
4451                         JS_ASSERT(obj->getSlot(slot).isUndefined());
4452                     } else {
4453                         if (!obj->allocSlot(cx, &slot))
4454                             goto error;
4455                         JS_ASSERT(slot == shape->slot);
4456                     }
4457
4458                     /* Simply extend obj's property tree path with shape! */
4459                     obj->extend(cx, shape);
4460
4461                     /*
4462                      * No method change check here because here we are adding a
4463                      * new property, not updating an existing slot's value that
4464                      * might contain a method of a branded shape.
4465                      */
4466                     TRACE_1(AddProperty, obj);
4467                     obj->nativeSetSlot(slot, rval);
4468
4469                     /*
4470                      * Purge the property cache of the id we may have just
4471                      * shadowed in obj's scope and proto chains.
4472                      */
4473                     js_PurgeScopeChain(cx, obj, shape->id);
4474                     break;
4475                 }
4476             }
4477             PCMETER(cache->setpcmisses++);
4478
4479             LOAD_ATOM(0, atom);
4480         } else {
4481             JS_ASSERT(atom);
4482         }
4483
4484         jsid id = ATOM_TO_JSID(atom);
4485         if (entry && JS_LIKELY(!obj->getOps()->setProperty)) {
4486             uintN defineHow;
4487             if (op == JSOP_SETMETHOD)
4488                 defineHow = JSDNP_CACHE_RESULT | JSDNP_SET_METHOD;
4489             else if (op == JSOP_SETNAME)
4490                 defineHow = JSDNP_CACHE_RESULT | JSDNP_UNQUALIFIED;
4491             else
4492                 defineHow = JSDNP_CACHE_RESULT;
4493             if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rval, script->strictModeCode))
4494                 goto error;
4495         } else {
4496             if (!obj->setProperty(cx, id, &rval, script->strictModeCode))
4497                 goto error;
4498             ABORT_RECORDING(cx, "Non-native set");
4499         }
4500     } while (0);
4501 }
4502 END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2);
4503
4504 BEGIN_CASE(JSOP_GETELEM)
4505 {
4506     Value &lref = regs.sp[-2];
4507     Value &rref = regs.sp[-1];
4508     if (lref.isString() && rref.isInt32()) {
4509         JSString *str = lref.toString();
4510         int32_t i = rref.toInt32();
4511         if (size_t(i) < str->length()) {
4512             str = JSString::getUnitString(cx, str, size_t(i));
4513             if (!str)
4514                 goto error;
4515             regs.sp--;
4516             regs.sp[-1].setString(str);
4517             len = JSOP_GETELEM_LENGTH;
4518             DO_NEXT_OP(len);
4519         }
4520     }
4521
4522     JSObject *obj;
4523     VALUE_TO_OBJECT(cx, &lref, obj);
4524
4525     const Value *copyFrom;
4526     Value rval;
4527     jsid id;
4528     if (rref.isInt32()) {
4529         int32_t i = rref.toInt32();
4530         if (obj->isDenseArray()) {
4531             jsuint idx = jsuint(i);
4532
4533             if (idx < obj->getDenseArrayCapacity()) {
4534                 copyFrom = obj->addressOfDenseArrayElement(idx);
4535                 if (!copyFrom->isMagic())
4536                     goto end_getelem;
4537             }
4538         } else if (obj->isArguments()) {
4539             uint32 arg = uint32(i);
4540
4541             if (arg < obj->getArgsInitialLength()) {
4542                 copyFrom = obj->addressOfArgsElement(arg);
4543                 if (!copyFrom->isMagic(JS_ARGS_HOLE)) {
4544                     if (JSStackFrame *afp = (JSStackFrame *) obj->getPrivate())
4545                         copyFrom = &afp->canonicalActualArg(arg);
4546                     goto end_getelem;
4547                 }
4548             }
4549         }
4550         if (JS_LIKELY(INT_FITS_IN_JSID(i)))
4551             id = INT_TO_JSID(i);
4552         else
4553             goto intern_big_int;
4554     } else {
4555         int32_t i;
4556         if (ValueFitsInInt32(rref, &i) && INT_FITS_IN_JSID(i)) {
4557             id = INT_TO_JSID(i);
4558         } else {
4559           intern_big_int:
4560             if (!js_InternNonIntElementId(cx, obj, rref, &id))
4561                 goto error;
4562         }
4563     }
4564
4565     if (!obj->getProperty(cx, id, &rval))
4566         goto error;
4567     copyFrom = &rval;
4568
4569   end_getelem:
4570     regs.sp--;
4571     regs.sp[-1] = *copyFrom;
4572     assertSameCompartment(cx, regs.sp[-1]);
4573 }
4574 END_CASE(JSOP_GETELEM)
4575
4576 BEGIN_CASE(JSOP_CALLELEM)
4577 {
4578     /* Find the object on which to look for |this|'s properties. */
4579     Value thisv = regs.sp[-2];
4580     JSObject *thisObj = ValuePropertyBearer(cx, thisv, -2);
4581     if (!thisObj)
4582         goto error;
4583
4584     /* Fetch index and convert it to id suitable for use with obj. */
4585     jsid id;
4586     FETCH_ELEMENT_ID(thisObj, -1, id);
4587
4588     /* Get the method. */
4589     if (!js_GetMethod(cx, thisObj, id, JSGET_NO_METHOD_BARRIER, &regs.sp[-2]))
4590         goto error;
4591
4592 #if JS_HAS_NO_SUCH_METHOD
4593     if (JS_UNLIKELY(regs.sp[-2].isUndefined()) && thisv.isObject()) {
4594         /* For js_OnUnknownMethod, sp[-2] is the index, and sp[-1] is the object missing it. */
4595         regs.sp[-2] = regs.sp[-1];
4596         regs.sp[-1].setObject(*thisObj);
4597         if (!js_OnUnknownMethod(cx, regs.sp - 2))
4598             goto error;
4599     } else
4600 #endif
4601     {
4602         regs.sp[-1] = thisv;
4603     }
4604 }
4605 END_CASE(JSOP_CALLELEM)
4606
4607 BEGIN_CASE(JSOP_SETELEM)
4608 {
4609     JSObject *obj;
4610     FETCH_OBJECT(cx, -3, obj);
4611     jsid id;
4612     FETCH_ELEMENT_ID(obj, -2, id);
4613     Value rval;
4614     do {
4615         if (obj->isDenseArray() && JSID_IS_INT(id)) {
4616             jsuint length = obj->getDenseArrayCapacity();
4617             jsint i = JSID_TO_INT(id);
4618             if ((jsuint)i < length) {
4619                 if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
4620                     if (js_PrototypeHasIndexedProperties(cx, obj))
4621                         break;
4622                     if ((jsuint)i >= obj->getArrayLength())
4623                         obj->setArrayLength(i + 1);
4624                 }
4625                 obj->setDenseArrayElement(i, regs.sp[-1]);
4626                 goto end_setelem;
4627             }
4628         }
4629     } while (0);
4630     rval = regs.sp[-1];
4631     if (!obj->setProperty(cx, id, &rval, script->strictModeCode))
4632         goto error;
4633   end_setelem:;
4634 }
4635 END_SET_CASE_STORE_RVAL(JSOP_SETELEM, 3)
4636
4637 BEGIN_CASE(JSOP_ENUMELEM)
4638 {
4639     /* Funky: the value to set is under the [obj, id] pair. */
4640     JSObject *obj;
4641     FETCH_OBJECT(cx, -2, obj);
4642     jsid id;
4643     FETCH_ELEMENT_ID(obj, -1, id);
4644     Value rval = regs.sp[-3];
4645     if (!obj->setProperty(cx, id, &rval, script->strictModeCode))
4646         goto error;
4647     regs.sp -= 3;
4648 }
4649 END_CASE(JSOP_ENUMELEM)
4650
4651 { // begin block around calling opcodes
4652     JSFunction *newfun;
4653     JSObject *callee;
4654     uint32 flags;
4655     uintN argc;
4656     Value *vp;
4657
4658 BEGIN_CASE(JSOP_NEW)
4659 {
4660     /* Get immediate argc and find the constructor function. */
4661     argc = GET_ARGC(regs.pc);
4662     vp = regs.sp - (2 + argc);
4663     JS_ASSERT(vp >= regs.fp->base());
4664
4665     /*
4666      * Assign lval, callee, and newfun exactly as the code at inline_call: expects to
4667      * find them, to avoid nesting a js_Interpret call via js_InvokeConstructor.
4668      */
4669     if (IsFunctionObject(vp[0], &callee)) {
4670         newfun = callee->getFunctionPrivate();
4671         if (newfun->isInterpreted()) {
4672             if (newfun->u.i.script->isEmpty()) {
4673                 JSObject *obj2 = js_CreateThisForFunction(cx, callee);
4674                 if (!obj2)
4675                     goto error;
4676                 vp[0].setObject(*obj2);
4677                 regs.sp = vp + 1;
4678                 goto end_new;
4679             }
4680
4681             flags = JSFRAME_CONSTRUCTING;
4682             goto inline_call;
4683         }
4684     }
4685
4686     if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(vp, argc)))
4687         goto error;
4688     regs.sp = vp + 1;
4689     CHECK_INTERRUPT_HANDLER();
4690     TRACE_0(NativeCallComplete);
4691
4692   end_new:;
4693 }
4694 END_CASE(JSOP_NEW)
4695
4696 BEGIN_CASE(JSOP_EVAL)
4697 {
4698     argc = GET_ARGC(regs.pc);
4699     vp = regs.sp - (argc + 2);
4700
4701     if (!IsFunctionObject(*vp, &callee))
4702         goto call_using_invoke;
4703
4704     newfun = callee->getFunctionPrivate();
4705     if (!IsBuiltinEvalFunction(newfun))
4706         goto call_using_invoke;
4707
4708     if (!DirectEval(cx, newfun, argc, vp))
4709         goto error;
4710 }
4711 END_CASE(JSOP_EVAL)
4712
4713 BEGIN_CASE(JSOP_CALL)
4714 BEGIN_CASE(JSOP_FUNAPPLY)
4715 BEGIN_CASE(JSOP_FUNCALL)
4716 {
4717     argc = GET_ARGC(regs.pc);
4718     vp = regs.sp - (argc + 2);
4719
4720     if (IsFunctionObject(*vp, &callee)) {
4721         newfun = callee->getFunctionPrivate();
4722
4723         /* Clear frame flags since this is not a constructor call. */
4724         flags = 0;
4725         if (newfun->isInterpreted())
4726       inline_call:
4727         {
4728             JSScript *newscript = newfun->script();
4729             if (JS_UNLIKELY(newscript->isEmpty())) {
4730                 vp->setUndefined();
4731                 regs.sp = vp + 1;
4732                 goto end_call;
4733             }
4734
4735             /* Restrict recursion of lightweight functions. */
4736             if (JS_UNLIKELY(inlineCallCount >= JS_MAX_INLINE_CALL_COUNT)) {
4737                 js_ReportOverRecursed(cx);
4738                 goto error;
4739             }
4740
4741             /* Get pointer to new frame/slots, prepare arguments. */
4742             StackSpace &stack = cx->stack();
4743             JSStackFrame *newfp = stack.getInlineFrame(cx, regs.sp, argc, newfun,
4744                                                        newscript, &flags);
4745             if (JS_UNLIKELY(!newfp))
4746                 goto error;
4747
4748             /* Initialize frame, locals. */
4749             newfp->initCallFrame(cx, *callee, newfun, argc, flags);
4750             SetValueRangeToUndefined(newfp->slots(), newscript->nfixed);
4751
4752             /* Officially push the frame. */
4753             stack.pushInlineFrame(cx, newscript, newfp, &regs);
4754
4755             /* Refresh interpreter locals. */
4756             JS_ASSERT(newfp == regs.fp);
4757             script = newscript;
4758             argv = regs.fp->formalArgsEnd() - newfun->nargs;
4759             atoms = script->atomMap.vector;
4760
4761             /* Now that the new frame is rooted, maybe create a call object. */
4762             if (newfun->isHeavyweight() && !js_GetCallObject(cx, regs.fp))
4763                 goto error;
4764
4765             RESET_USE_METHODJIT();
4766             inlineCallCount++;
4767             JS_RUNTIME_METER(rt, inlineCalls);
4768
4769             TRACE_0(EnterFrame);
4770
4771             CHECK_INTERRUPT_HANDLER();
4772
4773 #ifdef JS_METHODJIT
4774             /* Try to ensure methods are method JIT'd.  */
4775             mjit::CompileRequest request = (interpMode == JSINTERP_NORMAL)
4776                                            ? mjit::CompileRequest_Interpreter
4777                                            : mjit::CompileRequest_JIT;
4778             mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp, request);
4779             if (status == mjit::Compile_Error)
4780                 goto error;
4781             if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && status == mjit::Compile_Okay) {
4782                 interpReturnOK = mjit::JaegerShot(cx);
4783                 CHECK_INTERRUPT_HANDLER();
4784                 goto jit_return;
4785             }
4786 #endif
4787
4788             if (!ScriptPrologue(cx, regs.fp))
4789                 goto error;
4790
4791             CHECK_INTERRUPT_HANDLER();
4792
4793             /* Load first op and dispatch it (safe since JSOP_STOP). */
4794             op = (JSOp) *regs.pc;
4795             DO_OP();
4796         }
4797
4798         Probes::enterJSFun(cx, newfun, script);
4799         JSBool ok = CallJSNative(cx, newfun->u.n.native, argc, vp);
4800         Probes::exitJSFun(cx, newfun, script);
4801         regs.sp = vp + 1;
4802         if (!ok)
4803             goto error;
4804         TRACE_0(NativeCallComplete);
4805         goto end_call;
4806     }
4807
4808   call_using_invoke:
4809     bool ok;
4810     ok = Invoke(cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0);
4811     regs.sp = vp + 1;
4812     CHECK_INTERRUPT_HANDLER();
4813     if (!ok)
4814         goto error;
4815     JS_RUNTIME_METER(rt, nonInlineCalls);
4816     TRACE_0(NativeCallComplete);
4817
4818   end_call:;
4819 }
4820 END_CASE(JSOP_CALL)
4821
4822 } // end block around calling opcodes
4823
4824 BEGIN_CASE(JSOP_SETCALL)
4825 {
4826     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_LEFTSIDE_OF_ASS);
4827     goto error;
4828 }
4829 END_CASE(JSOP_SETCALL)
4830
4831 #define PUSH_IMPLICIT_THIS(cx, obj, funval)                                   \
4832     JS_BEGIN_MACRO                                                            \
4833         Value v;                                                              \
4834         if (!ComputeImplicitThis(cx, obj, funval, &v))                        \
4835             goto error;                                                       \
4836         PUSH_COPY(v);                                                         \
4837     JS_END_MACRO                                                              \
4838
4839 BEGIN_CASE(JSOP_GETGNAME)
4840 BEGIN_CASE(JSOP_CALLGNAME)
4841 BEGIN_CASE(JSOP_NAME)
4842 BEGIN_CASE(JSOP_CALLNAME)
4843 {
4844     JSObject *obj = &regs.fp->scopeChain();
4845
4846     const Shape *shape;
4847     Value rval;
4848
4849     PropertyCacheEntry *entry;
4850     JSObject *obj2;
4851     JSAtom *atom;
4852     JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
4853     if (!atom) {
4854         ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
4855         if (entry->vword.isFunObj()) {
4856             PUSH_OBJECT(entry->vword.toFunObj());
4857         } else if (entry->vword.isSlot()) {
4858             uintN slot = entry->vword.toSlot();
4859             PUSH_COPY(obj2->nativeGetSlot(slot));
4860         } else {
4861             JS_ASSERT(entry->vword.isShape());
4862             shape = entry->vword.toShape();
4863             NATIVE_GET(cx, obj, obj2, shape, JSGET_METHOD_BARRIER, &rval);
4864             PUSH_COPY(rval);
4865         }
4866
4867         JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj));
4868         if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME)
4869             PUSH_IMPLICIT_THIS(cx, obj, regs.sp[-1]);
4870         len = JSOP_NAME_LENGTH;
4871         DO_NEXT_OP(len);
4872     }
4873
4874     jsid id;
4875     id = ATOM_TO_JSID(atom);
4876     JSProperty *prop;
4877     if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
4878         goto error;
4879     if (!prop) {
4880         /* Kludge to allow (typeof foo == "undefined") tests. */
4881         JSOp op2 = js_GetOpcode(cx, script, regs.pc + JSOP_NAME_LENGTH);
4882         if (op2 == JSOP_TYPEOF) {
4883             PUSH_UNDEFINED();
4884             len = JSOP_NAME_LENGTH;
4885             DO_NEXT_OP(len);
4886         }
4887         atomNotDefined = atom;
4888         goto atom_not_defined;
4889     }
4890
4891     /* Take the slow path if prop was not found in a native object. */
4892     if (!obj->isNative() || !obj2->isNative()) {
4893         if (!obj->getProperty(cx, id, &rval))
4894             goto error;
4895     } else {
4896         shape = (Shape *)prop;
4897         JSObject *normalized = obj;
4898         if (normalized->getClass() == &js_WithClass && !shape->hasDefaultGetter())
4899             normalized = js_UnwrapWithObject(cx, normalized);
4900         NATIVE_GET(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, &rval);
4901     }
4902
4903     PUSH_COPY(rval);
4904
4905     /* obj must be on the scope chain, thus not a function. */
4906     if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME)
4907         PUSH_IMPLICIT_THIS(cx, obj, rval);
4908 }
4909 END_CASE(JSOP_NAME)
4910
4911 BEGIN_CASE(JSOP_UINT16)
4912     PUSH_INT32((int32_t) GET_UINT16(regs.pc));
4913 END_CASE(JSOP_UINT16)
4914
4915 BEGIN_CASE(JSOP_UINT24)
4916     PUSH_INT32((int32_t) GET_UINT24(regs.pc));
4917 END_CASE(JSOP_UINT24)
4918
4919 BEGIN_CASE(JSOP_INT8)
4920     PUSH_INT32(GET_INT8(regs.pc));
4921 END_CASE(JSOP_INT8)
4922
4923 BEGIN_CASE(JSOP_INT32)
4924     PUSH_INT32(GET_INT32(regs.pc));
4925 END_CASE(JSOP_INT32)
4926
4927 BEGIN_CASE(JSOP_INDEXBASE)
4928     /*
4929      * Here atoms can exceed script->atomMap.length as we use atoms as a
4930      * segment register for object literals as well.
4931      */
4932     atoms += GET_INDEXBASE(regs.pc);
4933 END_CASE(JSOP_INDEXBASE)
4934
4935 BEGIN_CASE(JSOP_INDEXBASE1)
4936 BEGIN_CASE(JSOP_INDEXBASE2)
4937 BEGIN_CASE(JSOP_INDEXBASE3)
4938     atoms += (op - JSOP_INDEXBASE1 + 1) << 16;
4939 END_CASE(JSOP_INDEXBASE3)
4940
4941 BEGIN_CASE(JSOP_RESETBASE0)
4942 BEGIN_CASE(JSOP_RESETBASE)
4943     atoms = script->atomMap.vector;
4944 END_CASE(JSOP_RESETBASE)
4945
4946 BEGIN_CASE(JSOP_DOUBLE)
4947 {
4948     JS_ASSERT(!regs.fp->hasImacropc());
4949     double dbl;
4950     LOAD_DOUBLE(0, dbl);
4951     PUSH_DOUBLE(dbl);
4952 }
4953 END_CASE(JSOP_DOUBLE)
4954
4955 BEGIN_CASE(JSOP_STRING)
4956 {
4957     JSAtom *atom;
4958     LOAD_ATOM(0, atom);
4959     PUSH_STRING(ATOM_TO_STRING(atom));
4960 }
4961 END_CASE(JSOP_STRING)
4962
4963 BEGIN_CASE(JSOP_OBJECT)
4964 {
4965     JSObject *obj;
4966     LOAD_OBJECT(0, obj);
4967     PUSH_OBJECT(*obj);
4968 }
4969 END_CASE(JSOP_OBJECT)
4970
4971 BEGIN_CASE(JSOP_REGEXP)
4972 {
4973     /*
4974      * Push a regexp object cloned from the regexp literal object mapped by the
4975      * bytecode at pc. ES5 finally fixed this bad old ES3 design flaw which was
4976      * flouted by many browser-based implementations.
4977      *
4978      * We avoid the GetScopeChain call here and pass fp->scopeChain as
4979      * js_GetClassPrototype uses the latter only to locate the global.
4980      */
4981     jsatomid index = GET_FULL_INDEX(0);
4982     JSObject *proto;
4983     if (!js_GetClassPrototype(cx, &regs.fp->scopeChain(), JSProto_RegExp, &proto))
4984         goto error;
4985     JS_ASSERT(proto);
4986     JSObject *obj = js_CloneRegExpObject(cx, script->getRegExp(index), proto);
4987     if (!obj)
4988         goto error;
4989     PUSH_OBJECT(*obj);
4990 }
4991 END_CASE(JSOP_REGEXP)
4992
4993 BEGIN_CASE(JSOP_ZERO)
4994     PUSH_INT32(0);
4995 END_CASE(JSOP_ZERO)
4996
4997 BEGIN_CASE(JSOP_ONE)
4998     PUSH_INT32(1);
4999 END_CASE(JSOP_ONE)
5000
5001 BEGIN_CASE(JSOP_NULL)
5002     PUSH_NULL();
5003 END_CASE(JSOP_NULL)
5004
5005 BEGIN_CASE(JSOP_FALSE)
5006     PUSH_BOOLEAN(false);
5007 END_CASE(JSOP_FALSE)
5008
5009 BEGIN_CASE(JSOP_TRUE)
5010     PUSH_BOOLEAN(true);
5011 END_CASE(JSOP_TRUE)
5012
5013 {
5014 BEGIN_CASE(JSOP_TABLESWITCH)
5015 {
5016     jsbytecode *pc2 = regs.pc;
5017     len = GET_JUMP_OFFSET(pc2);
5018
5019     /*
5020      * ECMAv2+ forbids conversion of discriminant, so we will skip to the
5021      * default case if the discriminant isn't already an int jsval.  (This
5022      * opcode is emitted only for dense jsint-domain switches.)
5023      */
5024     const Value &rref = *--regs.sp;
5025     int32_t i;
5026     if (rref.isInt32()) {
5027         i = rref.toInt32();
5028     } else {
5029         double d;
5030         /* Don't use JSDOUBLE_IS_INT32; treat -0 (double) as 0. */
5031         if (!rref.isDouble() || (d = rref.toDouble()) != (i = int32_t(rref.toDouble())))
5032             DO_NEXT_OP(len);
5033     }
5034
5035     pc2 += JUMP_OFFSET_LEN;
5036     jsint low = GET_JUMP_OFFSET(pc2);
5037     pc2 += JUMP_OFFSET_LEN;
5038     jsint high = GET_JUMP_OFFSET(pc2);
5039
5040     i -= low;
5041     if ((jsuint)i < (jsuint)(high - low + 1)) {
5042         pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i;
5043         jsint off = (jsint) GET_JUMP_OFFSET(pc2);
5044         if (off)
5045             len = off;
5046     }
5047 }
5048 END_VARLEN_CASE
5049 }
5050
5051 {
5052 BEGIN_CASE(JSOP_TABLESWITCHX)
5053 {
5054     jsbytecode *pc2 = regs.pc;
5055     len = GET_JUMPX_OFFSET(pc2);
5056
5057     /*
5058      * ECMAv2+ forbids conversion of discriminant, so we will skip to the
5059      * default case if the discriminant isn't already an int jsval.  (This
5060      * opcode is emitted only for dense jsint-domain switches.)
5061      */
5062     const Value &rref = *--regs.sp;
5063     int32_t i;
5064     if (rref.isInt32()) {
5065         i = rref.toInt32();
5066     } else if (rref.isDouble() && rref.toDouble() == 0) {
5067         /* Treat -0 (double) as 0. */
5068         i = 0;
5069     } else {
5070         DO_NEXT_OP(len);
5071     }
5072
5073     pc2 += JUMPX_OFFSET_LEN;
5074     jsint low = GET_JUMP_OFFSET(pc2);
5075     pc2 += JUMP_OFFSET_LEN;
5076     jsint high = GET_JUMP_OFFSET(pc2);
5077
5078     i -= low;
5079     if ((jsuint)i < (jsuint)(high - low + 1)) {
5080         pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i;
5081         jsint off = (jsint) GET_JUMPX_OFFSET(pc2);
5082         if (off)
5083             len = off;
5084     }
5085 }
5086 END_VARLEN_CASE
5087 }
5088
5089 {
5090 BEGIN_CASE(JSOP_LOOKUPSWITCHX)
5091 {
5092     jsint off;
5093     off = JUMPX_OFFSET_LEN;
5094     goto do_lookup_switch;
5095
5096 BEGIN_CASE(JSOP_LOOKUPSWITCH)
5097     off = JUMP_OFFSET_LEN;
5098
5099   do_lookup_switch:
5100     /*
5101      * JSOP_LOOKUPSWITCH and JSOP_LOOKUPSWITCHX are never used if any atom
5102      * index in it would exceed 64K limit.
5103      */
5104     JS_ASSERT(!regs.fp->hasImacropc());
5105     JS_ASSERT(atoms == script->atomMap.vector);
5106     jsbytecode *pc2 = regs.pc;
5107
5108     Value lval = regs.sp[-1];
5109     regs.sp--;
5110
5111     if (!lval.isPrimitive())
5112         goto end_lookup_switch;
5113
5114     pc2 += off;
5115     jsint npairs;
5116     npairs = (jsint) GET_UINT16(pc2);
5117     pc2 += UINT16_LEN;
5118     JS_ASSERT(npairs);  /* empty switch uses JSOP_TABLESWITCH */
5119
5120     bool match;
5121 #define SEARCH_PAIRS(MATCH_CODE)                                              \
5122     for (;;) {                                                                \
5123         Value rval = script->getConst(GET_INDEX(pc2));                        \
5124         MATCH_CODE                                                            \
5125         pc2 += INDEX_LEN;                                                     \
5126         if (match)                                                            \
5127             break;                                                            \
5128         pc2 += off;                                                           \
5129         if (--npairs == 0) {                                                  \
5130             pc2 = regs.pc;                                                    \
5131             break;                                                            \
5132         }                                                                     \
5133     }
5134
5135     if (lval.isString()) {
5136         JSLinearString *str = lval.toString()->ensureLinear(cx);
5137         if (!str)
5138             goto error;
5139         JSLinearString *str2;
5140         SEARCH_PAIRS(
5141             match = (rval.isString() &&
5142                      ((str2 = rval.toString()->assertIsLinear()) == str ||
5143                       EqualStrings(str2, str)));
5144         )
5145     } else if (lval.isNumber()) {
5146         double ldbl = lval.toNumber();
5147         SEARCH_PAIRS(
5148             match = rval.isNumber() && ldbl == rval.toNumber();
5149         )
5150     } else {
5151         SEARCH_PAIRS(
5152             match = (lval == rval);
5153         )
5154     }
5155 #undef SEARCH_PAIRS
5156
5157   end_lookup_switch:
5158     len = (op == JSOP_LOOKUPSWITCH)
5159           ? GET_JUMP_OFFSET(pc2)
5160           : GET_JUMPX_OFFSET(pc2);
5161 }
5162 END_VARLEN_CASE
5163 }
5164
5165 BEGIN_CASE(JSOP_TRAP)
5166 {
5167     Value rval;
5168     JSTrapStatus status = JS_HandleTrap(cx, script, regs.pc, Jsvalify(&rval));
5169     switch (status) {
5170       case JSTRAP_ERROR:
5171         goto error;
5172       case JSTRAP_RETURN:
5173         regs.fp->setReturnValue(rval);
5174         interpReturnOK = JS_TRUE;
5175         goto forced_return;
5176       case JSTRAP_THROW:
5177         cx->setPendingException(rval);
5178         goto error;
5179       default:
5180         break;
5181     }
5182     JS_ASSERT(status == JSTRAP_CONTINUE);
5183     CHECK_INTERRUPT_HANDLER();
5184     JS_ASSERT(rval.isInt32());
5185     op = (JSOp) rval.toInt32();
5186     JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
5187     DO_OP();
5188 }
5189
5190 BEGIN_CASE(JSOP_ARGUMENTS)
5191 {
5192     Value rval;
5193     if (!js_GetArgsValue(cx, regs.fp, &rval))
5194         goto error;
5195     PUSH_COPY(rval);
5196 }
5197 END_CASE(JSOP_ARGUMENTS)
5198
5199 BEGIN_CASE(JSOP_ARGSUB)
5200 {
5201     jsid id = INT_TO_JSID(GET_ARGNO(regs.pc));
5202     Value rval;
5203     if (!js_GetArgsProperty(cx, regs.fp, id, &rval))
5204         goto error;
5205     PUSH_COPY(rval);
5206 }
5207 END_CASE(JSOP_ARGSUB)
5208
5209 BEGIN_CASE(JSOP_ARGCNT)
5210 {
5211     jsid id = ATOM_TO_JSID(rt->atomState.lengthAtom);
5212     Value rval;
5213     if (!js_GetArgsProperty(cx, regs.fp, id, &rval))
5214         goto error;
5215     PUSH_COPY(rval);
5216 }
5217 END_CASE(JSOP_ARGCNT)
5218
5219 BEGIN_CASE(JSOP_GETARG)
5220 BEGIN_CASE(JSOP_CALLARG)
5221 {
5222     uint32 slot = GET_ARGNO(regs.pc);
5223     JS_ASSERT(slot < regs.fp->numFormalArgs());
5224     METER_SLOT_OP(op, slot);
5225     PUSH_COPY(argv[slot]);
5226     if (op == JSOP_CALLARG)
5227         PUSH_UNDEFINED();
5228 }
5229 END_CASE(JSOP_GETARG)
5230
5231 BEGIN_CASE(JSOP_SETARG)
5232 {
5233     uint32 slot = GET_ARGNO(regs.pc);
5234     JS_ASSERT(slot < regs.fp->numFormalArgs());
5235     METER_SLOT_OP(op, slot);
5236     argv[slot] = regs.sp[-1];
5237 }
5238 END_SET_CASE(JSOP_SETARG)
5239
5240 BEGIN_CASE(JSOP_GETLOCAL)
5241 {
5242     uint32 slot = GET_SLOTNO(regs.pc);
5243     JS_ASSERT(slot < script->nslots);
5244     PUSH_COPY(regs.fp->slots()[slot]);
5245 }
5246 END_CASE(JSOP_GETLOCAL)
5247
5248 BEGIN_CASE(JSOP_CALLLOCAL)
5249 {
5250     uint32 slot = GET_SLOTNO(regs.pc);
5251     JS_ASSERT(slot < script->nslots);
5252     PUSH_COPY(regs.fp->slots()[slot]);
5253     PUSH_UNDEFINED();
5254 }
5255 END_CASE(JSOP_CALLLOCAL)
5256
5257 BEGIN_CASE(JSOP_SETLOCAL)
5258 {
5259     uint32 slot = GET_SLOTNO(regs.pc);
5260     JS_ASSERT(slot < script->nslots);
5261     regs.fp->slots()[slot] = regs.sp[-1];
5262 }
5263 END_SET_CASE(JSOP_SETLOCAL)
5264
5265 BEGIN_CASE(JSOP_GETUPVAR_DBG)
5266 BEGIN_CASE(JSOP_CALLUPVAR_DBG)
5267 {
5268     JSFunction *fun = regs.fp->fun();
5269     JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
5270     JS_ASSERT(fun->u.i.wrapper);
5271
5272     /* Scope for tempPool mark and local names allocation in it. */
5273     JSObject *obj, *obj2;
5274     JSProperty *prop;
5275     jsid id;
5276     JSAtom *atom;
5277     {
5278         AutoLocalNameArray names(cx, fun);
5279         if (!names)
5280             goto error;
5281
5282         uintN index = fun->script()->bindings.countArgsAndVars() + GET_UINT16(regs.pc);
5283         atom = JS_LOCAL_NAME_TO_ATOM(names[index]);
5284         id = ATOM_TO_JSID(atom);
5285
5286         if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
5287             goto error;
5288     }
5289
5290     if (!prop) {
5291         atomNotDefined = atom;
5292         goto atom_not_defined;
5293     }
5294
5295     /* Minimize footprint with generic code instead of NATIVE_GET. */
5296     Value *vp = regs.sp;
5297     PUSH_NULL();
5298     if (!obj->getProperty(cx, id, vp))
5299         goto error;
5300
5301     if (op == JSOP_CALLUPVAR_DBG)
5302         PUSH_UNDEFINED();
5303 }
5304 END_CASE(JSOP_GETUPVAR_DBG)
5305
5306 BEGIN_CASE(JSOP_GETFCSLOT)
5307 BEGIN_CASE(JSOP_CALLFCSLOT)
5308 {
5309     JS_ASSERT(regs.fp->isFunctionFrame() && !regs.fp->isEvalFrame());
5310     uintN index = GET_UINT16(regs.pc);
5311     JSObject *obj = &argv[-2].toObject();
5312
5313     JS_ASSERT(index < obj->getFunctionPrivate()->script()->bindings.countUpvars());
5314     PUSH_COPY(obj->getFlatClosureUpvar(index));
5315     if (op == JSOP_CALLFCSLOT)
5316         PUSH_UNDEFINED();
5317 }
5318 END_CASE(JSOP_GETFCSLOT)
5319
5320 BEGIN_CASE(JSOP_GETGLOBAL)
5321 BEGIN_CASE(JSOP_CALLGLOBAL)
5322 {
5323     uint32 slot = GET_SLOTNO(regs.pc);
5324     slot = script->getGlobalSlot(slot);
5325     JSObject *obj = regs.fp->scopeChain().getGlobal();
5326     JS_ASSERT(obj->containsSlot(slot));
5327     PUSH_COPY(obj->getSlot(slot));
5328     if (op == JSOP_CALLGLOBAL)
5329         PUSH_UNDEFINED();
5330 }
5331 END_CASE(JSOP_GETGLOBAL)
5332
5333 BEGIN_CASE(JSOP_DEFCONST)
5334 BEGIN_CASE(JSOP_DEFVAR)
5335 {
5336     uint32 index = GET_INDEX(regs.pc);
5337     JSAtom *atom = atoms[index];
5338
5339     JSObject *obj = &regs.fp->varobj(cx);
5340     JS_ASSERT(!obj->getOps()->defineProperty);
5341     uintN attrs = JSPROP_ENUMERATE;
5342     if (!regs.fp->isEvalFrame())
5343         attrs |= JSPROP_PERMANENT;
5344
5345     /* Lookup id in order to check for redeclaration problems. */
5346     jsid id = ATOM_TO_JSID(atom);
5347     bool shouldDefine;
5348     if (op == JSOP_DEFVAR) {
5349         /*
5350          * Redundant declaration of a |var|, even one for a non-writable
5351          * property like |undefined| in ES5, does nothing.
5352          */
5353         JSProperty *prop;
5354         JSObject *obj2;
5355         if (!obj->lookupProperty(cx, id, &obj2, &prop))
5356             goto error;
5357         shouldDefine = (!prop || obj2 != obj);
5358     } else {
5359         JS_ASSERT(op == JSOP_DEFCONST);
5360         attrs |= JSPROP_READONLY;
5361         if (!CheckRedeclaration(cx, obj, id, attrs))
5362             goto error;
5363
5364         /*
5365          * As attrs includes readonly, CheckRedeclaration can succeed only
5366          * if prop does not exist.
5367          */
5368         shouldDefine = true;
5369     }
5370
5371     /* Bind a variable only if it's not yet defined. */
5372     if (shouldDefine &&
5373         !js_DefineNativeProperty(cx, obj, id, UndefinedValue(),
5374                                  PropertyStub, StrictPropertyStub, attrs, 0, 0, NULL)) {
5375         goto error;
5376     }
5377 }
5378 END_CASE(JSOP_DEFVAR)
5379
5380 BEGIN_CASE(JSOP_DEFFUN)
5381 {
5382     /*
5383      * A top-level function defined in Global or Eval code (see ECMA-262
5384      * Ed. 3), or else a SpiderMonkey extension: a named function statement in
5385      * a compound statement (not at the top statement level of global code, or
5386      * at the top level of a function body).
5387      */
5388     JSFunction *fun;
5389     LOAD_FUNCTION(0);
5390     JSObject *obj = FUN_OBJECT(fun);
5391
5392     JSObject *obj2;
5393     if (FUN_NULL_CLOSURE(fun)) {
5394         /*
5395          * Even a null closure needs a parent for principals finding.
5396          * FIXME: bug 476950, although debugger users may also demand some kind
5397          * of scope link for debugger-assisted eval-in-frame.
5398          */
5399         obj2 = &regs.fp->scopeChain();
5400     } else {
5401         JS_ASSERT(!fun->isFlatClosure());
5402
5403         obj2 = GetScopeChainFast(cx, regs.fp, JSOP_DEFFUN, JSOP_DEFFUN_LENGTH);
5404         if (!obj2)
5405             goto error;
5406     }
5407
5408     /*
5409      * If static link is not current scope, clone fun's object to link to the
5410      * current scope via parent. We do this to enable sharing of compiled
5411      * functions among multiple equivalent scopes, amortizing the cost of
5412      * compilation over a number of executions.  Examples include XUL scripts
5413      * and event handlers shared among Firefox or other Mozilla app chrome
5414      * windows, and user-defined JS functions precompiled and then shared among
5415      * requests in server-side JS.
5416      */
5417     if (obj->getParent() != obj2) {
5418         obj = CloneFunctionObject(cx, fun, obj2);
5419         if (!obj)
5420             goto error;
5421     }
5422
5423     /*
5424      * ECMA requires functions defined when entering Eval code to be
5425      * impermanent.
5426      */
5427     uintN attrs = regs.fp->isEvalFrame()
5428                   ? JSPROP_ENUMERATE
5429                   : JSPROP_ENUMERATE | JSPROP_PERMANENT;
5430
5431     /*
5432      * We define the function as a property of the variable object and not the
5433      * current scope chain even for the case of function expression statements
5434      * and functions defined by eval inside let or with blocks.
5435      */
5436     JSObject *parent = &regs.fp->varobj(cx);
5437
5438     /* ES5 10.5 (NB: with subsequent errata). */
5439     jsid id = ATOM_TO_JSID(fun->atom);
5440     JSProperty *prop = NULL;
5441     JSObject *pobj;
5442     if (!parent->lookupProperty(cx, id, &pobj, &prop))
5443         goto error;
5444
5445     Value rval = ObjectValue(*obj);
5446
5447     do {
5448         /* Steps 5d, 5f. */
5449         if (!prop || pobj != parent) {
5450             if (!parent->defineProperty(cx, id, rval, PropertyStub, StrictPropertyStub, attrs))
5451                 goto error;
5452             break;
5453         }
5454
5455         /* Step 5e. */
5456         JS_ASSERT(parent->isNative());
5457         Shape *shape = reinterpret_cast<Shape *>(prop);
5458         if (parent->isGlobal()) {
5459             if (shape->configurable()) {
5460                 if (!parent->defineProperty(cx, id, rval, PropertyStub, StrictPropertyStub, attrs))
5461                     goto error;
5462                 break;
5463             }
5464
5465             if (shape->isAccessorDescriptor() || !shape->writable() || !shape->enumerable()) {
5466                 JSAutoByteString bytes;
5467                 if (const char *name = js_ValueToPrintable(cx, IdToValue(id), &bytes)) {
5468                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5469                                          JSMSG_CANT_REDEFINE_PROP, name);
5470                 }
5471                 goto error;
5472             }
5473         }
5474
5475         /*
5476          * Non-global properties, and global properties which we aren't simply
5477          * redefining, must be set.  First, this preserves their attributes.
5478          * Second, this will produce warnings and/or errors as necessary if the
5479          * specified Call object property is not writable (const).
5480          */
5481
5482         /* Step 5f. */
5483         if (!parent->setProperty(cx, id, &rval, script->strictModeCode))
5484             goto error;
5485     } while (false);
5486 }
5487 END_CASE(JSOP_DEFFUN)
5488
5489 BEGIN_CASE(JSOP_DEFFUN_FC)
5490 BEGIN_CASE(JSOP_DEFFUN_DBGFC)
5491 {
5492     JSFunction *fun;
5493     LOAD_FUNCTION(0);
5494
5495     JSObject *obj = (op == JSOP_DEFFUN_FC)
5496                     ? js_NewFlatClosure(cx, fun, JSOP_DEFFUN_FC, JSOP_DEFFUN_FC_LENGTH)
5497                     : js_NewDebuggableFlatClosure(cx, fun);
5498     if (!obj)
5499         goto error;
5500
5501     Value rval = ObjectValue(*obj);
5502
5503     uintN attrs = regs.fp->isEvalFrame()
5504                   ? JSPROP_ENUMERATE
5505                   : JSPROP_ENUMERATE | JSPROP_PERMANENT;
5506
5507     JSObject &parent = regs.fp->varobj(cx);
5508
5509     jsid id = ATOM_TO_JSID(fun->atom);
5510     if (!CheckRedeclaration(cx, &parent, id, attrs))
5511         goto error;
5512
5513     if ((attrs == JSPROP_ENUMERATE)
5514         ? !parent.setProperty(cx, id, &rval, script->strictModeCode)
5515         : !parent.defineProperty(cx, id, rval, PropertyStub, StrictPropertyStub, attrs)) {
5516         goto error;
5517     }
5518 }
5519 END_CASE(JSOP_DEFFUN_FC)
5520
5521 BEGIN_CASE(JSOP_DEFLOCALFUN)
5522 {
5523     /*
5524      * Define a local function (i.e., one nested at the top level of another
5525      * function), parented by the current scope chain, stored in a local
5526      * variable slot that the compiler allocated.  This is an optimization over
5527      * JSOP_DEFFUN that avoids requiring a call object for the outer function's
5528      * activation.
5529      */
5530     JSFunction *fun;
5531     LOAD_FUNCTION(SLOTNO_LEN);
5532     JS_ASSERT(fun->isInterpreted());
5533     JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
5534     JSObject *obj = FUN_OBJECT(fun);
5535
5536     if (FUN_NULL_CLOSURE(fun)) {
5537         obj = CloneFunctionObject(cx, fun, &regs.fp->scopeChain());
5538         if (!obj)
5539             goto error;
5540     } else {
5541         JSObject *parent = GetScopeChainFast(cx, regs.fp, JSOP_DEFLOCALFUN,
5542                                              JSOP_DEFLOCALFUN_LENGTH);
5543         if (!parent)
5544             goto error;
5545
5546         if (obj->getParent() != parent) {
5547 #ifdef JS_TRACER
5548             if (TRACE_RECORDER(cx))
5549                 AbortRecording(cx, "DEFLOCALFUN for closure");
5550 #endif
5551             obj = CloneFunctionObject(cx, fun, parent);
5552             if (!obj)
5553                 goto error;
5554         }
5555     }
5556
5557     uint32 slot = GET_SLOTNO(regs.pc);
5558     TRACE_2(DefLocalFunSetSlot, slot, obj);
5559
5560     regs.fp->slots()[slot].setObject(*obj);
5561 }
5562 END_CASE(JSOP_DEFLOCALFUN)
5563
5564 BEGIN_CASE(JSOP_DEFLOCALFUN_FC)
5565 {
5566     JSFunction *fun;
5567     LOAD_FUNCTION(SLOTNO_LEN);
5568
5569     JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_DEFLOCALFUN_FC, JSOP_DEFLOCALFUN_FC_LENGTH);
5570     if (!obj)
5571         goto error;
5572
5573     uint32 slot = GET_SLOTNO(regs.pc);
5574     TRACE_2(DefLocalFunSetSlot, slot, obj);
5575
5576     regs.fp->slots()[slot].setObject(*obj);
5577 }
5578 END_CASE(JSOP_DEFLOCALFUN_FC)
5579
5580 BEGIN_CASE(JSOP_DEFLOCALFUN_DBGFC)
5581 {
5582     JSFunction *fun;
5583     LOAD_FUNCTION(SLOTNO_LEN);
5584
5585     JSObject *obj = js_NewDebuggableFlatClosure(cx, fun);
5586     if (!obj)
5587         goto error;
5588
5589     uint32 slot = GET_SLOTNO(regs.pc);
5590     regs.fp->slots()[slot].setObject(*obj);
5591 }
5592 END_CASE(JSOP_DEFLOCALFUN_DBGFC)
5593
5594 BEGIN_CASE(JSOP_LAMBDA)
5595 {
5596     /* Load the specified function object literal. */
5597     JSFunction *fun;
5598     LOAD_FUNCTION(0);
5599     JSObject *obj = FUN_OBJECT(fun);
5600
5601     /* do-while(0) so we can break instead of using a goto. */
5602     do {
5603         JSObject *parent;
5604         if (FUN_NULL_CLOSURE(fun)) {
5605             parent = &regs.fp->scopeChain();
5606
5607             if (obj->getParent() == parent) {
5608                 jsbytecode *pc2 = AdvanceOverBlockchainOp(regs.pc + JSOP_LAMBDA_LENGTH);
5609                 JSOp op2 = JSOp(*pc2);
5610
5611                 /*
5612                  * Optimize var obj = {method: function () { ... }, ...},
5613                  * this.method = function () { ... }; and other significant
5614                  * single-use-of-null-closure bytecode sequences.
5615                  *
5616                  * WARNING: code in TraceRecorder::record_JSOP_LAMBDA must
5617                  * match the optimization cases in the following code that
5618                  * break from the outer do-while(0).
5619                  */
5620                 if (op2 == JSOP_INITMETHOD) {
5621 #ifdef DEBUG
5622                     const Value &lref = regs.sp[-1];
5623                     JS_ASSERT(lref.isObject());
5624                     JSObject *obj2 = &lref.toObject();
5625                     JS_ASSERT(obj2->getClass() == &js_ObjectClass);
5626 #endif
5627
5628                     fun->setMethodAtom(script->getAtom(GET_FULL_INDEX(pc2 - regs.pc)));
5629                     JS_FUNCTION_METER(cx, joinedinitmethod);
5630                     break;
5631                 }
5632
5633                 if (op2 == JSOP_SETMETHOD) {
5634 #ifdef DEBUG
5635                     op2 = JSOp(pc2[JSOP_SETMETHOD_LENGTH]);
5636                     JS_ASSERT(op2 == JSOP_POP || op2 == JSOP_POPV);
5637 #endif
5638                     const Value &lref = regs.sp[-1];
5639                     if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) {
5640                         fun->setMethodAtom(script->getAtom(GET_FULL_INDEX(pc2 - regs.pc)));
5641                         JS_FUNCTION_METER(cx, joinedsetmethod);
5642                         break;
5643                     }
5644                 } else if (fun->joinable()) {
5645                     if (op2 == JSOP_CALL) {
5646                         /*
5647                          * Array.prototype.sort and String.prototype.replace are
5648                          * optimized as if they are special form. We know that they
5649                          * won't leak the joined function object in obj, therefore
5650                          * we don't need to clone that compiler- created function
5651                          * object for identity/mutation reasons.
5652                          */
5653                         int iargc = GET_ARGC(pc2);
5654
5655                         /*
5656                          * Note that we have not yet pushed obj as the final argument,
5657                          * so regs.sp[1 - (iargc + 2)], and not regs.sp[-(iargc + 2)],
5658                          * is the callee for this JSOP_CALL.
5659                          */
5660                         const Value &cref = regs.sp[1 - (iargc + 2)];
5661                         JSObject *callee;
5662
5663                         if (IsFunctionObject(cref, &callee)) {
5664                             JSFunction *calleeFun = GET_FUNCTION_PRIVATE(cx, callee);
5665                             if (Native native = calleeFun->maybeNative()) {
5666                                 if (iargc == 1 && native == array_sort) {
5667                                     JS_FUNCTION_METER(cx, joinedsort);
5668                                     break;
5669                                 }
5670                                 if (iargc == 2 && native == str_replace) {
5671                                     JS_FUNCTION_METER(cx, joinedreplace);
5672                                     break;
5673                                 }
5674                             }
5675                         }
5676                     } else if (op2 == JSOP_NULL) {
5677                         pc2 += JSOP_NULL_LENGTH;
5678                         op2 = JSOp(*pc2);
5679
5680                         if (op2 == JSOP_CALL && GET_ARGC(pc2) == 0) {
5681                             JS_FUNCTION_METER(cx, joinedmodulepat);
5682                             break;
5683                         }
5684                     }
5685                 }
5686             }
5687
5688 #ifdef DEBUG
5689             if (rt->functionMeterFilename) {
5690                 // No locking, this is mainly for js shell testing.
5691                 ++rt->functionMeter.unjoined;
5692
5693                 typedef JSRuntime::FunctionCountMap HM;
5694                 HM &h = rt->unjoinedFunctionCountMap;
5695                 HM::AddPtr p = h.lookupForAdd(fun);
5696                 if (!p) {
5697                     h.add(p, fun, 1);
5698                 } else {
5699                     JS_ASSERT(p->key == fun);
5700                     ++p->value;
5701                 }
5702             }
5703 #endif
5704         } else {
5705             parent = GetScopeChainFast(cx, regs.fp, JSOP_LAMBDA, JSOP_LAMBDA_LENGTH);
5706             if (!parent)
5707                 goto error;
5708         }
5709
5710         obj = CloneFunctionObject(cx, fun, parent);
5711         if (!obj)
5712             goto error;
5713     } while (0);
5714
5715     PUSH_OBJECT(*obj);
5716 }
5717 END_CASE(JSOP_LAMBDA)
5718
5719 BEGIN_CASE(JSOP_LAMBDA_FC)
5720 {
5721     JSFunction *fun;
5722     LOAD_FUNCTION(0);
5723
5724     JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH);
5725     if (!obj)
5726         goto error;
5727
5728     PUSH_OBJECT(*obj);
5729 }
5730 END_CASE(JSOP_LAMBDA_FC)
5731
5732 BEGIN_CASE(JSOP_LAMBDA_DBGFC)
5733 {
5734     JSFunction *fun;
5735     LOAD_FUNCTION(0);
5736
5737     JSObject *obj = js_NewDebuggableFlatClosure(cx, fun);
5738     if (!obj)
5739         goto error;
5740
5741     PUSH_OBJECT(*obj);
5742 }
5743 END_CASE(JSOP_LAMBDA_DBGFC)
5744
5745 BEGIN_CASE(JSOP_CALLEE)
5746     JS_ASSERT(regs.fp->isFunctionFrame() && !regs.fp->isEvalFrame());
5747     PUSH_COPY(argv[-2]);
5748 END_CASE(JSOP_CALLEE)
5749
5750 BEGIN_CASE(JSOP_GETTER)
5751 BEGIN_CASE(JSOP_SETTER)
5752 {
5753   do_getter_setter:
5754     JSOp op2 = (JSOp) *++regs.pc;
5755     jsid id;
5756     Value rval;
5757     jsint i;
5758     JSObject *obj;
5759     switch (op2) {
5760       case JSOP_INDEXBASE:
5761         atoms += GET_INDEXBASE(regs.pc);
5762         regs.pc += JSOP_INDEXBASE_LENGTH - 1;
5763         goto do_getter_setter;
5764       case JSOP_INDEXBASE1:
5765       case JSOP_INDEXBASE2:
5766       case JSOP_INDEXBASE3:
5767         atoms += (op2 - JSOP_INDEXBASE1 + 1) << 16;
5768         goto do_getter_setter;
5769
5770       case JSOP_SETNAME:
5771       case JSOP_SETPROP:
5772       {
5773         JSAtom *atom;
5774         LOAD_ATOM(0, atom);
5775         id = ATOM_TO_JSID(atom);
5776         rval = regs.sp[-1];
5777         i = -1;
5778         goto gs_pop_lval;
5779       }
5780       case JSOP_SETELEM:
5781         rval = regs.sp[-1];
5782         id = JSID_VOID;
5783         i = -2;
5784       gs_pop_lval:
5785         FETCH_OBJECT(cx, i - 1, obj);
5786         break;
5787
5788       case JSOP_INITPROP:
5789       {
5790         JS_ASSERT(regs.sp - regs.fp->base() >= 2);
5791         rval = regs.sp[-1];
5792         i = -1;
5793         JSAtom *atom;
5794         LOAD_ATOM(0, atom);
5795         id = ATOM_TO_JSID(atom);
5796         goto gs_get_lval;
5797       }
5798       default:
5799         JS_ASSERT(op2 == JSOP_INITELEM);
5800
5801         JS_ASSERT(regs.sp - regs.fp->base() >= 3);
5802         rval = regs.sp[-1];
5803         id = JSID_VOID;
5804         i = -2;
5805       gs_get_lval:
5806       {
5807         const Value &lref = regs.sp[i-1];
5808         JS_ASSERT(lref.isObject());
5809         obj = &lref.toObject();
5810         break;
5811       }
5812     }
5813
5814     /* Ensure that id has a type suitable for use with obj. */
5815     if (JSID_IS_VOID(id))
5816         FETCH_ELEMENT_ID(obj, i, id);
5817
5818     if (!js_IsCallable(rval)) {
5819         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5820                              JSMSG_BAD_GETTER_OR_SETTER,
5821                              (op == JSOP_GETTER)
5822                              ? js_getter_str
5823                              : js_setter_str);
5824         goto error;
5825     }
5826
5827     /*
5828      * Getters and setters are just like watchpoints from an access control
5829      * point of view.
5830      */
5831     Value rtmp;
5832     uintN attrs;
5833     if (!CheckAccess(cx, obj, id, JSACC_WATCH, &rtmp, &attrs))
5834         goto error;
5835
5836     PropertyOp getter;
5837     StrictPropertyOp setter;
5838     if (op == JSOP_GETTER) {
5839         getter = CastAsPropertyOp(&rval.toObject());
5840         setter = StrictPropertyStub;
5841         attrs = JSPROP_GETTER;
5842     } else {
5843         getter = PropertyStub;
5844         setter = CastAsStrictPropertyOp(&rval.toObject());
5845         attrs = JSPROP_SETTER;
5846     }
5847     attrs |= JSPROP_ENUMERATE | JSPROP_SHARED;
5848
5849     /* Check for a readonly or permanent property of the same name. */
5850     if (!CheckRedeclaration(cx, obj, id, attrs))
5851         goto error;
5852
5853     if (!obj->defineProperty(cx, id, UndefinedValue(), getter, setter, attrs))
5854         goto error;
5855
5856     regs.sp += i;
5857     if (js_CodeSpec[op2].ndefs > js_CodeSpec[op2].nuses) {
5858         JS_ASSERT(js_CodeSpec[op2].ndefs == js_CodeSpec[op2].nuses + 1);
5859         regs.sp[-1] = rval;
5860         assertSameCompartment(cx, regs.sp[-1]);
5861     }
5862     len = js_CodeSpec[op2].length;
5863     DO_NEXT_OP(len);
5864 }
5865
5866 BEGIN_CASE(JSOP_HOLE)
5867     PUSH_HOLE();
5868 END_CASE(JSOP_HOLE)
5869
5870 BEGIN_CASE(JSOP_NEWINIT)
5871 {
5872     jsint i = regs.pc[1];
5873
5874     JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
5875     JSObject *obj;
5876
5877     if (i == JSProto_Array) {
5878         obj = NewDenseEmptyArray(cx);
5879     } else {
5880         gc::FinalizeKind kind = GuessObjectGCKind(0, false);
5881         obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
5882     }
5883
5884     if (!obj)
5885         goto error;
5886
5887     PUSH_OBJECT(*obj);
5888     CHECK_INTERRUPT_HANDLER();
5889 }
5890 END_CASE(JSOP_NEWINIT)
5891
5892 BEGIN_CASE(JSOP_NEWARRAY)
5893 {
5894     unsigned count = GET_UINT24(regs.pc);
5895     JSObject *obj = NewDenseAllocatedArray(cx, count);
5896     if (!obj)
5897         goto error;
5898
5899     PUSH_OBJECT(*obj);
5900     CHECK_INTERRUPT_HANDLER();
5901 }
5902 END_CASE(JSOP_NEWARRAY)
5903
5904 BEGIN_CASE(JSOP_NEWOBJECT)
5905 {
5906     JSObject *baseobj;
5907     LOAD_OBJECT(0, baseobj);
5908
5909     JSObject *obj = CopyInitializerObject(cx, baseobj);
5910
5911     if (!obj)
5912         goto error;
5913
5914     PUSH_OBJECT(*obj);
5915     CHECK_INTERRUPT_HANDLER();
5916 }
5917 END_CASE(JSOP_NEWOBJECT)
5918
5919 BEGIN_CASE(JSOP_ENDINIT)
5920 {
5921     /* FIXME remove JSOP_ENDINIT bug 588522 */
5922     JS_ASSERT(regs.sp - regs.fp->base() >= 1);
5923     JS_ASSERT(regs.sp[-1].isObject());
5924 }
5925 END_CASE(JSOP_ENDINIT)
5926
5927 BEGIN_CASE(JSOP_INITPROP)
5928 BEGIN_CASE(JSOP_INITMETHOD)
5929 {
5930     /* Load the property's initial value into rval. */
5931     JS_ASSERT(regs.sp - regs.fp->base() >= 2);
5932     Value rval = regs.sp[-1];
5933
5934     /* Load the object being initialized into lval/obj. */
5935     JSObject *obj = &regs.sp[-2].toObject();
5936     JS_ASSERT(obj->isObject());
5937
5938     /*
5939      * Probe the property cache.
5940      *
5941      * On a hit, if the cached shape has a non-default setter, it must be
5942      * __proto__. If shape->previous() != obj->lastProperty(), there must be a
5943      * repeated property name. The fast path does not handle these two cases.
5944      */
5945     PropertyCacheEntry *entry;
5946     const Shape *shape;
5947     if (JS_PROPERTY_CACHE(cx).testForInit(rt, regs.pc, obj, &shape, &entry) &&
5948         shape->hasDefaultSetter() &&
5949         shape->previous() == obj->lastProperty())
5950     {
5951         /* Fast path. Property cache hit. */
5952         uint32 slot = shape->slot;
5953
5954         JS_ASSERT(slot == obj->slotSpan());
5955         JS_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
5956         if (slot < obj->numSlots()) {
5957             JS_ASSERT(obj->getSlot(slot).isUndefined());
5958         } else {
5959             if (!obj->allocSlot(cx, &slot))
5960                 goto error;
5961             JS_ASSERT(slot == shape->slot);
5962         }
5963
5964         /* A new object, or one we just extended in a recent initprop op. */
5965         JS_ASSERT(!obj->lastProperty() ||
5966                   obj->shape() == obj->lastProperty()->shape);
5967         obj->extend(cx, shape);
5968
5969         /*
5970          * No method change check here because here we are adding a new
5971          * property, not updating an existing slot's value that might
5972          * contain a method of a branded shape.
5973          */
5974         TRACE_1(AddProperty, obj);
5975         obj->nativeSetSlot(slot, rval);
5976     } else {
5977         PCMETER(JS_PROPERTY_CACHE(cx).inipcmisses++);
5978
5979         /* Get the immediate property name into id. */
5980         JSAtom *atom;
5981         LOAD_ATOM(0, atom);
5982         jsid id = ATOM_TO_JSID(atom);
5983
5984         uintN defineHow = (op == JSOP_INITMETHOD)
5985                           ? JSDNP_CACHE_RESULT | JSDNP_SET_METHOD
5986                           : JSDNP_CACHE_RESULT;
5987         if (!(JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom)
5988               ? js_SetPropertyHelper(cx, obj, id, defineHow, &rval, script->strictModeCode)
5989               : js_DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
5990                                         JSPROP_ENUMERATE, 0, 0, NULL,
5991                                         defineHow))) {
5992             goto error;
5993         }
5994     }
5995
5996     /* Common tail for property cache hit and miss cases. */
5997     regs.sp--;
5998 }
5999 END_CASE(JSOP_INITPROP);
6000
6001 BEGIN_CASE(JSOP_INITELEM)
6002 {
6003     /* Pop the element's value into rval. */
6004     JS_ASSERT(regs.sp - regs.fp->base() >= 3);
6005     const Value &rref = regs.sp[-1];
6006
6007     /* Find the object being initialized at top of stack. */
6008     const Value &lref = regs.sp[-3];
6009     JS_ASSERT(lref.isObject());
6010     JSObject *obj = &lref.toObject();
6011
6012     /* Fetch id now that we have obj. */
6013     jsid id;
6014     FETCH_ELEMENT_ID(obj, -2, id);
6015
6016     /*
6017      * If rref is a hole, do not call JSObject::defineProperty. In this case,
6018      * obj must be an array, so if the current op is the last element
6019      * initialiser, set the array length to one greater than id.
6020      */
6021     if (rref.isMagic(JS_ARRAY_HOLE)) {
6022         JS_ASSERT(obj->isArray());
6023         JS_ASSERT(JSID_IS_INT(id));
6024         JS_ASSERT(jsuint(JSID_TO_INT(id)) < JS_ARGS_LENGTH_MAX);
6025         if (js_GetOpcode(cx, script, regs.pc + JSOP_INITELEM_LENGTH) == JSOP_ENDINIT &&
6026             !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1))) {
6027             goto error;
6028         }
6029     } else {
6030         if (!obj->defineProperty(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE))
6031             goto error;
6032     }
6033     regs.sp -= 2;
6034 }
6035 END_CASE(JSOP_INITELEM)
6036
6037 #if JS_HAS_SHARP_VARS
6038
6039 BEGIN_CASE(JSOP_DEFSHARP)
6040 {
6041     uint32 slot = GET_UINT16(regs.pc);
6042     JS_ASSERT(slot + 1 < regs.fp->numFixed());
6043     const Value &lref = regs.fp->slots()[slot];
6044     JSObject *obj;
6045     if (lref.isObject()) {
6046         obj = &lref.toObject();
6047     } else {
6048         JS_ASSERT(lref.isUndefined());
6049         obj = NewDenseEmptyArray(cx);
6050         if (!obj)
6051             goto error;
6052         regs.fp->slots()[slot].setObject(*obj);
6053     }
6054     jsint i = (jsint) GET_UINT16(regs.pc + UINT16_LEN);
6055     jsid id = INT_TO_JSID(i);
6056     const Value &rref = regs.sp[-1];
6057     if (rref.isPrimitive()) {
6058         char numBuf[12];
6059         JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
6060         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6061                              JSMSG_BAD_SHARP_DEF, numBuf);
6062         goto error;
6063     }
6064     if (!obj->defineProperty(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE))
6065         goto error;
6066 }
6067 END_CASE(JSOP_DEFSHARP)
6068
6069 BEGIN_CASE(JSOP_USESHARP)
6070 {
6071     uint32 slot = GET_UINT16(regs.pc);
6072     JS_ASSERT(slot + 1 < regs.fp->numFixed());
6073     const Value &lref = regs.fp->slots()[slot];
6074     jsint i = (jsint) GET_UINT16(regs.pc + UINT16_LEN);
6075     Value rval;
6076     if (lref.isUndefined()) {
6077         rval.setUndefined();
6078     } else {
6079         JSObject *obj = &regs.fp->slots()[slot].toObject();
6080         jsid id = INT_TO_JSID(i);
6081         if (!obj->getProperty(cx, id, &rval))
6082             goto error;
6083     }
6084     if (!rval.isObjectOrNull()) {
6085         char numBuf[12];
6086
6087         JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
6088         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6089                              JSMSG_BAD_SHARP_USE, numBuf);
6090         goto error;
6091     }
6092     PUSH_COPY(rval);
6093 }
6094 END_CASE(JSOP_USESHARP)
6095
6096 BEGIN_CASE(JSOP_SHARPINIT)
6097 {
6098     uint32 slot = GET_UINT16(regs.pc);
6099     JS_ASSERT(slot + 1 < regs.fp->numFixed());
6100     Value *vp = &regs.fp->slots()[slot];
6101     Value rval = vp[1];
6102
6103     /*
6104      * We peek ahead safely here because empty initialisers get zero
6105      * JSOP_SHARPINIT ops, and non-empty ones get two: the first comes
6106      * immediately after JSOP_NEWINIT followed by one or more property
6107      * initialisers; and the second comes directly before JSOP_ENDINIT.
6108      */
6109     if (regs.pc[JSOP_SHARPINIT_LENGTH] != JSOP_ENDINIT) {
6110         rval.setInt32(rval.isUndefined() ? 1 : rval.toInt32() + 1);
6111     } else {
6112         JS_ASSERT(rval.isInt32());
6113         rval.getInt32Ref() -= 1;
6114         if (rval.toInt32() == 0)
6115             vp[0].setUndefined();
6116     }
6117     vp[1] = rval;
6118 }
6119 END_CASE(JSOP_SHARPINIT)
6120
6121 #endif /* JS_HAS_SHARP_VARS */
6122
6123 {
6124 BEGIN_CASE(JSOP_GOSUB)
6125     PUSH_BOOLEAN(false);
6126     jsint i = (regs.pc - script->main) + JSOP_GOSUB_LENGTH;
6127     PUSH_INT32(i);
6128     len = GET_JUMP_OFFSET(regs.pc);
6129 END_VARLEN_CASE
6130 }
6131
6132 {
6133 BEGIN_CASE(JSOP_GOSUBX)
6134     PUSH_BOOLEAN(false);
6135     jsint i = (regs.pc - script->main) + JSOP_GOSUBX_LENGTH;
6136     len = GET_JUMPX_OFFSET(regs.pc);
6137     PUSH_INT32(i);
6138 END_VARLEN_CASE
6139 }
6140
6141 {
6142 BEGIN_CASE(JSOP_RETSUB)
6143     /* Pop [exception or hole, retsub pc-index]. */
6144     Value rval, lval;
6145     POP_COPY_TO(rval);
6146     POP_COPY_TO(lval);
6147     JS_ASSERT(lval.isBoolean());
6148     if (lval.toBoolean()) {
6149         /*
6150          * Exception was pending during finally, throw it *before* we adjust
6151          * pc, because pc indexes into script->trynotes.  This turns out not to
6152          * be necessary, but it seems clearer.  And it points out a FIXME:
6153          * 350509, due to Igor Bukanov.
6154          */
6155         cx->setPendingException(rval);
6156         goto error;
6157     }
6158     JS_ASSERT(rval.isInt32());
6159     len = rval.toInt32();
6160     regs.pc = script->main;
6161 END_VARLEN_CASE
6162 }
6163
6164 BEGIN_CASE(JSOP_EXCEPTION)
6165     PUSH_COPY(cx->getPendingException());
6166     cx->clearPendingException();
6167 #if defined(JS_TRACER) && defined(JS_METHODJIT)
6168     if (interpMode == JSINTERP_PROFILE) {
6169         leaveOnSafePoint = true;
6170         LEAVE_ON_SAFE_POINT();
6171     }
6172 #endif
6173     CHECK_BRANCH();
6174 END_CASE(JSOP_EXCEPTION)
6175
6176 BEGIN_CASE(JSOP_FINALLY)
6177     CHECK_BRANCH();
6178 END_CASE(JSOP_FINALLY)
6179
6180 BEGIN_CASE(JSOP_THROWING)
6181 {
6182     JS_ASSERT(!cx->isExceptionPending());
6183     Value v;
6184     POP_COPY_TO(v);
6185     cx->setPendingException(v);
6186 }
6187 END_CASE(JSOP_THROWING)
6188
6189 BEGIN_CASE(JSOP_THROW)
6190 {
6191     JS_ASSERT(!cx->isExceptionPending());
6192     CHECK_BRANCH();
6193     Value v;
6194     POP_COPY_TO(v);
6195     cx->setPendingException(v);
6196     /* let the code at error try to catch the exception. */
6197     goto error;
6198 }
6199 BEGIN_CASE(JSOP_SETLOCALPOP)
6200 {
6201     /*
6202      * The stack must have a block with at least one local slot below the
6203      * exception object.
6204      */
6205     JS_ASSERT((size_t) (regs.sp - regs.fp->base()) >= 2);
6206     uint32 slot = GET_UINT16(regs.pc);
6207     JS_ASSERT(slot + 1 < script->nslots);
6208     POP_COPY_TO(regs.fp->slots()[slot]);
6209 }
6210 END_CASE(JSOP_SETLOCALPOP)
6211
6212 BEGIN_CASE(JSOP_IFPRIMTOP)
6213     /*
6214      * If the top of stack is of primitive type, jump to our target. Otherwise
6215      * advance to the next opcode.
6216      */
6217     JS_ASSERT(regs.sp > regs.fp->base());
6218     if (regs.sp[-1].isPrimitive()) {
6219         len = GET_JUMP_OFFSET(regs.pc);
6220         BRANCH(len);
6221     }
6222 END_CASE(JSOP_IFPRIMTOP)
6223
6224 BEGIN_CASE(JSOP_PRIMTOP)
6225     JS_ASSERT(regs.sp > regs.fp->base());
6226     if (regs.sp[-1].isObject()) {
6227         jsint i = GET_INT8(regs.pc);
6228         js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO, -2, regs.sp[-2], NULL,
6229                              (i == JSTYPE_VOID) ? "primitive type" : JS_TYPE_STR(i));
6230         goto error;
6231     }
6232 END_CASE(JSOP_PRIMTOP)
6233
6234 BEGIN_CASE(JSOP_OBJTOP)
6235     if (regs.sp[-1].isPrimitive()) {
6236         js_ReportValueError(cx, GET_UINT16(regs.pc), -1, regs.sp[-1], NULL);
6237         goto error;
6238     }
6239 END_CASE(JSOP_OBJTOP)
6240
6241 BEGIN_CASE(JSOP_INSTANCEOF)
6242 {
6243     const Value &rref = regs.sp[-1];
6244     if (rref.isPrimitive()) {
6245         js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, -1, rref, NULL);
6246         goto error;
6247     }
6248     JSObject *obj = &rref.toObject();
6249     const Value &lref = regs.sp[-2];
6250     JSBool cond = JS_FALSE;
6251     if (!HasInstance(cx, obj, &lref, &cond))
6252         goto error;
6253     regs.sp--;
6254     regs.sp[-1].setBoolean(cond);
6255 }
6256 END_CASE(JSOP_INSTANCEOF)
6257
6258 BEGIN_CASE(JSOP_DEBUGGER)
6259 {
6260     JSDebuggerHandler handler = cx->debugHooks->debuggerHandler;
6261     if (handler) {
6262         Value rval;
6263         switch (handler(cx, script, regs.pc, Jsvalify(&rval), cx->debugHooks->debuggerHandlerData)) {
6264         case JSTRAP_ERROR:
6265             goto error;
6266         case JSTRAP_CONTINUE:
6267             break;
6268         case JSTRAP_RETURN:
6269             regs.fp->setReturnValue(rval);
6270             interpReturnOK = JS_TRUE;
6271             goto forced_return;
6272         case JSTRAP_THROW:
6273             cx->setPendingException(rval);
6274             goto error;
6275         default:;
6276         }
6277         CHECK_INTERRUPT_HANDLER();
6278     }
6279 }
6280 END_CASE(JSOP_DEBUGGER)
6281
6282 #if JS_HAS_XML_SUPPORT
6283 BEGIN_CASE(JSOP_DEFXMLNS)
6284 {
6285     if (!js_SetDefaultXMLNamespace(cx, regs.sp[-1]))
6286         goto error;
6287     regs.sp--;
6288 }
6289 END_CASE(JSOP_DEFXMLNS)
6290
6291 BEGIN_CASE(JSOP_ANYNAME)
6292 {
6293     jsid id;
6294     if (!js_GetAnyName(cx, &id))
6295         goto error;
6296     PUSH_COPY(IdToValue(id));
6297 }
6298 END_CASE(JSOP_ANYNAME)
6299
6300 BEGIN_CASE(JSOP_QNAMEPART)
6301 {
6302     JSAtom *atom;
6303     LOAD_ATOM(0, atom);
6304     PUSH_STRING(ATOM_TO_STRING(atom));
6305 }
6306 END_CASE(JSOP_QNAMEPART)
6307
6308 BEGIN_CASE(JSOP_QNAMECONST)
6309 {
6310     JSAtom *atom;
6311     LOAD_ATOM(0, atom);
6312     Value rval = StringValue(ATOM_TO_STRING(atom));
6313     Value lval = regs.sp[-1];
6314     JSObject *obj = js_ConstructXMLQNameObject(cx, lval, rval);
6315     if (!obj)
6316         goto error;
6317     regs.sp[-1].setObject(*obj);
6318 }
6319 END_CASE(JSOP_QNAMECONST)
6320
6321 BEGIN_CASE(JSOP_QNAME)
6322 {
6323     Value rval = regs.sp[-1];
6324     Value lval = regs.sp[-2];
6325     JSObject *obj = js_ConstructXMLQNameObject(cx, lval, rval);
6326     if (!obj)
6327         goto error;
6328     regs.sp--;
6329     regs.sp[-1].setObject(*obj);
6330 }
6331 END_CASE(JSOP_QNAME)
6332
6333 BEGIN_CASE(JSOP_TOATTRNAME)
6334 {
6335     Value rval;
6336     rval = regs.sp[-1];
6337     if (!js_ToAttributeName(cx, &rval))
6338         goto error;
6339     regs.sp[-1] = rval;
6340 }
6341 END_CASE(JSOP_TOATTRNAME)
6342
6343 BEGIN_CASE(JSOP_TOATTRVAL)
6344 {
6345     Value rval;
6346     rval = regs.sp[-1];
6347     JS_ASSERT(rval.isString());
6348     JSString *str = js_EscapeAttributeValue(cx, rval.toString(), JS_FALSE);
6349     if (!str)
6350         goto error;
6351     regs.sp[-1].setString(str);
6352 }
6353 END_CASE(JSOP_TOATTRVAL)
6354
6355 BEGIN_CASE(JSOP_ADDATTRNAME)
6356 BEGIN_CASE(JSOP_ADDATTRVAL)
6357 {
6358     Value rval = regs.sp[-1];
6359     Value lval = regs.sp[-2];
6360     JSString *str = lval.toString();
6361     JSString *str2 = rval.toString();
6362     str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2);
6363     if (!str)
6364         goto error;
6365     regs.sp--;
6366     regs.sp[-1].setString(str);
6367 }
6368 END_CASE(JSOP_ADDATTRNAME)
6369
6370 BEGIN_CASE(JSOP_BINDXMLNAME)
6371 {
6372     Value lval;
6373     lval = regs.sp[-1];
6374     JSObject *obj;
6375     jsid id;
6376     if (!js_FindXMLProperty(cx, lval, &obj, &id))
6377         goto error;
6378     regs.sp[-1].setObjectOrNull(obj);
6379     PUSH_COPY(IdToValue(id));
6380 }
6381 END_CASE(JSOP_BINDXMLNAME)
6382
6383 BEGIN_CASE(JSOP_SETXMLNAME)
6384 {
6385     JSObject *obj = &regs.sp[-3].toObject();
6386     Value rval = regs.sp[-1];
6387     jsid id;
6388     FETCH_ELEMENT_ID(obj, -2, id);
6389     if (!obj->setProperty(cx, id, &rval, script->strictModeCode))
6390         goto error;
6391     rval = regs.sp[-1];
6392     regs.sp -= 2;
6393     regs.sp[-1] = rval;
6394 }
6395 END_CASE(JSOP_SETXMLNAME)
6396
6397 BEGIN_CASE(JSOP_CALLXMLNAME)
6398 BEGIN_CASE(JSOP_XMLNAME)
6399 {
6400     Value lval = regs.sp[-1];
6401     JSObject *obj;
6402     jsid id;
6403     if (!js_FindXMLProperty(cx, lval, &obj, &id))
6404         goto error;
6405     Value rval;
6406     if (!obj->getProperty(cx, id, &rval))
6407         goto error;
6408     regs.sp[-1] = rval;
6409     if (op == JSOP_CALLXMLNAME)
6410         PUSH_IMPLICIT_THIS(cx, obj, rval);
6411 }
6412 END_CASE(JSOP_XMLNAME)
6413
6414 BEGIN_CASE(JSOP_DESCENDANTS)
6415 BEGIN_CASE(JSOP_DELDESC)
6416 {
6417     JSObject *obj;
6418     FETCH_OBJECT(cx, -2, obj);
6419     jsval rval = Jsvalify(regs.sp[-1]);
6420     if (!js_GetXMLDescendants(cx, obj, rval, &rval))
6421         goto error;
6422
6423     if (op == JSOP_DELDESC) {
6424         regs.sp[-1] = Valueify(rval);   /* set local root */
6425         if (!js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval)))
6426             goto error;
6427         rval = JSVAL_TRUE;                  /* always succeed */
6428     }
6429
6430     regs.sp--;
6431     regs.sp[-1] = Valueify(rval);
6432 }
6433 END_CASE(JSOP_DESCENDANTS)
6434
6435 {
6436 BEGIN_CASE(JSOP_FILTER)
6437     /*
6438      * We push the hole value before jumping to [enditer] so we can detect the
6439      * first iteration and direct js_StepXMLListFilter to initialize filter's
6440      * state.
6441      */
6442     PUSH_HOLE();
6443     len = GET_JUMP_OFFSET(regs.pc);
6444     JS_ASSERT(len > 0);
6445 END_VARLEN_CASE
6446 }
6447
6448 BEGIN_CASE(JSOP_ENDFILTER)
6449 {
6450     bool cond = !regs.sp[-1].isMagic();
6451     if (cond) {
6452         /* Exit the "with" block left from the previous iteration. */
6453         js_LeaveWith(cx);
6454     }
6455     if (!js_StepXMLListFilter(cx, cond))
6456         goto error;
6457     if (!regs.sp[-1].isNull()) {
6458         /*
6459          * Decrease sp after EnterWith returns as we use sp[-1] there to root
6460          * temporaries.
6461          */
6462         JS_ASSERT(IsXML(regs.sp[-1]));
6463         if (!js_EnterWith(cx, -2, JSOP_ENDFILTER, JSOP_ENDFILTER_LENGTH))
6464             goto error;
6465         regs.sp--;
6466         len = GET_JUMP_OFFSET(regs.pc);
6467         JS_ASSERT(len < 0);
6468         BRANCH(len);
6469     }
6470     regs.sp--;
6471 }
6472 END_CASE(JSOP_ENDFILTER);
6473
6474 BEGIN_CASE(JSOP_TOXML)
6475 {
6476     Value rval = regs.sp[-1];
6477     JSObject *obj = js_ValueToXMLObject(cx, rval);
6478     if (!obj)
6479         goto error;
6480     regs.sp[-1].setObject(*obj);
6481 }
6482 END_CASE(JSOP_TOXML)
6483
6484 BEGIN_CASE(JSOP_TOXMLLIST)
6485 {
6486     Value rval = regs.sp[-1];
6487     JSObject *obj = js_ValueToXMLListObject(cx, rval);
6488     if (!obj)
6489         goto error;
6490     regs.sp[-1].setObject(*obj);
6491 }
6492 END_CASE(JSOP_TOXMLLIST)
6493
6494 BEGIN_CASE(JSOP_XMLTAGEXPR)
6495 {
6496     Value rval = regs.sp[-1];
6497     JSString *str = js_ValueToString(cx, rval);
6498     if (!str)
6499         goto error;
6500     regs.sp[-1].setString(str);
6501 }
6502 END_CASE(JSOP_XMLTAGEXPR)
6503
6504 BEGIN_CASE(JSOP_XMLELTEXPR)
6505 {
6506     Value rval = regs.sp[-1];
6507     JSString *str;
6508     if (IsXML(rval)) {
6509         str = js_ValueToXMLString(cx, rval);
6510     } else {
6511         str = js_ValueToString(cx, rval);
6512         if (str)
6513             str = js_EscapeElementValue(cx, str);
6514     }
6515     if (!str)
6516         goto error;
6517     regs.sp[-1].setString(str);
6518 }
6519 END_CASE(JSOP_XMLELTEXPR)
6520
6521 BEGIN_CASE(JSOP_XMLCDATA)
6522 {
6523     JSAtom *atom;
6524     LOAD_ATOM(0, atom);
6525     JSString *str = ATOM_TO_STRING(atom);
6526     JSObject *obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_TEXT, NULL, str);
6527     if (!obj)
6528         goto error;
6529     PUSH_OBJECT(*obj);
6530 }
6531 END_CASE(JSOP_XMLCDATA)
6532
6533 BEGIN_CASE(JSOP_XMLCOMMENT)
6534 {
6535     JSAtom *atom;
6536     LOAD_ATOM(0, atom);
6537     JSString *str = ATOM_TO_STRING(atom);
6538     JSObject *obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_COMMENT, NULL, str);
6539     if (!obj)
6540         goto error;
6541     PUSH_OBJECT(*obj);
6542 }
6543 END_CASE(JSOP_XMLCOMMENT)
6544
6545 BEGIN_CASE(JSOP_XMLPI)
6546 {
6547     JSAtom *atom;
6548     LOAD_ATOM(0, atom);
6549     JSString *str = ATOM_TO_STRING(atom);
6550     Value rval = regs.sp[-1];
6551     JSString *str2 = rval.toString();
6552     JSObject *obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_PROCESSING_INSTRUCTION, str, str2);
6553     if (!obj)
6554         goto error;
6555     regs.sp[-1].setObject(*obj);
6556 }
6557 END_CASE(JSOP_XMLPI)
6558
6559 BEGIN_CASE(JSOP_GETFUNNS)
6560 {
6561     Value rval;
6562     if (!js_GetFunctionNamespace(cx, &rval))
6563         goto error;
6564     PUSH_COPY(rval);
6565 }
6566 END_CASE(JSOP_GETFUNNS)
6567 #endif /* JS_HAS_XML_SUPPORT */
6568
6569 BEGIN_CASE(JSOP_ENTERBLOCK)
6570 {
6571     JSObject *obj;
6572     LOAD_OBJECT(0, obj);
6573     JS_ASSERT(obj->isStaticBlock());
6574     JS_ASSERT(regs.fp->base() + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp);
6575     Value *vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj);
6576     JS_ASSERT(regs.sp < vp);
6577     JS_ASSERT(vp <= regs.fp->slots() + script->nslots);
6578     SetValueRangeToUndefined(regs.sp, vp);
6579     regs.sp = vp;
6580
6581 #ifdef DEBUG
6582     /*
6583      * The young end of fp->scopeChain may omit blocks if we haven't closed
6584      * over them, but if there are any closure blocks on fp->scopeChain, they'd
6585      * better be (clones of) ancestors of the block we're entering now;
6586      * anything else we should have popped off fp->scopeChain when we left its
6587      * static scope.
6588      */
6589     JSObject *obj2 = &regs.fp->scopeChain();
6590     Class *clasp;
6591     while ((clasp = obj2->getClass()) == &js_WithClass)
6592         obj2 = obj2->getParent();
6593     if (clasp == &js_BlockClass &&
6594         obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, regs.fp)) {
6595         JSObject *youngestProto = obj2->getProto();
6596         JS_ASSERT(youngestProto->isStaticBlock());
6597         JSObject *parent = obj;
6598         while ((parent = parent->getParent()) != youngestProto)
6599             JS_ASSERT(parent);
6600     }
6601 #endif
6602 }
6603 END_CASE(JSOP_ENTERBLOCK)
6604
6605 BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
6606 BEGIN_CASE(JSOP_LEAVEBLOCK)
6607 {
6608     JSObject *blockChain;
6609     LOAD_OBJECT(UINT16_LEN, blockChain);
6610 #ifdef DEBUG
6611     JS_ASSERT(blockChain->isStaticBlock());
6612     uintN blockDepth = OBJ_BLOCK_DEPTH(cx, blockChain);
6613     JS_ASSERT(blockDepth <= StackDepth(script));
6614 #endif
6615     /*
6616      * If we're about to leave the dynamic scope of a block that has been
6617      * cloned onto fp->scopeChain, clear its private data, move its locals from
6618      * the stack into the clone, and pop it off the chain.
6619      */
6620     JSObject &obj = regs.fp->scopeChain();
6621     if (obj.getProto() == blockChain) {
6622         JS_ASSERT(obj.isClonedBlock());
6623         if (!js_PutBlockObject(cx, JS_TRUE))
6624             goto error;
6625     }
6626
6627     /* Move the result of the expression to the new topmost stack slot. */
6628     Value *vp = NULL;  /* silence GCC warnings */
6629     if (op == JSOP_LEAVEBLOCKEXPR)
6630         vp = &regs.sp[-1];
6631     regs.sp -= GET_UINT16(regs.pc);
6632     if (op == JSOP_LEAVEBLOCKEXPR) {
6633         JS_ASSERT(regs.fp->base() + blockDepth == regs.sp - 1);
6634         regs.sp[-1] = *vp;
6635     } else {
6636         JS_ASSERT(regs.fp->base() + blockDepth == regs.sp);
6637     }
6638 }
6639 END_CASE(JSOP_LEAVEBLOCK)
6640
6641 #if JS_HAS_GENERATORS
6642 BEGIN_CASE(JSOP_GENERATOR)
6643 {
6644     JS_ASSERT(!cx->isExceptionPending());
6645     regs.pc += JSOP_GENERATOR_LENGTH;
6646     JSObject *obj = js_NewGenerator(cx);
6647     if (!obj)
6648         goto error;
6649     JS_ASSERT(!regs.fp->hasCallObj() && !regs.fp->hasArgsObj());
6650     regs.fp->setReturnValue(ObjectValue(*obj));
6651     interpReturnOK = true;
6652     if (entryFrame != regs.fp)
6653         goto inline_return;
6654     goto exit;
6655 }
6656
6657 BEGIN_CASE(JSOP_YIELD)
6658     JS_ASSERT(!cx->isExceptionPending());
6659     JS_ASSERT(regs.fp->isFunctionFrame() && !regs.fp->isEvalFrame());
6660     if (cx->generatorFor(regs.fp)->state == JSGEN_CLOSING) {
6661         js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD,
6662                             JSDVG_SEARCH_STACK, argv[-2], NULL);
6663         goto error;
6664     }
6665     regs.fp->setReturnValue(regs.sp[-1]);
6666     regs.fp->setYielding();
6667     regs.pc += JSOP_YIELD_LENGTH;
6668     interpReturnOK = JS_TRUE;
6669     goto exit;
6670
6671 BEGIN_CASE(JSOP_ARRAYPUSH)
6672 {
6673     uint32 slot = GET_UINT16(regs.pc);
6674     JS_ASSERT(script->nfixed <= slot);
6675     JS_ASSERT(slot < script->nslots);
6676     JSObject *obj = &regs.fp->slots()[slot].toObject();
6677     if (!js_ArrayCompPush(cx, obj, regs.sp[-1]))
6678         goto error;
6679     regs.sp--;
6680 }
6681 END_CASE(JSOP_ARRAYPUSH)
6682 #endif /* JS_HAS_GENERATORS */
6683
6684 #if JS_THREADED_INTERP
6685   L_JSOP_BACKPATCH:
6686   L_JSOP_BACKPATCH_POP:
6687
6688 # if !JS_HAS_GENERATORS
6689   L_JSOP_GENERATOR:
6690   L_JSOP_YIELD:
6691   L_JSOP_ARRAYPUSH:
6692 # endif
6693
6694 # if !JS_HAS_SHARP_VARS
6695   L_JSOP_DEFSHARP:
6696   L_JSOP_USESHARP:
6697   L_JSOP_SHARPINIT:
6698 # endif
6699
6700 # if !JS_HAS_DESTRUCTURING
6701   L_JSOP_ENUMCONSTELEM:
6702 # endif
6703
6704 # if !JS_HAS_XML_SUPPORT
6705   L_JSOP_CALLXMLNAME:
6706   L_JSOP_STARTXMLEXPR:
6707   L_JSOP_STARTXML:
6708   L_JSOP_DELDESC:
6709   L_JSOP_GETFUNNS:
6710   L_JSOP_XMLPI:
6711   L_JSOP_XMLCOMMENT:
6712   L_JSOP_XMLCDATA:
6713   L_JSOP_XMLELTEXPR:
6714   L_JSOP_XMLTAGEXPR:
6715   L_JSOP_TOXMLLIST:
6716   L_JSOP_TOXML:
6717   L_JSOP_ENDFILTER:
6718   L_JSOP_FILTER:
6719   L_JSOP_DESCENDANTS:
6720   L_JSOP_XMLNAME:
6721   L_JSOP_SETXMLNAME:
6722   L_JSOP_BINDXMLNAME:
6723   L_JSOP_ADDATTRVAL:
6724   L_JSOP_ADDATTRNAME:
6725   L_JSOP_TOATTRVAL:
6726   L_JSOP_TOATTRNAME:
6727   L_JSOP_QNAME:
6728   L_JSOP_QNAMECONST:
6729   L_JSOP_QNAMEPART:
6730   L_JSOP_ANYNAME:
6731   L_JSOP_DEFXMLNS:
6732 # endif
6733
6734 #endif /* !JS_THREADED_INTERP */
6735 #if !JS_THREADED_INTERP
6736           default:
6737 #endif
6738           {
6739             char numBuf[12];
6740             JS_snprintf(numBuf, sizeof numBuf, "%d", op);
6741             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6742                                  JSMSG_BAD_BYTECODE, numBuf);
6743             goto error;
6744           }
6745
6746 #if !JS_THREADED_INTERP
6747         } /* switch (op) */
6748     } /* for (;;) */
6749 #endif /* !JS_THREADED_INTERP */
6750
6751   error:
6752     JS_ASSERT(cx->regs == &regs);
6753 #ifdef JS_TRACER
6754     if (regs.fp->hasImacropc() && cx->isExceptionPending()) {
6755         // Handle exceptions as if they came from the imacro-calling pc.
6756         regs.pc = regs.fp->imacropc();
6757         regs.fp->clearImacropc();
6758     }
6759 #endif
6760
6761     JS_ASSERT(size_t((regs.fp->hasImacropc() ? regs.fp->imacropc() : regs.pc) - script->code) <
6762               script->length);
6763
6764 #ifdef JS_TRACER
6765     /*
6766      * This abort could be weakened to permit tracing through exceptions that
6767      * are thrown and caught within a loop, with the co-operation of the tracer.
6768      * For now just bail on any sign of trouble.
6769      */
6770     if (TRACE_RECORDER(cx))
6771         AbortRecording(cx, "error or exception while recording");
6772 # ifdef JS_METHODJIT
6773     if (TRACE_PROFILER(cx))
6774         AbortProfiling(cx);
6775 # endif
6776 #endif
6777
6778     if (!cx->isExceptionPending()) {
6779         /* This is an error, not a catchable exception, quit the frame ASAP. */
6780         interpReturnOK = JS_FALSE;
6781     } else {
6782         JSThrowHook handler;
6783         JSTryNote *tn, *tnlimit;
6784         uint32 offset;
6785
6786         /* Restore atoms local in case we will resume. */
6787         atoms = script->atomMap.vector;
6788
6789         /* Call debugger throw hook if set. */
6790         handler = cx->debugHooks->throwHook;
6791         if (handler) {
6792             Value rval;
6793             switch (handler(cx, script, regs.pc, Jsvalify(&rval),
6794                             cx->debugHooks->throwHookData)) {
6795               case JSTRAP_ERROR:
6796                 cx->clearPendingException();
6797                 goto error;
6798               case JSTRAP_RETURN:
6799                 cx->clearPendingException();
6800                 regs.fp->setReturnValue(rval);
6801                 interpReturnOK = JS_TRUE;
6802                 goto forced_return;
6803               case JSTRAP_THROW:
6804                 cx->setPendingException(rval);
6805               case JSTRAP_CONTINUE:
6806               default:;
6807             }
6808             CHECK_INTERRUPT_HANDLER();
6809         }
6810
6811         /*
6812          * Look for a try block in script that can catch this exception.
6813          */
6814         if (!JSScript::isValidOffset(script->trynotesOffset))
6815             goto no_catch;
6816
6817         offset = (uint32)(regs.pc - script->main);
6818         tn = script->trynotes()->vector;
6819         tnlimit = tn + script->trynotes()->length;
6820         do {
6821             if (offset - tn->start >= tn->length)
6822                 continue;
6823
6824             /*
6825              * We have a note that covers the exception pc but we must check
6826              * whether the interpreter has already executed the corresponding
6827              * handler. This is possible when the executed bytecode
6828              * implements break or return from inside a for-in loop.
6829              *
6830              * In this case the emitter generates additional [enditer] and
6831              * [gosub] opcodes to close all outstanding iterators and execute
6832              * the finally blocks. If such an [enditer] throws an exception,
6833              * its pc can still be inside several nested for-in loops and
6834              * try-finally statements even if we have already closed the
6835              * corresponding iterators and invoked the finally blocks.
6836              *
6837              * To address this, we make [enditer] always decrease the stack
6838              * even when its implementation throws an exception. Thus already
6839              * executed [enditer] and [gosub] opcodes will have try notes
6840              * with the stack depth exceeding the current one and this
6841              * condition is what we use to filter them out.
6842              */
6843             if (tn->stackDepth > regs.sp - regs.fp->base())
6844                 continue;
6845
6846             /*
6847              * Set pc to the first bytecode after the the try note to point
6848              * to the beginning of catch or finally or to [enditer] closing
6849              * the for-in loop.
6850              */
6851             regs.pc = (script)->main + tn->start + tn->length;
6852
6853             JSBool ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE);
6854             JS_ASSERT(regs.sp == regs.fp->base() + tn->stackDepth);
6855             if (!ok) {
6856                 /*
6857                  * Restart the handler search with updated pc and stack depth
6858                  * to properly notify the debugger.
6859                  */
6860                 goto error;
6861             }
6862
6863             switch (tn->kind) {
6864               case JSTRY_CATCH:
6865 #if JS_HAS_GENERATORS
6866                 /* Catch cannot intercept the closing of a generator. */
6867                   if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING)))
6868                     break;
6869 #endif
6870
6871                 /*
6872                  * Don't clear exceptions to save cx->exception from GC
6873                  * until it is pushed to the stack via [exception] in the
6874                  * catch block.
6875                  */
6876                 len = 0;
6877                 DO_NEXT_OP(len);
6878
6879               case JSTRY_FINALLY:
6880                 /*
6881                  * Push (true, exception) pair for finally to indicate that
6882                  * [retsub] should rethrow the exception.
6883                  */
6884                 PUSH_BOOLEAN(true);
6885                 PUSH_COPY(cx->getPendingException());
6886                 cx->clearPendingException();
6887                 len = 0;
6888                 DO_NEXT_OP(len);
6889
6890               case JSTRY_ITER: {
6891                 /* This is similar to JSOP_ENDITER in the interpreter loop. */
6892                 JS_ASSERT(js_GetOpcode(cx, regs.fp->script(), regs.pc) == JSOP_ENDITER);
6893                 Value v = cx->getPendingException();
6894                 cx->clearPendingException();
6895                 ok = js_CloseIterator(cx, &regs.sp[-1].toObject());
6896                 regs.sp -= 1;
6897                 if (!ok)
6898                     goto error;
6899                 cx->setPendingException(v);
6900               }
6901            }
6902         } while (++tn != tnlimit);
6903
6904       no_catch:
6905         /*
6906          * Propagate the exception or error to the caller unless the exception
6907          * is an asynchronous return from a generator.
6908          */
6909         interpReturnOK = JS_FALSE;
6910 #if JS_HAS_GENERATORS
6911         if (JS_UNLIKELY(cx->isExceptionPending() &&
6912                         cx->getPendingException().isMagic(JS_GENERATOR_CLOSING))) {
6913             cx->clearPendingException();
6914             interpReturnOK = JS_TRUE;
6915             regs.fp->clearReturnValue();
6916         }
6917 #endif
6918     }
6919
6920   forced_return:
6921     /*
6922      * Unwind the scope making sure that interpReturnOK stays false even when
6923      * js_UnwindScope returns true.
6924      *
6925      * When a trap handler returns JSTRAP_RETURN, we jump here with
6926      * interpReturnOK set to true bypassing any finally blocks.
6927      */
6928     interpReturnOK &= js_UnwindScope(cx, 0, interpReturnOK || cx->isExceptionPending());
6929     JS_ASSERT(regs.sp == regs.fp->base());
6930
6931 #ifdef DEBUG
6932     cx->logPrevPc = NULL;
6933 #endif
6934
6935     if (entryFrame != regs.fp)
6936         goto inline_return;
6937
6938   exit:
6939     interpReturnOK = ScriptEpilogue(cx, regs.fp, interpReturnOK);
6940     regs.fp->setFinishedInInterpreter();
6941
6942     /*
6943      * At this point we are inevitably leaving an interpreted function or a
6944      * top-level script, and returning to one of:
6945      * (a) an "out of line" call made through js_Invoke;
6946      * (b) a js_Execute activation;
6947      * (c) a generator (SendToGenerator, jsiter.c).
6948      *
6949      * We must not be in an inline frame. The check above ensures that for the
6950      * error case and for a normal return, the code jumps directly to parent's
6951      * frame pc.
6952      */
6953     JS_ASSERT(entryFrame == regs.fp);
6954
6955 #ifdef JS_TRACER
6956     JS_ASSERT_IF(interpReturnOK && interpMode == JSINTERP_RECORD, !TRACE_RECORDER(cx));
6957     if (TRACE_RECORDER(cx))
6958         AbortRecording(cx, "recording out of Interpret");
6959 # ifdef JS_METHODJIT
6960     if (TRACE_PROFILER(cx))
6961         AbortProfiling(cx);
6962 # endif
6963 #endif
6964
6965     JS_ASSERT_IF(!regs.fp->isGeneratorFrame(), !js_IsActiveWithOrBlock(cx, &regs.fp->scopeChain(), 0));
6966
6967     return interpReturnOK;
6968
6969   atom_not_defined:
6970     {
6971         JSAutoByteString printable;
6972         if (js_AtomToPrintableString(cx, atomNotDefined, &printable))
6973             js_ReportIsNotDefined(cx, printable.ptr());
6974     }
6975     goto error;
6976
6977     /*
6978      * This path is used when it's guaranteed the method can be finished
6979      * inside the JIT.
6980      */
6981 #if defined(JS_METHODJIT)
6982   leave_on_safe_point:
6983 #endif
6984     return interpReturnOK;
6985 }
6986
6987 } /* namespace js */
6988
6989 #endif /* !defined jsinvoke_cpp___ */