1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
17 * The Original Code is Mozilla Communicator client code, released
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.
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.
39 * ***** END LICENSE BLOCK ***** */
42 * JavaScript bytecode interpreter.
58 #include "jsversion.h"
68 #include "jspropertycache.h"
74 #include "jsstaticcheck.h"
76 #include "jslibmath.h"
78 #include "methodjit/MethodJIT.h"
79 #include "methodjit/MethodJIT-inl.h"
80 #include "methodjit/Logging.h"
82 #include "jsatominlines.h"
83 #include "jscntxtinlines.h"
84 #include "jsinterpinlines.h"
85 #include "jsobjinlines.h"
87 #include "jspropertycacheinlines.h"
88 #include "jsscopeinlines.h"
89 #include "jsscriptinlines.h"
90 #include "jsstrinlines.h"
91 #include "jsopcodeinlines.h"
93 #if JS_HAS_XML_SUPPORT
97 #include "jsautooplen.h"
99 #if defined(JS_METHODJIT) && defined(JS_MONOIC)
100 #include "methodjit/MonoIC.h"
104 using namespace js::gc;
106 /* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */
107 #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___
110 JSObject *const JSStackFrame::sInvalidScopeChain = (JSObject *)0xbeef;
114 JSStackFrame::pc(JSContext *cx, JSStackFrame *next)
116 JS_ASSERT_IF(next, next->prev_ == this);
117 JS_ASSERT(cx->containingSegment(this) != NULL);
123 StackSegment *segment = cx->getCurrentSegment();
124 regs = segment->getSuspendedRegs();
127 if (this == regs->fp)
131 next = cx->computeNextFrame(this);
133 if (next->flags_ & JSFRAME_HAS_PREVPC)
134 return next->prevpc_;
136 #if defined(JS_METHODJIT) && defined(JS_MONOIC)
137 js::mjit::JITScript *jit = script()->getJIT(isConstructing());
138 return jit->nativeToPC(next->ncode_);
140 JS_NOT_REACHED("Unknown PC for frame");
146 js::GetScopeChain(JSContext *cx)
148 JSStackFrame *fp = js_GetTopStackFrame(cx);
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.
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.
160 JSObject *obj = cx->globalObject;
162 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE);
166 OBJ_TO_INNER_OBJECT(cx, obj);
169 return GetScopeChain(cx, fp);
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.
180 js::GetBlockChain(JSContext *cx, JSStackFrame *fp)
182 if (!fp->isScriptFrame())
185 /* Assume that imacros don't affect blockChain */
186 jsbytecode *target = fp->hasImacropc() ? fp->imacropc() : fp->pc(cx);
188 JSScript *script = fp->script();
189 jsbytecode *start = script->code;
190 JS_ASSERT(target >= start && target < start + script->length);
192 JSObject *blockChain = NULL;
195 for (jsbytecode *pc = start; pc < target; pc += oplen) {
196 JSOp op = js_GetOpcode(cx, script, pc);
197 const JSCodeSpec *cs = &js_CodeSpec[op];
200 oplen = js_GetVariableBytecodeLength(pc);
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)
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)
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.
229 js::GetBlockChainFast(JSContext *cx, JSStackFrame *fp, JSOp op, size_t oplen)
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);
237 JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == op);
239 /* The fast paths assume no JSOP_RESETBASE/INDEXBASE noise. */
240 if (op == JSOP_NULLBLOCKCHAIN)
242 if (op == JSOP_BLOCKCHAIN)
243 return fp->script()->getObject(GET_INDEX(pc));
245 return GetBlockChain(cx, fp);
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.
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.
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.
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.
275 * This lazy cloning is implemented in GetScopeChain, which is also used in
276 * some other cases --- entering 'with' blocks, for example.
279 GetScopeChainFull(JSContext *cx, JSStackFrame *fp, JSObject *blockChain)
281 JSObject *sharedBlock = blockChain;
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.
288 JS_ASSERT_IF(fp->isFunctionFrame() && fp->fun()->isHeavyweight(),
290 return &fp->scopeChain();
293 /* We don't handle cloning blocks on trace. */
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.
301 * Also, identify the innermost compiler-allocated block we needn't clone.
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))
310 /* We know we must clone everything on blockChain. */
311 limitBlock = limitClone = NULL;
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.
319 limitClone = &fp->scopeChain();
320 while (limitClone->getClass() == &js_WithClass)
321 limitClone = limitClone->getParent();
322 JS_ASSERT(limitClone);
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.
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.
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.
342 limitBlock = limitClone->getProto();
344 /* If the innermost block has already been cloned, we are done. */
345 if (limitBlock == sharedBlock)
346 return &fp->scopeChain();
350 * Special-case cloning the innermost block; this doesn't have enough in
351 * common with subsequent steps to include in the loop.
353 * js_CloneBlockObject leaves the clone's parent slot uninitialized. We
356 JSObject *innermostNewChild = js_CloneBlockObject(cx, sharedBlock, fp);
357 if (!innermostNewChild)
359 AutoObjectRooter tvr(cx, innermostNewChild);
362 * Clone our way towards outer scopes until we reach the innermost
363 * enclosing function, or the innermost block we've already cloned.
365 JSObject *newChild = innermostNewChild;
367 JS_ASSERT(newChild->getProto() == sharedBlock);
368 sharedBlock = sharedBlock->getParent();
370 /* Sometimes limitBlock will be NULL, so check that first. */
371 if (sharedBlock == limitBlock || !sharedBlock)
374 /* As in the call above, we don't know the real parent yet. */
375 JSObject *clone = js_CloneBlockObject(cx, sharedBlock, fp);
379 newChild->setParent(clone);
382 newChild->setParent(&fp->scopeChain());
386 * If we found a limit block belonging to this frame, then we should have
387 * found it in blockChain.
389 JS_ASSERT_IF(limitBlock &&
390 limitBlock->isBlock() &&
391 limitClone->getPrivate() == js_FloatingFrameIfGenerator(cx, fp),
394 /* Place our newly cloned blocks at the head of the scope chain. */
395 fp->setScopeChainNoCallObj(*innermostNewChild);
396 return innermostNewChild;
400 js::GetScopeChain(JSContext *cx, JSStackFrame *fp)
402 return GetScopeChainFull(cx, fp, GetBlockChain(cx, fp));
406 js::GetScopeChainFast(JSContext *cx, JSStackFrame *fp, JSOp op, size_t oplen)
408 return GetScopeChainFull(cx, fp, GetBlockChainFast(cx, fp, op, oplen));
411 /* Some objects (e.g., With) delegate 'this' to another object. */
412 static inline JSObject *
413 CallThisObjectHook(JSContext *cx, JSObject *obj, Value *argv)
415 JSObject *thisp = obj->thisObject(cx);
418 argv[-1].setObject(*thisp);
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:
428 * function f() { return this }
429 * function g() { return f }
435 * The alert should display "true".
437 JS_STATIC_INTERPRET bool
438 ComputeGlobalThis(JSContext *cx, Value *vp)
440 JSObject *thisp = vp[0].toObject().getGlobal()->thisObject(cx);
443 vp[1].setObject(*thisp);
450 ReportIncompatibleMethod(JSContext *cx, Value *vp, Class *clasp)
452 Value &thisv = vp[1];
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);
464 JS_ASSERT(thisv.isUndefined() || thisv.isNull());
468 if (JSFunction *fun = js_ValueToFunction(cx, &vp[0], 0)) {
469 const char *name = thisv.isObject()
470 ? thisv.toObject().getClass()->name
479 : thisv.isUndefined()
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);
491 BoxThisForVp(JSContext *cx, Value *vp)
494 * Check for SynthesizeFrame poisoning and fast constructors which
495 * didn't check their vp properly.
497 JS_ASSERT(!vp[1].isMagic());
499 JSFunction *fun = vp[0].toObject().isFunction() ? vp[0].toObject().getFunctionPrivate() : NULL;
500 JS_ASSERT_IF(fun && fun->isInterpreted(), !fun->inStrictMode());
503 if (vp[1].isNullOrUndefined())
504 return ComputeGlobalThis(cx, vp);
506 if (!vp[1].isObject())
507 return !!js_PrimitiveToObject(cx, &vp[1]);
514 #if JS_HAS_NO_SUCH_METHOD
516 const uint32 JSSLOT_FOUND_FUNCTION = 0;
517 const uint32 JSSLOT_SAVED_ID = 1;
519 Class js_NoSuchMethodClass = {
521 JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
522 PropertyStub, /* addProperty */
523 PropertyStub, /* delProperty */
524 PropertyStub, /* getProperty */
525 StrictPropertyStub, /* setProperty */
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:
539 * this.__noSuchMethod__(id, args)
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
546 js_OnUnknownMethod(JSContext *cx, Value *vp)
548 JS_ASSERT(!vp[1].isPrimitive());
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()))
555 if (tvr.value().isPrimitive()) {
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))
564 if (!JSID_IS_VOID(id))
565 vp[0] = IdToValue(id);
568 obj = js_NewGCObject(cx, FINALIZE_OBJECT2);
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.
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);
587 static JS_REQUIRES_STACK JSBool
588 NoSuchMethod(JSContext *cx, uintN argc, Value *vp, uint32 flags)
590 InvokeArgsGuard args;
591 if (!cx->stack().pushInvokeArgs(cx, 2, &args))
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);
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);
605 args[1].setObject(*argsobj);
606 JSBool ok = (flags & JSINVOKE_CONSTRUCT)
607 ? InvokeConstructor(cx, args)
608 : Invoke(cx, args, flags);
613 #endif /* JS_HAS_NO_SUCH_METHOD */
617 JS_REQUIRES_STACK bool
618 RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp)
621 JS_ASSERT(fp == cx->fp());
622 JS_ASSERT(fp->script() == script);
623 #ifdef JS_METHODJIT_SPEW
627 AutoInterpPreparer prepareInterp(cx, script);
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);
640 mjit::CompileStatus status;
641 status = mjit::CanMethodJIT(cx, script, fp, mjit::CompileRequest_Interpreter);
642 if (status == mjit::Compile_Error)
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());
653 ok = Interpret(cx, fp);
654 JS_ASSERT_IF(!fp->isYielding() && !(fp->isEvalFrame() && !fp->script()->strictModeCode),
655 !fp->hasCallObj() && !fp->hasArgsObj());
659 PutOwnedActivationObjects(cx, fp);
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.
669 JS_REQUIRES_STACK bool
670 Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags)
672 /* N.B. Must be kept in sync with InvokeSessionGuard::start/invoke */
674 CallArgs args = argsRef;
675 JS_ASSERT(args.argc() <= JS_ARGS_LENGTH_MAX);
677 if (args.callee().isPrimitive()) {
678 js_ReportIsNotFunction(cx, &args.callee(), flags & JSINVOKE_FUNFLAGS);
682 JSObject &callee = args.callee().toObject();
683 Class *clasp = callee.getClass();
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);
691 JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !clasp->construct);
693 js_ReportIsNotFunction(cx, &args.callee(), flags);
696 return CallJSNative(cx, clasp->call, args.argc(), args.base());
699 /* Invoke native functions. */
700 JSFunction *fun = callee.getFunctionPrivate();
701 JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !fun->isConstructor());
703 return CallJSNative(cx, fun->u.n.native, args.argc(), args.base());
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);
712 args.rval().setObject(*obj);
714 args.rval().setUndefined();
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)))
724 /* Initialize frame, locals. */
725 JSStackFrame *fp = frame.fp();
726 fp->initCallFrame(cx, callee, fun, args.argc(), flags);
727 SetValueRangeToUndefined(fp->slots(), script->nfixed);
729 /* Officially push fp. frame's destructor pops. */
730 cx->stack().pushInvokeFrame(cx, args, &frame);
732 /* Now that the new frame is rooted, maybe create a call object. */
733 if (fun->isHeavyweight() && !js_GetCallObject(cx, fp))
736 /* Run function until JSOP_STOP, JSOP_RETURN or error. */
739 AutoPreserveEnumerators preserve(cx);
740 ok = RunScript(cx, script, fp);
743 args.rval() = fp->returnValue();
744 JS_ASSERT_IF(ok && (flags & JSINVOKE_CONSTRUCT), !args.rval().isPrimitive());
750 InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &thisv, uintN argc)
753 if (TRACE_RECORDER(cx))
754 AbortRecording(cx, "attempt to reenter VM while recording");
758 /* Always push arguments, regardless of optimized/normal invoke. */
759 StackSpace &stack = cx->stack();
760 if (!stack.pushInvokeArgs(cx, argc, &args_))
763 /* Callees may clobber 'this' or 'callee'. */
764 savedCallee_ = args_.callee() = calleev;
765 savedThis_ = args_.thisv() = thisv;
768 /* Hoist dynamic checks from scripted Invoke. */
769 if (!calleev.isObject())
771 JSObject &callee = calleev.toObject();
772 if (callee.getClass() != &js_FunctionClass)
774 JSFunction *fun = callee.getFunctionPrivate();
777 script_ = fun->script();
778 if (fun->isHeavyweight() || script_->isEmpty())
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.
785 if (callee.getGlobal() != GetGlobalForScopeChain(cx))
788 /* Push the stack frame once for the session. */
790 if (!stack.getInvokeFrame(cx, args_, fun, script_, &flags, &frame_))
792 JSStackFrame *fp = frame_.fp();
793 fp->initCallFrame(cx, calleev.toObject(), fun, argc, flags);
794 stack.pushInvokeFrame(cx, args_, &frame_);
797 /* Hoist dynamic checks from RunScript. */
798 mjit::CompileStatus status = mjit::CanMethodJIT(cx, script_, fp, mjit::CompileRequest_JIT);
799 if (status == mjit::Compile_Error)
801 if (status != mjit::Compile_Okay)
803 /* Cannot also cache the raw code pointer; it can change. */
805 /* Hoist dynamic checks from CheckStackAndEnterMethodJIT. */
806 JS_CHECK_RECURSION(cx, return JS_FALSE);
807 stackLimit_ = stack.getStackLimit(cx);
811 stop_ = script_->code + script_->length - 1;
812 JS_ASSERT(*stop_ == JSOP_STOP);
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());
824 * Use the normal invoke path.
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.
832 formals_ = actuals_ = args_.argv();
833 nformals_ = (unsigned)-1;
838 ExternalInvoke(JSContext *cx, const Value &thisv, const Value &fval,
839 uintN argc, Value *argv, Value *rval)
843 InvokeArgsGuard args;
844 if (!cx->stack().pushInvokeArgs(cx, argc, &args))
847 args.callee() = fval;
848 args.thisv() = thisv;
849 memcpy(args.argv(), argv, argc * sizeof(Value));
851 if (args.thisv().isObject()) {
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
857 JSObject *thisp = args.thisv().toObject().thisObject(cx);
860 args.thisv().setObject(*thisp);
863 if (!Invoke(cx, args, 0))
871 ExternalInvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *argv,
876 InvokeArgsGuard args;
877 if (!cx->stack().pushInvokeArgs(cx, argc, &args))
880 args.callee() = fval;
881 args.thisv().setMagic(JS_THIS_POISON);
882 memcpy(args.argv(), argv, argc * sizeof(Value));
884 if (!InvokeConstructor(cx, args))
892 ExternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, const Value &fval,
893 JSAccessMode mode, uintN argc, Value *argv, Value *rval)
898 * ExternalInvoke could result in another try to get or set the same id
899 * again, see bug 355497.
901 JS_CHECK_RECURSION(cx, return JS_FALSE);
903 return ExternalInvoke(cx, ObjectValue(*obj), fval, argc, argv, rval);
907 Execute(JSContext *cx, JSObject *chain, JSScript *script,
908 JSStackFrame *prev, uintN flags, Value *result)
911 JS_ASSERT_IF(prev, !prev->isDummyFrame());
913 if (script->isEmpty()) {
915 result->setUndefined();
922 * Get a pointer to new frame/slots. This memory is not "claimed", so the
923 * code before pushExecuteFrame must not reenter the interpreter.
925 ExecuteFrameGuard frame;
926 if (!cx->stack().getExecuteFrame(cx, script, &frame))
929 /* Initialize fixed slots (GVAR ops expect NULL). */
930 SetValueRangeToNull(frame.fp()->slots(), script->nfixed);
932 /* Initialize frame and locals. */
933 JSObject *initialVarObj;
935 JS_ASSERT(chain == &prev->scopeChain());
936 frame.fp()->initEvalFrame(cx, script, prev, flags);
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.
945 initialVarObj = (prev == cx->maybefp())
947 : &prev->varobj(cx->containingSegment(prev));
949 /* The scope chain could be anything, so innerize just in case. */
950 JSObject *innerizedChain = chain;
951 OBJ_TO_INNER_OBJECT(cx, innerizedChain);
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);
962 /* Initialize frame. */
963 frame.fp()->initGlobalFrame(script, *innerizedChain, flags);
965 /* Compute 'this'. */
966 JSObject *thisp = chain->thisObject(cx);
969 frame.fp()->globalThis().setObject(*thisp);
971 initialVarObj = cx->hasRunOption(JSOPTION_VAROBJFIX) ? chain->getGlobal() : chain;
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.
978 if ((flags & JSFRAME_EVAL) && script->strictModeCode) {
979 AutoScriptRooter root(cx, script);
980 initialVarObj = NewCallObject(cx, &script->bindings, *initialVarObj, NULL);
983 initialVarObj->setPrivate(frame.fp());
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);
990 JS_ASSERT(!initialVarObj->getOps()->defineProperty);
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;
1004 sharps[0] = prev->slots()[base];
1005 sharps[1] = prev->slots()[base + 1];
1007 sharps[0].setUndefined();
1008 sharps[1].setUndefined();
1013 /* Officially push |fp|. |frame|'s destructor pops. */
1014 cx->stack().pushExecuteFrame(cx, initialVarObj, &frame);
1016 /* Now that the frame has been pushed, we can call the thisObject hook. */
1018 JSObject *thisp = chain->thisObject(cx);
1021 frame.fp()->globalThis().setObject(*thisp);
1024 Probes::startExecution(cx, script);
1026 /* Run script until JSOP_STOP or error. */
1027 AutoPreserveEnumerators preserve(cx);
1028 JSBool ok = RunScript(cx, script, frame.fp());
1030 *result = frame.fp()->returnValue();
1032 Probes::stopExecution(cx, script);
1038 CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs)
1044 const char *type, *name;
1046 if (!obj->lookupProperty(cx, id, &obj2, &prop))
1050 if (obj2->isNative()) {
1051 oldAttrs = ((Shape *) prop)->attributes();
1053 if (!obj2->getAttributes(cx, id, &oldAttrs))
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)))
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:
1068 * no-property --> getter --> getter + setter
1069 * no-property --> setter --> getter + setter
1071 if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0)
1075 * Allow redeclaration of an impermanent property (in which case
1076 * anyone could delete it and redefine it, willy-nilly).
1078 if (!(oldAttrs & JSPROP_PERMANENT))
1082 isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
1085 if (!obj->getProperty(cx, id, &value))
1087 isFunction = IsFunctionObject(value);
1090 type = (oldAttrs & attrs & JSPROP_GETTER)
1092 : (oldAttrs & attrs & JSPROP_SETTER)
1094 : (oldAttrs & JSPROP_READONLY)
1099 JSAutoByteString bytes;
1100 name = js_ValueToPrintable(cx, IdToValue(id), &bytes);
1103 JS_ALWAYS_FALSE(JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
1104 JSMSG_REDECLARED_VAR, type, name));
1109 HasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
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);
1120 StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref, JSBool *equal)
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);
1130 if (lval.isObject()) {
1131 *equal = &lval.toObject() == &rval.toObject();
1134 if (lval.isUndefined()) {
1138 *equal = lval.payloadAsRawUint32() == rval.payloadAsRawUint32();
1142 if (lval.isDouble() && rval.isInt32()) {
1143 double ld = lval.toDouble();
1144 double rd = rval.toInt32();
1145 *equal = JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1148 if (lval.isInt32() && rval.isDouble()) {
1149 double ld = lval.toInt32();
1150 double rd = rval.toDouble();
1151 *equal = JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1160 IsNegativeZero(const Value &v)
1162 return v.isDouble() && JSDOUBLE_IS_NEGZERO(v.toDouble());
1166 IsNaN(const Value &v)
1168 return v.isDouble() && JSDOUBLE_IS_NaN(v.toDouble());
1172 SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same)
1174 if (IsNegativeZero(v1)) {
1175 *same = IsNegativeZero(v2);
1178 if (IsNegativeZero(v2)) {
1182 if (IsNaN(v1) && IsNaN(v2)) {
1186 return StrictlyEqual(cx, v1, v2, same);
1190 TypeOfValue(JSContext *cx, const Value &vref)
1194 return JSTYPE_NUMBER;
1196 return JSTYPE_STRING;
1198 return JSTYPE_OBJECT;
1199 if (v.isUndefined())
1202 return v.toObject().typeOf(cx);
1203 JS_ASSERT(v.isBoolean());
1204 return JSTYPE_BOOLEAN;
1208 InstanceOfSlow(JSContext *cx, JSObject *obj, Class *clasp, Value *argv)
1210 JS_ASSERT(!obj || obj->getClass() != clasp);
1212 JSFunction *fun = js_ValueToFunction(cx, &argv[-2], 0);
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);
1225 JS_REQUIRES_STACK bool
1226 InvokeConstructor(JSContext *cx, const CallArgs &argsRef)
1228 JS_ASSERT(!js_FunctionClass.construct);
1229 CallArgs args = argsRef;
1232 if (args.callee().isPrimitive() || !(callee = &args.callee().toObject())->getParent()) {
1233 js_ReportIsNotFunction(cx, &args.callee(), JSV2F_CONSTRUCT);
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());
1246 } else if (clasp->construct) {
1247 args.thisv().setMagicWithObjectOrNullPayload(NULL);
1248 return CallJSNativeConstructor(cx, clasp->construct, args.argc(), args.base());
1251 /* Scripts create their own |this| in JSOP_BEGIN */
1252 if (!fun || !fun->isInterpreted()) {
1253 JSObject *obj = js_CreateThis(cx, callee);
1256 args.thisv().setObject(*obj);
1259 if (!Invoke(cx, args, JSINVOKE_CONSTRUCT))
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());
1273 /* The interpreter fixes rval for us. */
1274 JS_ASSERT(!fun->isInterpreted());
1276 args.rval() = args.thisv();
1279 JS_RUNTIME_METER(cx->runtime, constructs);
1284 InvokeConstructorWithGivenThis(JSContext *cx, JSObject *thisobj, const Value &fval,
1285 uintN argc, Value *argv, Value *rval)
1289 InvokeArgsGuard args;
1290 if (!cx->stack().pushInvokeArgs(cx, argc, &args))
1293 args.callee() = fval;
1294 /* Initialize args.thisv on all paths below. */
1295 memcpy(args.argv(), argv, argc * sizeof(Value));
1297 /* Handle the fast-constructor cases before calling the general case. */
1298 JSObject &callee = fval.toObject();
1299 Class *clasp = callee.getClass();
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());
1309 args.thisv().setObjectOrNull(thisobj);
1310 ok = Invoke(cx, args, JSINVOKE_CONSTRUCT);
1313 *rval = args.rval();
1318 DirectEval(JSContext *cx, JSFunction *evalfun, uint32 argc, Value *vp)
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));
1326 JSStackFrame *caller = cx->fp();
1327 JS_ASSERT(caller->isScriptFrame());
1328 AutoFunctionCallProbe callProbe(cx, evalfun, caller->script());
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))
1334 cx->regs->sp = vp + 1;
1339 ValueToId(JSContext *cx, const Value &v, jsid *idp)
1342 if (ValueFitsInInt32(v, &i) && INT_FITS_IN_JSID(i)) {
1343 *idp = INT_TO_JSID(i);
1347 #if JS_HAS_XML_SUPPORT
1349 JSObject *obj = &v.toObject();
1350 if (obj->isXMLId()) {
1351 *idp = OBJECT_TO_JSID(obj);
1357 return js_ValueToStringId(cx, v, idp);
1360 } /* namespace js */
1363 * Enter the new with scope using an object at sp[-1] and associate the depth
1364 * of the with block with sp + stackIndex.
1366 JS_STATIC_INTERPRET JS_REQUIRES_STACK JSBool
1367 js_EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen)
1369 JSStackFrame *fp = cx->fp();
1370 Value *sp = cx->regs->sp;
1371 JS_ASSERT(stackIndex < 0);
1372 JS_ASSERT(fp->base() <= sp + stackIndex);
1375 if (sp[-1].isObject()) {
1376 obj = &sp[-1].toObject();
1378 obj = js_ValueToNonNullObject(cx, sp[-1]);
1381 sp[-1].setObject(*obj);
1384 JSObject *parent = GetScopeChainFast(cx, fp, op, oplen);
1388 OBJ_TO_INNER_OBJECT(cx, obj);
1392 JSObject *withobj = js_NewWithObject(cx, obj, parent,
1393 sp + stackIndex - fp->base());
1397 fp->setScopeChainNoCallObj(*withobj);
1401 JS_STATIC_INTERPRET JS_REQUIRES_STACK void
1402 js_LeaveWith(JSContext *cx)
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());
1414 JS_REQUIRES_STACK Class *
1415 js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth)
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) {
1429 * Unwind block and scope chains to match the given depth. The function sets
1430 * fp->sp on return to stackDepth.
1432 JS_REQUIRES_STACK JSBool
1433 js_UnwindScope(JSContext *cx, jsint stackDepth, JSBool normalUnwind)
1437 JS_ASSERT(stackDepth >= 0);
1438 JS_ASSERT(cx->fp()->base() + stackDepth <= cx->regs->sp);
1440 JSStackFrame *fp = cx->fp();
1442 clasp = js_IsActiveWithOrBlock(cx, &fp->scopeChain(), stackDepth);
1445 if (clasp == &js_BlockClass) {
1446 /* Don't fail until after we've updated all stacks. */
1447 normalUnwind &= js_PutBlockObject(cx, normalUnwind);
1453 cx->regs->sp = fp->base() + stackDepth;
1454 return normalUnwind;
1457 JS_STATIC_INTERPRET JSBool
1458 js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, Value *vp, Value *vp2)
1460 if (cs->format & JOF_POST) {
1462 if (!ValueToNumber(cx, *vp, &d))
1465 (cs->format & JOF_INC) ? ++d : --d;
1471 if (!ValueToNumber(cx, *vp, &d))
1473 (cs->format & JOF_INC) ? ++d : --d;
1480 js::GetUpvar(JSContext *cx, uintN closureLevel, UpvarCookie cookie)
1482 JS_ASSERT(closureLevel >= cookie.level() && cookie.level() > 0);
1483 const uintN targetLevel = closureLevel - cookie.level();
1484 JS_ASSERT(targetLevel < UpvarCookie::UPVAR_LEVEL_LIMIT);
1486 JSStackFrame *fp = cx->findFrameAtLevel(targetLevel);
1487 uintN slot = cookie.slot();
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();
1498 slot -= fp->numFormalArgs();
1499 JS_ASSERT(slot < fp->numSlots());
1508 JS_STATIC_INTERPRET JS_REQUIRES_STACK void
1509 js_LogOpcode(JSContext *cx)
1514 intN ndefs, n, nuses;
1517 logfp = (FILE *) cx->logfp;
1523 * Operations in prologues don't produce interesting values, and
1524 * js_DecompileValueGenerator isn't set up to handle them anyway.
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);
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
1537 ndefs < regs->sp - fp->slots()) {
1538 for (n = -ndefs; n < 0; n++) {
1539 char *bytes = DecompileValueGenerator(cx, n, regs->sp[n], NULL);
1541 fprintf(logfp, "%s %s",
1542 (n == -ndefs) ? " output:" : ",",
1546 JS_ClearPendingException(cx);
1549 fprintf(logfp, " @ %u\n", (uintN) (regs->sp - fp->base()));
1551 fprintf(logfp, " stack: ");
1552 for (Value *siter = fp->base(); siter < regs->sp; siter++) {
1553 if (siter->isObject() && siter->toObject().getClass() == &js_CallClass) {
1555 * Call objects have NULL convert ops so that we catch cases
1556 * where they escape. So js_ValueToString doesn't work on them.
1558 fputs("<call>", logfp);
1560 JSString *str = js_ValueToString(cx, *siter);
1561 JSLinearString *linearStr = str ? str->ensureLinear(cx) : NULL;
1563 fputs("<null>", logfp);
1564 JS_ClearPendingException(cx);
1566 FileEscapedString(logfp, linearStr, 0);
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,
1580 op = (JSOp) *regs->pc;
1581 nuses = js_GetStackUses(&js_CodeSpec[op], op, regs->pc);
1583 for (n = -nuses; n < 0; n++) {
1584 char *bytes = DecompileValueGenerator(cx, n, regs->sp[n], NULL);
1586 fprintf(logfp, "%s %s",
1587 (n == -nuses) ? " inputs:" : ",",
1591 JS_ClearPendingException(cx);
1594 fprintf(logfp, " @ %u\n", (uintN) (regs->sp - fp->base()));
1596 cx->logPrevPc = regs->pc;
1598 /* It's nice to have complete logs when debugging a crash. */
1606 # include <stdlib.h>
1608 # define HIST_NSLOTS 8
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.
1615 static uint32 succeeds[JSOP_LIMIT][256];
1616 static uint32 slot_ops[JSOP_LIMIT][HIST_NSLOTS];
1618 JS_STATIC_INTERPRET void
1619 js_MeterOpcodePair(JSOp op1, JSOp op2)
1621 if (op1 != JSOP_STOP)
1622 ++succeeds[op1][op2];
1625 JS_STATIC_INTERPRET void
1626 js_MeterSlotOpcode(JSOp op, uint32 slot)
1628 if (slot < HIST_NSLOTS)
1629 ++slot_ops[op][slot];
1632 typedef struct Edge {
1639 compare_edges(const void *a, const void *b)
1641 const Edge *ea = (const Edge *) a;
1642 const Edge *eb = (const Edge *) b;
1644 return (int32)eb->count - (int32)ea->count;
1650 const char *name, *from, *style;
1652 uint32 total, count;
1653 uint32 i, j, nedges;
1656 name = getenv("JS_OPMETER_FILE");
1658 name = "/tmp/ops.dot";
1659 fp = fopen(name, "w");
1666 for (i = 0; i < JSOP_LIMIT; i++) {
1667 for (j = 0; j < JSOP_LIMIT; j++) {
1668 count = succeeds[i][j];
1676 # define SIGNIFICANT(count,total) (200. * (count) >= (total))
1678 graph = (Edge *) js_calloc(nedges * sizeof graph[0]);
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;
1693 qsort(graph, nedges, sizeof(Edge), compare_edges);
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";
1705 fprintf(fp, " %s -> %s [label=\"%lu\" style=%s]\n",
1706 graph[i].from, graph[i].to,
1707 (unsigned long)graph[i].count, style);
1713 name = getenv("JS_OPMETER_HIST");
1715 name = "/tmp/ops.hist";
1716 fp = fopen(name, "w");
1721 fputs("bytecode", fp);
1722 for (j = 0; j < HIST_NSLOTS; j++)
1723 fprintf(fp, " slot %1u", (unsigned)j);
1725 fputs("========", fp);
1726 for (j = 0; j < HIST_NSLOTS; j++)
1727 fputs(" =======", 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]);
1744 #endif /* JS_OPSMETER */
1746 #endif /* !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___ */
1748 #ifndef jsinvoke_cpp___
1751 // jsval representation metering: this measures the kinds of jsvals that
1752 // are used as inputs to each JSOp.
1753 namespace reprmeter {
1763 FUNCTION_INTERPRETED,
1764 FUNCTION_FASTNATIVE,
1769 // Return the |repr| value giving the representation of the given jsval.
1773 if (JSVAL_IS_INT(v))
1775 if (JSVAL_IS_DOUBLE(v))
1777 if (JSVAL_IS_SPECIAL(v)) {
1778 return (v == JSVAL_TRUE || v == JSVAL_FALSE)
1782 if (JSVAL_IS_STRING(v))
1785 JS_ASSERT(JSVAL_IS_OBJECT(v));
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;
1794 // This must come before the general array test, because that
1795 // one subsumes this one.
1798 if (obj->isDenseArray())
1802 return OBJECT_PLAIN;
1805 static const char *reprName[] = { "invalid", "int", "double", "bool", "special",
1806 "string", "null", "object",
1807 "fun:interp", "fun:native"
1808 "array:slow", "array:dense" };
1810 // Logically, a tuple of (JSOp, repr_1, ..., repr_n) where repr_i is
1811 // the |repr| of the ith input to the JSOp.
1813 enum { max_uses = 16 };
1816 Repr uses[max_uses];
1818 OpInput() : op(JSOp(255)) {
1819 for (int i = 0; i < max_uses; ++i)
1823 OpInput(JSOp op) : op(op) {
1824 for (int i = 0; i < max_uses; ++i)
1829 operator uint32() const {
1831 for (int i = 0; i < max_uses; ++i)
1832 h = h * 7 + uses[i] * 13;
1836 bool operator==(const OpInput &opinput) const {
1837 if (op != opinput.op)
1839 for (int i = 0; i < max_uses; ++i) {
1840 if (uses[i] != opinput.uses[i])
1846 OpInput &operator=(const OpInput &opinput) {
1848 for (int i = 0; i < max_uses; ++i)
1849 uses[i] = opinput.uses[i];
1854 typedef HashMap<OpInput, uint64, DefaultHasher<OpInput>, SystemAllocPolicy> OpInputHistogram;
1856 OpInputHistogram opinputs;
1857 bool opinputsInitialized = false;
1859 // Record an OpInput for the current op. This should be called just
1860 // before executing the op.
1862 MeterRepr(JSContext *cx)
1864 // Note that we simply ignore the possibility of errors (OOMs)
1865 // using the hash map, since this is only metering code.
1867 if (!opinputsInitialized) {
1869 opinputsInitialized = true;
1872 JSOp op = JSOp(*cx->regs->pc);
1873 int nuses = js_GetStackUses(&js_CodeSpec[op], op, cx->regs->pc);
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);
1882 OpInputHistogram::AddPtr p = opinputs.lookupForAdd(opinput);
1886 opinputs.add(p, opinput, 1);
1892 FILE *f = fopen("/tmp/reprmeter.txt", "w");
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);
1905 #endif /* JS_REPRMETER */
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)
1920 #define POP_BOOLEAN(cx, vp, b) \
1922 vp = ®s.sp[-1]; \
1923 if (vp->isNull()) { \
1925 } else if (vp->isBoolean()) { \
1926 b = vp->toBoolean(); \
1928 b = !!js_ValueToBoolean(*vp); \
1933 #define VALUE_TO_OBJECT(cx, vp, obj) \
1935 if ((vp)->isObject()) { \
1936 obj = &(vp)->toObject(); \
1938 obj = js_ValueToNonNullObject(cx, *(vp)); \
1941 (vp)->setObject(*obj); \
1945 #define FETCH_OBJECT(cx, n, obj) \
1947 Value *vp_ = ®s.sp[n]; \
1948 VALUE_TO_OBJECT(cx, vp_, obj); \
1951 #define DEFAULT_VALUE(cx, n, hint, v) \
1953 JS_ASSERT(v.isObject()); \
1954 JS_ASSERT(v == regs.sp[n]); \
1955 if (!DefaultValue(cx, &v.toObject(), hint, ®s.sp[n])) \
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)
1964 return (i > JSVAL_INT_MIN) && (i < JSVAL_INT_MAX);
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.
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.
1978 # define METER_OP_INIT(op) /* nothing */
1979 # define METER_OP_PAIR(op1,op2) /* nothing */
1980 # define METER_SLOT_OP(op,slot) /* nothing */
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.
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))
1995 # define METER_REPR(cx) (reprmeter::MeterRepr(cx))
1997 # define METER_REPR(cx) ((void) 0)
1998 #endif /* JS_REPRMETER */
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.
2007 #ifndef JS_THREADED_INTERP
2008 # if JS_VERSION >= 160 && ( \
2010 (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \
2011 __SUNPRO_C >= 0x570)
2012 # define JS_THREADED_INTERP 1
2014 # define JS_THREADED_INTERP 0
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.
2022 #if defined DEBUG && !defined JS_THREADSAFE
2024 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) \
2026 if (!AssertValidPropertyCacheHit(cx, script, regs, pcoff, obj, pobj, \
2033 AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
2034 ptrdiff_t pcoff, JSObject *start, JSObject *found,
2035 PropertyCacheEntry *entry)
2037 uint32 sample = cx->runtime->gcNumber;
2041 GET_ATOM_FROM_BYTECODE(script, regs.pc, pcoff, atom);
2043 atom = cx->runtime->atomState.lengthAtom;
2045 JSObject *obj, *pobj;
2049 if (JOF_OPMODE(*regs.pc) == JOF_NAME) {
2050 ok = js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &pobj, &prop);
2053 ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop);
2057 if (cx->runtime->gcNumber != sample || entry->vshape() != pobj->shape())
2060 JS_ASSERT(pobj == found);
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());
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());
2080 if (shape->isMethod()) {
2081 JS_ASSERT(js_CodeSpec[*regs.pc].format & JOF_CALLOP);
2082 JS_ASSERT(&shape->methodObject() == &v.toObject());
2090 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0)
2094 * Ensure that the intrepreter switch can close call-bytecode cases in the
2095 * same way as non-call bytecodes.
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);
2106 * Same for debuggable flat closures defined at top level in another function
2107 * or program fragment.
2109 JS_STATIC_ASSERT(JSOP_DEFFUN_FC_LENGTH == JSOP_DEFFUN_DBGFC_LENGTH);
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}.
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);
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);
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);
2129 # define ABORT_RECORDING(cx, reason) \
2131 if (TRACE_RECORDER(cx)) \
2132 AbortRecording(cx, reason); \
2135 # define ABORT_RECORDING(cx, reason) ((void) 0)
2139 * Inline fast paths for iteration. js_IteratorMore and js_IteratorNext handle
2140 * all cases, but we inline the most frequently taken paths here.
2143 IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, Value *rval)
2145 if (iterobj->getClass() == &js_IteratorClass) {
2146 NativeIterator *ni = (NativeIterator *) iterobj->getPrivate();
2147 if (ni->isKeyIter()) {
2148 *cond = (ni->props_cursor < ni->props_end);
2152 if (!js_IteratorMore(cx, iterobj, rval))
2154 *cond = rval->isTrue();
2159 IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
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));
2171 /* Take the slow path if we have to stringify a numeric property name. */
2174 return js_IteratorNext(cx, iterobj, rval);
2178 ScriptPrologue(JSContext *cx, JSStackFrame *fp)
2180 if (fp->isConstructing()) {
2181 JSObject *obj = js_CreateThisForFunction(cx, &fp->callee());
2184 fp->functionThis().setObject(*obj);
2186 if (fp->isExecuteFrame()) {
2187 if (JSInterpreterHook hook = cx->debugHooks->executeHook)
2188 fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->executeHookData));
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());
2201 static JS_NEVER_INLINE bool
2202 NEVER_INLINE_ComputeImplicitThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp)
2204 return ComputeImplicitThis(cx, obj, funval, vp);
2206 #define ComputeImplicitThis(cx, obj, funval, vp) NEVER_INLINE_ComputeImplicitThis(cx, obj, funval, vp)
2209 JS_REQUIRES_STACK JS_NEVER_INLINE bool
2210 Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInterpMode interpMode)
2213 TraceVisStateObj tvso(cx, S_INTERP);
2215 JSAutoResolveFlags rf(cx, JSRESOLVE_INFER);
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.
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.)
2232 # define LOG_OPCODE(OP) JS_BEGIN_MACRO \
2233 if (JS_UNLIKELY(cx->logfp != NULL) && \
2238 # define LOG_OPCODE(OP) ((void) 0)
2242 * Macros for threaded interpreter loop
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"
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"
2259 register void * const *jumpTable = normalJumpTable;
2261 METER_OP_INIT(op); /* to nullify first METER_OP_PAIR */
2263 # define ENABLE_INTERRUPTS() ((void) (jumpTable = interruptJumpTable))
2266 # define CHECK_RECORDER() \
2267 JS_ASSERT_IF(TRACE_RECORDER(cx), jumpTable == interruptJumpTable)
2269 # define CHECK_RECORDER() ((void)0)
2272 # define DO_OP() JS_BEGIN_MACRO \
2274 JS_EXTENSION_(goto *jumpTable[op]); \
2276 # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
2277 METER_OP_PAIR(op, JSOp(regs.pc[n])); \
2278 op = (JSOp) *(regs.pc += (n)); \
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; \
2291 # define END_EMPTY_CASES
2293 #else /* !JS_THREADED_INTERP */
2295 register intN switchMask = 0;
2298 # define ENABLE_INTERRUPTS() ((void) (switchMask = -1))
2301 # define CHECK_RECORDER() \
2302 JS_ASSERT_IF(TRACE_RECORDER(cx), switchMask == -1)
2304 # define CHECK_RECORDER() ((void)0)
2307 # define DO_OP() goto do_op
2308 # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
2309 JS_ASSERT((n) == len); \
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
2319 * To share the code for all len == 1 cases we use the specialized label with
2320 * code that falls through to advance_pc: .
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;
2331 #endif /* !JS_THREADED_INTERP */
2333 #define LOAD_ATOM(PCOFF, atom) \
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)]; \
2344 #define GET_FULL_INDEX(PCOFF) \
2345 (atoms - script->atomMap.vector + GET_INDEX(regs.pc + (PCOFF)))
2347 #define LOAD_OBJECT(PCOFF, obj) \
2348 (obj = script->getObject(GET_FULL_INDEX(PCOFF)))
2350 #define LOAD_FUNCTION(PCOFF) \
2351 (fun = script->getFunction(GET_FULL_INDEX(PCOFF)))
2353 #define LOAD_DOUBLE(PCOFF, dbl) \
2354 (dbl = script->getConst(GET_FULL_INDEX(PCOFF)).toDouble())
2356 bool useMethodJIT = false;
2360 #define RESET_USE_METHODJIT() \
2362 useMethodJIT = cx->methodJitEnabled && \
2363 interpMode == JSINTERP_NORMAL && \
2364 script->getJITStatus(regs.fp->isConstructing()) != JITScript_Invalid; \
2367 #define MONITOR_BRANCH_METHODJIT() \
2369 mjit::CompileStatus status = \
2370 mjit::CanMethodJITAtBranch(cx, script, regs.fp, regs.pc); \
2371 if (status == mjit::Compile_Error) \
2373 if (status == mjit::Compile_Okay) { \
2375 script->nativeCodeForPC(regs.fp->isConstructing(), regs.pc); \
2376 interpReturnOK = mjit::JaegerShotAtSafePoint(cx, ncode); \
2377 if (inlineCallCount) \
2379 regs.fp->setFinishedInInterpreter(); \
2380 goto leave_on_safe_point; \
2382 if (status == mjit::Compile_Abort) { \
2383 useMethodJIT = false; \
2389 #define RESET_USE_METHODJIT() ((void) 0)
2391 #define MONITOR_BRANCH_METHODJIT() ((void) 0)
2398 #if JS_THREADED_INTERP
2399 #define MONITOR_BRANCH_TRACEVIS \
2401 if (jumpTable != interruptJumpTable) \
2402 EnterTraceVisState(cx, S_RECORD, R_NONE); \
2404 #else /* !JS_THREADED_INTERP */
2405 #define MONITOR_BRANCH_TRACEVIS \
2407 EnterTraceVisState(cx, S_RECORD, R_NONE); \
2411 #define MONITOR_BRANCH_TRACEVIS
2414 #define RESTORE_INTERP_VARS() \
2416 script = regs.fp->script(); \
2417 argv = regs.fp->maybeFormalArgs(); \
2418 atoms = FrameAtomBase(cx, regs.fp); \
2419 JS_ASSERT(cx->regs == ®s); \
2420 if (cx->isExceptionPending()) \
2424 #define MONITOR_BRANCH() \
2426 if (TRACING_ENABLED(cx)) { \
2427 if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && useMethodJIT) { \
2428 MONITOR_BRANCH_METHODJIT(); \
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(); \
2438 RESTORE_INTERP_VARS(); \
2439 JS_ASSERT_IF(cx->isExceptionPending(), r == MONITOR_ERROR); \
2440 if (r == MONITOR_ERROR) \
2444 MONITOR_BRANCH_METHODJIT(); \
2448 #else /* !JS_TRACER */
2450 #define MONITOR_BRANCH() \
2452 MONITOR_BRANCH_METHODJIT(); \
2455 #endif /* !JS_TRACER */
2458 * Prepare to call a user-supplied branch handler, and abort the script
2459 * if it returns false.
2461 #define CHECK_BRANCH() \
2463 if (JS_THREAD_DATA(cx)->interruptFlags && !js_HandleExecutionInterrupt(cx)) \
2467 #if defined(JS_TRACER) && defined(JS_METHODJIT)
2468 # define LEAVE_ON_SAFE_POINT() \
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; \
2481 # define LEAVE_ON_SAFE_POINT() /* nop */
2487 op = (JSOp) *regs.pc; \
2490 if (op == JSOP_NOTRACE) { \
2491 if (TRACE_RECORDER(cx) || TRACE_PROFILER(cx)) { \
2493 op = (JSOp) *regs.pc; \
2495 } else if (op == JSOP_TRACE) { \
2497 op = (JSOp) *regs.pc; \
2500 LEAVE_ON_SAFE_POINT(); \
2504 #define CHECK_INTERRUPT_HANDLER() \
2506 if (cx->debugHooks->interruptHook) \
2507 ENABLE_INTERRUPTS(); \
2510 JSFrameRegs regs = *cx->regs;
2512 /* Repoint cx->regs to a local variable for faster access. */
2513 struct InterpExitGuard {
2515 const JSFrameRegs ®s;
2516 JSFrameRegs *prevContextRegs;
2517 InterpExitGuard(JSContext *cx, JSFrameRegs ®s)
2518 : cx(cx), regs(regs), prevContextRegs(cx->regs) {
2519 cx->setCurrentRegs(®s);
2522 ~InterpExitGuard() {
2524 JS_ASSERT(cx->regs == ®s);
2525 *prevContextRegs = regs;
2526 cx->setCurrentRegs(prevContextRegs);
2528 } interpGuard(cx, regs);
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();
2536 #if defined(JS_TRACER) && defined(JS_METHODJIT)
2537 bool leaveOnSafePoint = (interpMode == JSINTERP_SAFEPOINT);
2538 # define CLEAR_LEAVE_ON_TRACE_POINT() ((void) (leaveOnSafePoint = false))
2540 # define CLEAR_LEAVE_ON_TRACE_POINT() ((void) 0)
2544 entryFrame = regs.fp;
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.
2553 JSAtom **atoms = script->atomMap.vector;
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));
2562 * To support generator_throw and to catch ignored exceptions,
2563 * fail if cx->isExceptionPending() is true.
2565 if (cx->isExceptionPending())
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.
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");
2586 if (regs.fp->hasImacropc())
2587 atoms = COMMON_ATOMS_START(&rt->atomState);
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))
2597 CHECK_INTERRUPT_HANDLER();
2599 RESET_USE_METHODJIT();
2601 /* State communicated between non-local jumps: */
2602 JSBool interpReturnOK;
2603 JSAtom *atomNotDefined;
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.
2615 /* Check for too deep of a native thread stack. */
2618 JS_CHECK_RECURSION(cx, do {
2619 if (TRACE_RECORDER(cx))
2620 AbortRecording(cx, "too much recursion");
2621 if (TRACE_PROFILER(cx))
2626 JS_CHECK_RECURSION(cx, do {
2627 if (TRACE_RECORDER(cx))
2628 AbortRecording(cx, "too much recursion");
2633 JS_CHECK_RECURSION(cx, return JS_FALSE);
2636 #if JS_THREADED_INTERP
2642 #if JS_THREADED_INTERP
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.
2652 #else /* !JS_THREADED_INTERP */
2655 JS_ASSERT(js_CodeSpec[op].length == 1);
2659 op = (JSOp) *regs.pc;
2664 switchOp = intN(op) | switchMask;
2669 #if JS_THREADED_INTERP
2671 #else /* !JS_THREADED_INTERP */
2673 JS_ASSERT(switchMask == -1);
2674 #endif /* !JS_THREADED_INTERP */
2676 bool moreInterrupts = false;
2677 JSInterruptHook hook = cx->debugHooks->interruptHook;
2680 if (TRACE_RECORDER(cx))
2681 AbortRecording(cx, "interrupt hook");
2683 if (TRACE_PROFILER(cx))
2688 switch (hook(cx, script, regs.pc, Jsvalify(&rval),
2689 cx->debugHooks->interruptHookData)) {
2692 case JSTRAP_CONTINUE:
2695 regs.fp->setReturnValue(rval);
2696 interpReturnOK = JS_TRUE;
2699 cx->setPendingException(rval);
2703 moreInterrupts = true;
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);
2713 case LoopProfile::ProfComplete:
2714 if (interpMode != JSINTERP_NORMAL) {
2715 leaveOnSafePoint = true;
2716 LEAVE_ON_SAFE_POINT();
2720 moreInterrupts = true;
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);
2730 if (interpMode != JSINTERP_NORMAL) {
2731 JS_ASSERT(interpMode == JSINTERP_RECORD || JSINTERP_SAFEPOINT);
2733 case ARECORD_IMACRO_ABORTED:
2734 case ARECORD_ABORTED:
2735 case ARECORD_COMPLETED:
2738 leaveOnSafePoint = true;
2739 LEAVE_ON_SAFE_POINT();
2748 case ARECORD_CONTINUE:
2749 moreInterrupts = true;
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. */
2760 // The code at 'error:' aborts the recording.
2762 case ARECORD_ABORTED:
2763 case ARECORD_COMPLETED:
2766 /* A 'stop' error should have already aborted recording. */
2768 JS_NOT_REACHED("Bad recording status");
2771 #endif /* !JS_TRACER */
2773 #if JS_THREADED_INTERP
2775 if (!moreInterrupts)
2776 ExitTraceVisState(cx, R_ABORT);
2778 jumpTable = moreInterrupts ? interruptJumpTable : normalJumpTable;
2779 JS_EXTENSION_(goto *normalJumpTable[op]);
2781 switchMask = moreInterrupts ? -1 : 0;
2782 switchOp = intN(op);
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)
2795 ADD_EMPTY_CASE(JSOP_NULLBLOCKCHAIN)
2798 BEGIN_CASE(JSOP_TRACE)
2799 BEGIN_CASE(JSOP_NOTRACE)
2800 LEAVE_ON_SAFE_POINT();
2801 END_CASE(JSOP_TRACE)
2803 /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
2804 BEGIN_CASE(JSOP_LINENO)
2805 END_CASE(JSOP_LINENO)
2807 BEGIN_CASE(JSOP_BLOCKCHAIN)
2808 END_CASE(JSOP_BLOCKCHAIN)
2810 BEGIN_CASE(JSOP_PUSH)
2814 BEGIN_CASE(JSOP_POP)
2818 BEGIN_CASE(JSOP_POPN)
2820 regs.sp -= GET_UINT16(regs.pc);
2822 JS_ASSERT(regs.fp->base() <= regs.sp);
2823 JSObject *obj = GetBlockChain(cx, regs.fp);
2825 OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
2826 <= (size_t) (regs.sp - regs.fp->base()));
2827 for (obj = ®s.fp->scopeChain(); obj; obj = obj->getParent()) {
2828 Class *clasp = obj->getClass();
2829 if (clasp != &js_BlockClass && clasp != &js_WithClass)
2831 if (obj->getPrivate() != js_FloatingFrameIfGenerator(cx, regs.fp))
2833 JS_ASSERT(regs.fp->base() + OBJ_BLOCK_DEPTH(cx, obj)
2834 + ((clasp == &js_BlockClass)
2835 ? OBJ_BLOCK_COUNT(cx, obj)
2843 BEGIN_CASE(JSOP_SETRVAL)
2844 BEGIN_CASE(JSOP_POPV)
2848 BEGIN_CASE(JSOP_ENTERWITH)
2849 if (!js_EnterWith(cx, -1, JSOP_ENTERWITH, JSOP_ENTERWITH_LENGTH))
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
2858 * We set sp[-1] to the current "with" object to help asserting the
2859 * enter/leave balance in [leavewith].
2861 regs.sp[-1].setObject(regs.fp->scopeChain());
2862 END_CASE(JSOP_ENTERWITH)
2864 BEGIN_CASE(JSOP_LEAVEWITH)
2865 JS_ASSERT(®s.sp[-1].toObject() == ®s.fp->scopeChain());
2868 END_CASE(JSOP_LEAVEWITH)
2870 BEGIN_CASE(JSOP_RETURN)
2874 BEGIN_CASE(JSOP_RETRVAL) /* fp return value already set */
2875 BEGIN_CASE(JSOP_STOP)
2878 * When the inlined frame exits with an exception or an error, ok will be
2879 * false after the inline_return label.
2884 if (regs.fp->hasImacropc()) {
2886 * If we are at the end of an imacro, return to its caller in the
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);
2901 interpReturnOK = true;
2902 if (entryFrame != regs.fp)
2905 JS_ASSERT(!js_IsActiveWithOrBlock(cx, ®s.fp->scopeChain(), 0));
2906 interpReturnOK = ScriptEpilogue(cx, regs.fp, interpReturnOK);
2907 CHECK_INTERRUPT_HANDLER();
2909 /* The JIT inlines ScriptEpilogue. */
2911 Value *newsp = regs.fp->actualArgs() - 1;
2912 newsp[-1] = regs.fp->returnValue();
2913 cx->stack().popInlineFrame(cx, regs.fp->prev(), newsp);
2915 /* Sync interpreter registers. */
2916 script = regs.fp->script();
2917 argv = regs.fp->maybeFormalArgs();
2918 atoms = FrameAtomBase(cx, regs.fp);
2920 /* Resume execution in the calling frame. */
2921 RESET_USE_METHODJIT();
2922 JS_ASSERT(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;
2933 JS_ASSERT(regs.sp == regs.fp->base());
2935 interpReturnOK = true;
2939 BEGIN_CASE(JSOP_DEFAULT)
2942 BEGIN_CASE(JSOP_GOTO)
2944 len = GET_JUMP_OFFSET(regs.pc);
2949 BEGIN_CASE(JSOP_IFEQ)
2953 POP_BOOLEAN(cx, _, cond);
2954 if (cond == false) {
2955 len = GET_JUMP_OFFSET(regs.pc);
2961 BEGIN_CASE(JSOP_IFNE)
2965 POP_BOOLEAN(cx, _, cond);
2966 if (cond != false) {
2967 len = GET_JUMP_OFFSET(regs.pc);
2977 POP_BOOLEAN(cx, vp, cond);
2979 len = GET_JUMP_OFFSET(regs.pc);
2986 BEGIN_CASE(JSOP_AND)
2990 POP_BOOLEAN(cx, vp, cond);
2991 if (cond == false) {
2992 len = GET_JUMP_OFFSET(regs.pc);
2999 BEGIN_CASE(JSOP_DEFAULTX)
3002 BEGIN_CASE(JSOP_GOTOX)
3004 len = GET_JUMPX_OFFSET(regs.pc);
3007 END_CASE(JSOP_GOTOX);
3009 BEGIN_CASE(JSOP_IFEQX)
3013 POP_BOOLEAN(cx, _, cond);
3014 if (cond == false) {
3015 len = GET_JUMPX_OFFSET(regs.pc);
3019 END_CASE(JSOP_IFEQX)
3021 BEGIN_CASE(JSOP_IFNEX)
3025 POP_BOOLEAN(cx, _, cond);
3026 if (cond != false) {
3027 len = GET_JUMPX_OFFSET(regs.pc);
3031 END_CASE(JSOP_IFNEX)
3033 BEGIN_CASE(JSOP_ORX)
3037 POP_BOOLEAN(cx, vp, cond);
3039 len = GET_JUMPX_OFFSET(regs.pc);
3046 BEGIN_CASE(JSOP_ANDX)
3050 POP_BOOLEAN(cx, vp, cond);
3051 if (cond == JS_FALSE) {
3052 len = GET_JUMPX_OFFSET(regs.pc);
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
3065 #define FETCH_ELEMENT_ID(obj, n, id) \
3067 const Value &idval_ = regs.sp[n]; \
3069 if (ValueFitsInInt32(idval_, &i_) && INT_FITS_IN_JSID(i_)) { \
3070 id = INT_TO_JSID(i_); \
3072 if (!js_InternNonIntElementId(cx, obj, idval_, &id, ®s.sp[n])) \
3077 #define TRY_BRANCH_AFTER_COND(cond,spdec) \
3079 JS_ASSERT(js_CodeSpec[op].length == 1); \
3080 uintN diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ; \
3083 if (cond == (diff_ != 0)) { \
3085 len = GET_JUMP_OFFSET(regs.pc); \
3088 len = 1 + JSOP_IFEQ_LENGTH; \
3095 const Value &rref = regs.sp[-1];
3096 if (!rref.isObject()) {
3097 js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rref, NULL);
3100 JSObject *obj = &rref.toObject();
3102 FETCH_ELEMENT_ID(obj, -2, id);
3105 if (!obj->lookupProperty(cx, id, &obj2, &prop))
3107 bool cond = prop != NULL;
3108 TRY_BRANCH_AFTER_COND(cond, 2);
3110 regs.sp[-1].setBoolean(cond);
3114 BEGIN_CASE(JSOP_ITER)
3116 JS_ASSERT(regs.sp > regs.fp->base());
3117 uintN flags = regs.pc[1];
3118 if (!js_ValueToIterator(cx, flags, ®s.sp[-1]))
3120 CHECK_INTERRUPT_HANDLER();
3121 JS_ASSERT(!regs.sp[-1].isPrimitive());
3125 BEGIN_CASE(JSOP_MOREITER)
3127 JS_ASSERT(regs.sp - 1 >= regs.fp->base());
3128 JS_ASSERT(regs.sp[-1].isObject());
3131 if (!IteratorMore(cx, ®s.sp[-2].toObject(), &cond, ®s.sp[-1]))
3133 CHECK_INTERRUPT_HANDLER();
3134 regs.sp[-1].setBoolean(cond);
3136 END_CASE(JSOP_MOREITER)
3138 BEGIN_CASE(JSOP_ENDITER)
3140 JS_ASSERT(regs.sp - 1 >= regs.fp->base());
3141 bool ok = !!js_CloseIterator(cx, ®s.sp[-1].toObject());
3146 END_CASE(JSOP_ENDITER)
3148 BEGIN_CASE(JSOP_FORARG)
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, ®s.sp[-1].toObject(), &argv[slot]))
3157 END_CASE(JSOP_FORARG)
3159 BEGIN_CASE(JSOP_FORLOCAL)
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, ®s.sp[-1].toObject(), ®s.fp->slots()[slot]))
3168 END_CASE(JSOP_FORLOCAL)
3170 BEGIN_CASE(JSOP_FORNAME)
3171 BEGIN_CASE(JSOP_FORGNAME)
3173 JS_ASSERT(regs.sp - 1 >= regs.fp->base());
3176 jsid id = ATOM_TO_JSID(atom);
3177 JSObject *obj, *obj2;
3179 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
3183 AutoValueRooter tvr(cx);
3184 JS_ASSERT(regs.sp[-1].isObject());
3185 if (!IteratorNext(cx, ®s.sp[-1].toObject(), tvr.addr()))
3187 if (!obj->setProperty(cx, id, tvr.addr(), script->strictModeCode))
3191 END_CASE(JSOP_FORNAME)
3193 BEGIN_CASE(JSOP_FORPROP)
3195 JS_ASSERT(regs.sp - 2 >= regs.fp->base());
3198 jsid id = ATOM_TO_JSID(atom);
3200 FETCH_OBJECT(cx, -1, obj);
3202 AutoValueRooter tvr(cx);
3203 JS_ASSERT(regs.sp[-2].isObject());
3204 if (!IteratorNext(cx, ®s.sp[-2].toObject(), tvr.addr()))
3206 if (!obj->setProperty(cx, id, tvr.addr(), script->strictModeCode))
3211 END_CASE(JSOP_FORPROP)
3213 BEGIN_CASE(JSOP_FORELEM)
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.
3220 JS_ASSERT(regs.sp - 1 >= regs.fp->base());
3221 JS_ASSERT(regs.sp[-1].isObject());
3223 if (!IteratorNext(cx, ®s.sp[-2].toObject(), ®s.sp[-1]))
3225 END_CASE(JSOP_FORELEM)
3227 BEGIN_CASE(JSOP_DUP)
3229 JS_ASSERT(regs.sp > regs.fp->base());
3230 const Value &rref = regs.sp[-1];
3235 BEGIN_CASE(JSOP_DUP2)
3237 JS_ASSERT(regs.sp - 2 >= regs.fp->base());
3238 const Value &lref = regs.sp[-2];
3239 const Value &rref = regs.sp[-1];
3245 BEGIN_CASE(JSOP_SWAP)
3247 JS_ASSERT(regs.sp - 2 >= regs.fp->base());
3248 Value &lref = regs.sp[-2];
3249 Value &rref = regs.sp[-1];
3254 BEGIN_CASE(JSOP_PICK)
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);
3264 #define NATIVE_GET(cx,obj,pobj,shape,getHow,vp) \
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); \
3273 (vp)->setUndefined(); \
3275 if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp)) \
3280 #define NATIVE_SET(cx,obj,shape,entry,strict,vp) \
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); \
3288 if (!js_NativeSet(cx, obj, shape, false, strict, vp)) \
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.
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.
3304 #define SKIP_POP_AFTER_SET(oplen,spdec) \
3305 if (regs.pc[oplen] == JSOP_POP) { \
3307 regs.pc += oplen + JSOP_POP_LENGTH; \
3308 op = (JSOp) *regs.pc; \
3312 #define END_SET_CASE(OP) \
3313 SKIP_POP_AFTER_SET(OP##_LENGTH, 1); \
3316 #define END_SET_CASE_STORE_RVAL(OP,spdec) \
3317 SKIP_POP_AFTER_SET(OP##_LENGTH, spdec); \
3319 Value *newsp = regs.sp - ((spdec) - 1); \
3320 newsp[-1] = regs.sp[-1]; \
3325 BEGIN_CASE(JSOP_SETCONST)
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)) {
3337 END_SET_CASE(JSOP_SETCONST);
3339 #if JS_HAS_DESTRUCTURING
3340 BEGIN_CASE(JSOP_ENUMCONSTELEM)
3342 const Value &ref = regs.sp[-3];
3344 FETCH_OBJECT(cx, -2, obj);
3346 FETCH_ELEMENT_ID(obj, -1, id);
3347 if (!obj->defineProperty(cx, id, ref,
3348 PropertyStub, StrictPropertyStub,
3349 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) {
3354 END_CASE(JSOP_ENUMCONSTELEM)
3357 BEGIN_CASE(JSOP_BINDGNAME)
3358 PUSH_OBJECT(*regs.fp->scopeChain().getGlobal());
3359 END_CASE(JSOP_BINDGNAME)
3361 BEGIN_CASE(JSOP_BINDNAME)
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.
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.
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
3381 obj = ®s.fp->scopeChain();
3382 if (!obj->getParent())
3385 PropertyCacheEntry *entry;
3388 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
3390 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
3394 jsid id = ATOM_TO_JSID(atom);
3395 obj = js_FindIdentifierBase(cx, ®s.fp->scopeChain(), id);
3401 END_CASE(JSOP_BINDNAME)
3403 BEGIN_CASE(JSOP_IMACOP)
3404 JS_ASSERT(JS_UPTRDIFF(regs.fp->imacropc(), script->code) < script->length);
3405 op = JSOp(*regs.fp->imacropc());
3408 #define BITWISE_OP(OP) \
3411 if (!ValueToECMAInt32(cx, regs.sp[-2], &i)) \
3413 if (!ValueToECMAInt32(cx, regs.sp[-1], &j)) \
3417 regs.sp[-1].setInt32(i); \
3420 BEGIN_CASE(JSOP_BITOR)
3422 END_CASE(JSOP_BITOR)
3424 BEGIN_CASE(JSOP_BITXOR)
3426 END_CASE(JSOP_BITXOR)
3428 BEGIN_CASE(JSOP_BITAND)
3430 END_CASE(JSOP_BITAND)
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!
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)) \
3445 cond = cond OP JS_TRUE; \
3448 #define XML_EQUALITY_OP(OP) /* nothing */
3451 #define EQUALITY_OP(OP, IFNAN) \
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(); \
3461 if (!EqualStrings(cx, l, r, &equal)) \
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)) \
3473 cond = cond OP JS_TRUE; \
3478 cond = lval.payloadAsRawUint32() OP rval.payloadAsRawUint32();\
3481 if (lval.isNullOrUndefined()) { \
3482 cond = rval.isNullOrUndefined() OP true; \
3483 } else if (rval.isNullOrUndefined()) { \
3484 cond = true OP false; \
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(); \
3493 if (!EqualStrings(cx, l, r, &equal)) \
3495 cond = equal OP JS_TRUE; \
3498 if (!ValueToNumber(cx, lval, &l) || \
3499 !ValueToNumber(cx, rval, &r)) { \
3502 cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN); \
3506 TRY_BRANCH_AFTER_COND(cond, 2); \
3508 regs.sp[-1].setBoolean(cond); \
3512 EQUALITY_OP(==, false);
3516 EQUALITY_OP(!=, true);
3520 #undef XML_EQUALITY_OP
3521 #undef EXTENDED_EQUALITY_OP
3523 #define STRICT_EQUALITY_OP(OP, COND) \
3525 const Value &rref = regs.sp[-1]; \
3526 const Value &lref = regs.sp[-2]; \
3528 if (!StrictlyEqual(cx, lref, rref, &equal)) \
3530 COND = equal OP JS_TRUE; \
3534 BEGIN_CASE(JSOP_STRICTEQ)
3537 STRICT_EQUALITY_OP(==, cond);
3538 regs.sp[-1].setBoolean(cond);
3540 END_CASE(JSOP_STRICTEQ)
3542 BEGIN_CASE(JSOP_STRICTNE)
3545 STRICT_EQUALITY_OP(!=, cond);
3546 regs.sp[-1].setBoolean(cond);
3548 END_CASE(JSOP_STRICTNE)
3550 BEGIN_CASE(JSOP_CASE)
3553 STRICT_EQUALITY_OP(==, cond);
3556 len = GET_JUMP_OFFSET(regs.pc);
3562 BEGIN_CASE(JSOP_CASEX)
3565 STRICT_EQUALITY_OP(==, cond);
3568 len = GET_JUMPX_OFFSET(regs.pc);
3572 END_CASE(JSOP_CASEX)
3574 #undef STRICT_EQUALITY_OP
3576 #define RELATIONAL_OP(OP) \
3578 Value rval = regs.sp[-1]; \
3579 Value lval = regs.sp[-2]; \
3581 /* Optimize for two int-tagged operands (typical loop control). */ \
3582 if (lval.isInt32() && rval.isInt32()) { \
3583 cond = lval.toInt32() OP rval.toInt32(); \
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(); \
3592 if (!CompareStrings(cx, l, r, &result)) \
3594 cond = result OP 0; \
3597 if (!ValueToNumber(cx, lval, &l) || \
3598 !ValueToNumber(cx, rval, &r)) { \
3601 cond = JSDOUBLE_COMPARE(l, OP, r, false); \
3604 TRY_BRANCH_AFTER_COND(cond, 2); \
3606 regs.sp[-1].setBoolean(cond); \
3625 #undef RELATIONAL_OP
3627 #define SIGNED_SHIFT_OP(OP) \
3630 if (!ValueToECMAInt32(cx, regs.sp[-2], &i)) \
3632 if (!ValueToECMAInt32(cx, regs.sp[-1], &j)) \
3634 i = i OP (j & 31); \
3636 regs.sp[-1].setInt32(i); \
3639 BEGIN_CASE(JSOP_LSH)
3640 SIGNED_SHIFT_OP(<<);
3643 BEGIN_CASE(JSOP_RSH)
3644 SIGNED_SHIFT_OP(>>);
3647 #undef SIGNED_SHIFT_OP
3649 BEGIN_CASE(JSOP_URSH)
3652 if (!ValueToECMAUint32(cx, regs.sp[-2], &u))
3655 if (!ValueToECMAInt32(cx, regs.sp[-1], &j))
3661 regs.sp[-1].setNumber(uint32(u));
3665 BEGIN_CASE(JSOP_ADD)
3667 Value rval = regs.sp[-1];
3668 Value lval = regs.sp[-2];
3670 if (lval.isInt32() && rval.isInt32()) {
3671 int32_t l = lval.toInt32(), r = rval.toInt32();
3672 int32_t sum = l + r;
3674 if (JS_UNLIKELY(bool((l ^ sum) & (r ^ sum) & 0x80000000)))
3675 regs.sp[-1].setDouble(double(l) + double(r));
3677 regs.sp[-1].setInt32(sum);
3679 #if JS_HAS_XML_SUPPORT
3680 if (IsXML(lval) && IsXML(rval)) {
3681 if (!js_ConcatenateXML(cx, &lval.toObject(), &rval.toObject(), &rval))
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;
3696 lstr = lval.toString();
3698 lstr = js_ValueToString(cx, lval);
3701 regs.sp[-2].setString(lstr);
3704 rstr = rval.toString();
3706 rstr = js_ValueToString(cx, rval);
3709 regs.sp[-1].setString(rstr);
3711 JSString *str = js_ConcatStrings(cx, lstr, rstr);
3715 regs.sp[-1].setString(str);
3718 if (!ValueToNumber(cx, lval, &l) || !ValueToNumber(cx, rval, &r))
3722 regs.sp[-1].setNumber(l);
3728 #define BINARY_OP(OP) \
3731 if (!ValueToNumber(cx, regs.sp[-2], &d1) || \
3732 !ValueToNumber(cx, regs.sp[-1], &d2)) { \
3735 double d = d1 OP d2; \
3737 regs.sp[-1].setNumber(d); \
3740 BEGIN_CASE(JSOP_SUB)
3744 BEGIN_CASE(JSOP_MUL)
3750 BEGIN_CASE(JSOP_DIV)
3753 if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
3754 !ValueToNumber(cx, regs.sp[-1], &d2)) {
3761 /* XXX MSVC miscompiles such that (NaN == 0) */
3762 if (JSDOUBLE_IS_NaN(d2))
3766 if (d1 == 0 || JSDOUBLE_IS_NaN(d1))
3768 else if (JSDOUBLE_IS_NEG(d1) != JSDOUBLE_IS_NEG(d2))
3769 vp = &rt->negativeInfinityValue;
3771 vp = &rt->positiveInfinityValue;
3775 regs.sp[-1].setNumber(d1);
3780 BEGIN_CASE(JSOP_MOD)
3782 Value &lref = regs.sp[-2];
3783 Value &rref = regs.sp[-1];
3785 if (lref.isInt32() && rref.isInt32() &&
3786 (l = lref.toInt32()) >= 0 && (r = rref.toInt32()) > 0) {
3787 int32_t mod = l % r;
3789 regs.sp[-1].setInt32(mod);
3792 if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
3793 !ValueToNumber(cx, regs.sp[-1], &d2)) {
3798 regs.sp[-1].setDouble(js_NaN);
3800 d1 = js_fmod(d1, d2);
3801 regs.sp[-1].setDouble(d1);
3807 BEGIN_CASE(JSOP_NOT)
3811 POP_BOOLEAN(cx, _, cond);
3812 PUSH_BOOLEAN(!cond);
3816 BEGIN_CASE(JSOP_BITNOT)
3819 if (!ValueToECMAInt32(cx, regs.sp[-1], &i))
3822 regs.sp[-1].setInt32(i);
3824 END_CASE(JSOP_BITNOT)
3826 BEGIN_CASE(JSOP_NEG)
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.
3833 const Value &ref = regs.sp[-1];
3835 if (ref.isInt32() && (i = ref.toInt32()) != 0 && i != INT32_MIN) {
3837 regs.sp[-1].setInt32(i);
3840 if (!ValueToNumber(cx, regs.sp[-1], &d))
3843 regs.sp[-1].setDouble(d);
3848 BEGIN_CASE(JSOP_POS)
3849 if (!ValueToNumber(cx, ®s.sp[-1]))
3853 BEGIN_CASE(JSOP_DELNAME)
3857 jsid id = ATOM_TO_JSID(atom);
3858 JSObject *obj, *obj2;
3860 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
3863 /* Strict mode code should never contain JSOP_DELNAME opcodes. */
3864 JS_ASSERT(!script->strictModeCode);
3866 /* ECMA says to return true if name is undefined or inherited. */
3869 if (!obj->deleteProperty(cx, id, ®s.sp[-1], false))
3873 END_CASE(JSOP_DELNAME)
3875 BEGIN_CASE(JSOP_DELPROP)
3879 jsid id = ATOM_TO_JSID(atom);
3882 FETCH_OBJECT(cx, -1, obj);
3885 if (!obj->deleteProperty(cx, id, &rval, script->strictModeCode))
3890 END_CASE(JSOP_DELPROP)
3892 BEGIN_CASE(JSOP_DELELEM)
3894 /* Fetch the left part and resolve it to a non-null object. */
3896 FETCH_OBJECT(cx, -2, obj);
3898 /* Fetch index and convert it to id suitable for use with obj. */
3900 FETCH_ELEMENT_ID(obj, -1, id);
3902 /* Get or set the element. */
3903 if (!obj->deleteProperty(cx, id, ®s.sp[-2], script->strictModeCode))
3908 END_CASE(JSOP_DELELEM)
3910 BEGIN_CASE(JSOP_TYPEOFEXPR)
3911 BEGIN_CASE(JSOP_TYPEOF)
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));
3918 END_CASE(JSOP_TYPEOF)
3920 BEGIN_CASE(JSOP_VOID)
3921 regs.sp[-1].setUndefined();
3930 BEGIN_CASE(JSOP_INCELEM)
3931 BEGIN_CASE(JSOP_DECELEM)
3932 BEGIN_CASE(JSOP_ELEMINC)
3933 BEGIN_CASE(JSOP_ELEMDEC)
3936 * Delay fetching of id until we have the object to ensure the proper
3937 * evaluation order. See bug 372331.
3941 goto fetch_incop_obj;
3943 BEGIN_CASE(JSOP_INCPROP)
3944 BEGIN_CASE(JSOP_DECPROP)
3945 BEGIN_CASE(JSOP_PROPINC)
3946 BEGIN_CASE(JSOP_PROPDEC)
3948 id = ATOM_TO_JSID(atom);
3952 FETCH_OBJECT(cx, i, obj);
3953 if (JSID_IS_VOID(id))
3954 FETCH_ELEMENT_ID(obj, -1, id);
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)
3966 obj = ®s.fp->scopeChain();
3967 if (js_CodeSpec[op].format & JOF_GNAME)
3968 obj = obj->getGlobal();
3971 PropertyCacheEntry *entry;
3972 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, 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);
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))
3983 rref.getInt32Ref() = inc;
3985 len = JSOP_INCNAME_LENGTH;
3992 id = ATOM_TO_JSID(atom);
3994 if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
3997 atomNotDefined = atom;
3998 goto atom_not_defined;
4005 * We need a root to store the value to leave on the stack until
4006 * we have done with obj->setProperty.
4009 if (!obj->getProperty(cx, id, ®s.sp[-1]))
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];
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;
4022 ref.getInt32Ref() = tmp += incr;
4023 regs.fp->setAssigning();
4024 JSBool ok = obj->setProperty(cx, id, &ref, script->strictModeCode);
4025 regs.fp->clearAssigning();
4030 * We must set regs.sp[-1] to tmp for both post and pre increments
4031 * as the setter overwrites regs.sp[-1].
4035 /* We need an extra root for the result. */
4037 if (!js_DoIncDec(cx, cs, ®s.sp[-2], ®s.sp[-1]))
4039 regs.fp->setAssigning();
4040 JSBool ok = obj->setProperty(cx, id, ®s.sp[-1], script->strictModeCode);
4041 regs.fp->clearAssigning();
4047 if (cs->nuses == 0) {
4048 /* regs.sp[-1] already contains the result of name increment. */
4050 regs.sp[-1 - cs->nuses] = regs.sp[-1];
4051 regs.sp -= cs->nuses;
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;
4074 slot = GET_ARGNO(regs.pc);
4075 JS_ASSERT(slot < regs.fp->numFormalArgs());
4076 METER_SLOT_OP(op, slot);
4078 goto do_int_fast_incop;
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;
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
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;
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);
4109 if (!js_DoIncDec(cx, &js_CodeSpec[op], ®s.sp[-1], vp))
4112 len = JSOP_INCARG_LENGTH;
4113 JS_ASSERT(len == js_CodeSpec[op].length);
4117 BEGIN_CASE(JSOP_THIS)
4118 if (!regs.fp->computeThis(cx))
4120 PUSH_COPY(regs.fp->thisValue());
4123 BEGIN_CASE(JSOP_UNBRANDTHIS)
4125 if (!regs.fp->computeThis(cx))
4127 Value &thisv = regs.fp->thisValue();
4128 if (thisv.isObject()) {
4129 JSObject *obj = &thisv.toObject();
4130 if (obj->isNative())
4134 END_CASE(JSOP_UNBRANDTHIS)
4141 BEGIN_CASE(JSOP_GETTHISPROP)
4142 if (!regs.fp->computeThis(cx))
4145 PUSH_COPY(regs.fp->thisValue());
4146 goto do_getprop_body;
4148 BEGIN_CASE(JSOP_GETARGPROP)
4151 uint32 slot = GET_ARGNO(regs.pc);
4152 JS_ASSERT(slot < regs.fp->numFormalArgs());
4153 PUSH_COPY(argv[slot]);
4154 goto do_getprop_body;
4157 BEGIN_CASE(JSOP_GETLOCALPROP)
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;
4166 BEGIN_CASE(JSOP_GETPROP)
4167 BEGIN_CASE(JSOP_GETXPROP)
4173 do_getprop_with_lval:
4174 VALUE_TO_OBJECT(cx, vp, obj);
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.
4184 JSObject *aobj = js_GetProtoIfDenseArray(obj);
4186 PropertyCacheEntry *entry;
4189 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, 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);
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,
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,
4215 : !obj->getProperty(cx, id, &rval)) {
4221 assertSameCompartment(cx, regs.sp[-1]);
4222 JS_ASSERT(JSOP_GETPROP_LENGTH + i == js_CodeSpec[op].length);
4223 len = JSOP_GETPROP_LENGTH + i;
4227 BEGIN_CASE(JSOP_LENGTH)
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));
4242 goto do_getprop_with_lval;
4246 goto do_getprop_with_lval;
4248 END_CASE(JSOP_LENGTH)
4252 BEGIN_CASE(JSOP_CALLPROP)
4254 Value lval = regs.sp[-1];
4257 if (lval.isObject()) {
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;
4268 JS_ASSERT(lval.isNull() || lval.isUndefined());
4269 js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
4273 if (!js_GetClassPrototype(cx, NULL, protoKey, &pobj))
4275 objv.setObject(*pobj);
4278 JSObject *aobj = js_GetProtoIfDenseArray(&objv.toObject());
4281 PropertyCacheEntry *entry;
4284 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, 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);
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);
4298 assertSameCompartment(cx, regs.sp[-1]);
4302 * Cache miss: use the immediate atom that was loaded for us under
4303 * PropertyCache::test.
4306 id = ATOM_TO_JSID(atom);
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,
4319 assertSameCompartment(cx, regs.sp[-1], regs.sp[-2]);
4321 JS_ASSERT(!objv.toObject().getOps()->getProperty);
4322 if (!js_GetPropertyHelper(cx, &objv.toObject(), id,
4323 JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER,
4329 assertSameCompartment(cx, regs.sp[-1], regs.sp[-2]);
4332 #if JS_HAS_NO_SUCH_METHOD
4333 if (JS_UNLIKELY(rval.isUndefined()) && regs.sp[-1].isObject()) {
4335 regs.sp[-2].setString(ATOM_TO_STRING(atom));
4336 if (!js_OnUnknownMethod(cx, regs.sp - 2))
4341 END_CASE(JSOP_CALLPROP)
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)
4348 BEGIN_CASE(JSOP_SETGNAME)
4349 BEGIN_CASE(JSOP_SETNAME)
4350 BEGIN_CASE(JSOP_SETPROP)
4351 BEGIN_CASE(JSOP_SETMETHOD)
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());
4358 VALUE_TO_OBJECT(cx, &lref, obj);
4360 JS_ASSERT_IF(op == JSOP_SETGNAME, obj == regs.fp->scopeChain().getGlobal());
4363 PropertyCache *cache = &JS_PROPERTY_CACHE(cx);
4366 * Probe the property cache, specializing for two important
4367 * set-property cases. First:
4369 * function f(a, b, c) {
4370 * var o = {p:a, q:b, r:c};
4374 * or similar real-world cases, which evolve a newborn native
4375 * object predicatably through some bounded number of property
4376 * additions. And second:
4380 * in a frequently executed method or loop body, where p will
4381 * (possibly after the first iteration) always exist in native
4384 PropertyCacheEntry *entry;
4387 if (cache->testForSet(cx, regs.pc, obj, &entry, &obj2, &atom)) {
4389 * Property cache hit, only partially confirmed by testForSet. We
4390 * know that the entry applies to regs.pc and that obj's shape
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.
4397 const Shape *shape = entry->vword.toShape();
4398 JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable());
4399 JS_ASSERT_IF(shape->hasSlot(), entry->vcapTag() == 0);
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.
4406 if (!entry->adding()) {
4407 if (entry->vcapTag() == 0 ||
4408 ((obj2 = obj->getProto()) && obj2->shape() == entry->vshape()))
4411 if (entry->directHit()) {
4412 JS_ASSERT(obj->nativeContains(*shape));
4414 JS_ASSERT(obj2->nativeContains(*shape));
4415 JS_ASSERT(entry->vcapTag() == 1);
4416 JS_ASSERT(entry->kshape != entry->vshape());
4417 JS_ASSERT(!shape->hasSlot());
4421 PCMETER(cache->pchits++);
4422 PCMETER(cache->setpchits++);
4423 NATIVE_SET(cx, obj, shape, entry, script->strictModeCode, &rval);
4427 JS_ASSERT(obj->isExtensible());
4429 if (obj->nativeEmpty()) {
4430 if (!obj->ensureClassReservedSlotsForEmptyObject(cx))
4435 if (shape->previous() == obj->lastProperty() &&
4436 entry->vshape() == rt->protoHazardShape &&
4437 shape->hasDefaultSetter()) {
4439 JS_ASSERT(slot == obj->slotSpan());
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.
4447 PCMETER(cache->pchits++);
4448 PCMETER(cache->addpchits++);
4450 if (slot < obj->numSlots()) {
4451 JS_ASSERT(obj->getSlot(slot).isUndefined());
4453 if (!obj->allocSlot(cx, &slot))
4455 JS_ASSERT(slot == shape->slot);
4458 /* Simply extend obj's property tree path with shape! */
4459 obj->extend(cx, shape);
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.
4466 TRACE_1(AddProperty, obj);
4467 obj->nativeSetSlot(slot, rval);
4470 * Purge the property cache of the id we may have just
4471 * shadowed in obj's scope and proto chains.
4473 js_PurgeScopeChain(cx, obj, shape->id);
4477 PCMETER(cache->setpcmisses++);
4484 jsid id = ATOM_TO_JSID(atom);
4485 if (entry && JS_LIKELY(!obj->getOps()->setProperty)) {
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;
4492 defineHow = JSDNP_CACHE_RESULT;
4493 if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rval, script->strictModeCode))
4496 if (!obj->setProperty(cx, id, &rval, script->strictModeCode))
4498 ABORT_RECORDING(cx, "Non-native set");
4502 END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2);
4504 BEGIN_CASE(JSOP_GETELEM)
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));
4516 regs.sp[-1].setString(str);
4517 len = JSOP_GETELEM_LENGTH;
4523 VALUE_TO_OBJECT(cx, &lref, obj);
4525 const Value *copyFrom;
4528 if (rref.isInt32()) {
4529 int32_t i = rref.toInt32();
4530 if (obj->isDenseArray()) {
4531 jsuint idx = jsuint(i);
4533 if (idx < obj->getDenseArrayCapacity()) {
4534 copyFrom = obj->addressOfDenseArrayElement(idx);
4535 if (!copyFrom->isMagic())
4538 } else if (obj->isArguments()) {
4539 uint32 arg = uint32(i);
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);
4550 if (JS_LIKELY(INT_FITS_IN_JSID(i)))
4551 id = INT_TO_JSID(i);
4553 goto intern_big_int;
4556 if (ValueFitsInInt32(rref, &i) && INT_FITS_IN_JSID(i)) {
4557 id = INT_TO_JSID(i);
4560 if (!js_InternNonIntElementId(cx, obj, rref, &id))
4565 if (!obj->getProperty(cx, id, &rval))
4571 regs.sp[-1] = *copyFrom;
4572 assertSameCompartment(cx, regs.sp[-1]);
4574 END_CASE(JSOP_GETELEM)
4576 BEGIN_CASE(JSOP_CALLELEM)
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);
4584 /* Fetch index and convert it to id suitable for use with obj. */
4586 FETCH_ELEMENT_ID(thisObj, -1, id);
4588 /* Get the method. */
4589 if (!js_GetMethod(cx, thisObj, id, JSGET_NO_METHOD_BARRIER, ®s.sp[-2]))
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))
4602 regs.sp[-1] = thisv;
4605 END_CASE(JSOP_CALLELEM)
4607 BEGIN_CASE(JSOP_SETELEM)
4610 FETCH_OBJECT(cx, -3, obj);
4612 FETCH_ELEMENT_ID(obj, -2, id);
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))
4622 if ((jsuint)i >= obj->getArrayLength())
4623 obj->setArrayLength(i + 1);
4625 obj->setDenseArrayElement(i, regs.sp[-1]);
4631 if (!obj->setProperty(cx, id, &rval, script->strictModeCode))
4635 END_SET_CASE_STORE_RVAL(JSOP_SETELEM, 3)
4637 BEGIN_CASE(JSOP_ENUMELEM)
4639 /* Funky: the value to set is under the [obj, id] pair. */
4641 FETCH_OBJECT(cx, -2, obj);
4643 FETCH_ELEMENT_ID(obj, -1, id);
4644 Value rval = regs.sp[-3];
4645 if (!obj->setProperty(cx, id, &rval, script->strictModeCode))
4649 END_CASE(JSOP_ENUMELEM)
4651 { // begin block around calling opcodes
4658 BEGIN_CASE(JSOP_NEW)
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());
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.
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);
4676 vp[0].setObject(*obj2);
4681 flags = JSFRAME_CONSTRUCTING;
4686 if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(vp, argc)))
4689 CHECK_INTERRUPT_HANDLER();
4690 TRACE_0(NativeCallComplete);
4696 BEGIN_CASE(JSOP_EVAL)
4698 argc = GET_ARGC(regs.pc);
4699 vp = regs.sp - (argc + 2);
4701 if (!IsFunctionObject(*vp, &callee))
4702 goto call_using_invoke;
4704 newfun = callee->getFunctionPrivate();
4705 if (!IsBuiltinEvalFunction(newfun))
4706 goto call_using_invoke;
4708 if (!DirectEval(cx, newfun, argc, vp))
4713 BEGIN_CASE(JSOP_CALL)
4714 BEGIN_CASE(JSOP_FUNAPPLY)
4715 BEGIN_CASE(JSOP_FUNCALL)
4717 argc = GET_ARGC(regs.pc);
4718 vp = regs.sp - (argc + 2);
4720 if (IsFunctionObject(*vp, &callee)) {
4721 newfun = callee->getFunctionPrivate();
4723 /* Clear frame flags since this is not a constructor call. */
4725 if (newfun->isInterpreted())
4728 JSScript *newscript = newfun->script();
4729 if (JS_UNLIKELY(newscript->isEmpty())) {
4735 /* Restrict recursion of lightweight functions. */
4736 if (JS_UNLIKELY(inlineCallCount >= JS_MAX_INLINE_CALL_COUNT)) {
4737 js_ReportOverRecursed(cx);
4741 /* Get pointer to new frame/slots, prepare arguments. */
4742 StackSpace &stack = cx->stack();
4743 JSStackFrame *newfp = stack.getInlineFrame(cx, regs.sp, argc, newfun,
4745 if (JS_UNLIKELY(!newfp))
4748 /* Initialize frame, locals. */
4749 newfp->initCallFrame(cx, *callee, newfun, argc, flags);
4750 SetValueRangeToUndefined(newfp->slots(), newscript->nfixed);
4752 /* Officially push the frame. */
4753 stack.pushInlineFrame(cx, newscript, newfp, ®s);
4755 /* Refresh interpreter locals. */
4756 JS_ASSERT(newfp == regs.fp);
4758 argv = regs.fp->formalArgsEnd() - newfun->nargs;
4759 atoms = script->atomMap.vector;
4761 /* Now that the new frame is rooted, maybe create a call object. */
4762 if (newfun->isHeavyweight() && !js_GetCallObject(cx, regs.fp))
4765 RESET_USE_METHODJIT();
4767 JS_RUNTIME_METER(rt, inlineCalls);
4769 TRACE_0(EnterFrame);
4771 CHECK_INTERRUPT_HANDLER();
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)
4781 if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && status == mjit::Compile_Okay) {
4782 interpReturnOK = mjit::JaegerShot(cx);
4783 CHECK_INTERRUPT_HANDLER();
4788 if (!ScriptPrologue(cx, regs.fp))
4791 CHECK_INTERRUPT_HANDLER();
4793 /* Load first op and dispatch it (safe since JSOP_STOP). */
4794 op = (JSOp) *regs.pc;
4798 Probes::enterJSFun(cx, newfun, script);
4799 JSBool ok = CallJSNative(cx, newfun->u.n.native, argc, vp);
4800 Probes::exitJSFun(cx, newfun, script);
4804 TRACE_0(NativeCallComplete);
4810 ok = Invoke(cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0);
4812 CHECK_INTERRUPT_HANDLER();
4815 JS_RUNTIME_METER(rt, nonInlineCalls);
4816 TRACE_0(NativeCallComplete);
4822 } // end block around calling opcodes
4824 BEGIN_CASE(JSOP_SETCALL)
4826 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_LEFTSIDE_OF_ASS);
4829 END_CASE(JSOP_SETCALL)
4831 #define PUSH_IMPLICIT_THIS(cx, obj, funval) \
4834 if (!ComputeImplicitThis(cx, obj, funval, &v)) \
4839 BEGIN_CASE(JSOP_GETGNAME)
4840 BEGIN_CASE(JSOP_CALLGNAME)
4841 BEGIN_CASE(JSOP_NAME)
4842 BEGIN_CASE(JSOP_CALLNAME)
4844 JSObject *obj = ®s.fp->scopeChain();
4849 PropertyCacheEntry *entry;
4852 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, 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));
4861 JS_ASSERT(entry->vword.isShape());
4862 shape = entry->vword.toShape();
4863 NATIVE_GET(cx, obj, obj2, shape, JSGET_METHOD_BARRIER, &rval);
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;
4875 id = ATOM_TO_JSID(atom);
4877 if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &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) {
4884 len = JSOP_NAME_LENGTH;
4887 atomNotDefined = atom;
4888 goto atom_not_defined;
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))
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);
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);
4911 BEGIN_CASE(JSOP_UINT16)
4912 PUSH_INT32((int32_t) GET_UINT16(regs.pc));
4913 END_CASE(JSOP_UINT16)
4915 BEGIN_CASE(JSOP_UINT24)
4916 PUSH_INT32((int32_t) GET_UINT24(regs.pc));
4917 END_CASE(JSOP_UINT24)
4919 BEGIN_CASE(JSOP_INT8)
4920 PUSH_INT32(GET_INT8(regs.pc));
4923 BEGIN_CASE(JSOP_INT32)
4924 PUSH_INT32(GET_INT32(regs.pc));
4925 END_CASE(JSOP_INT32)
4927 BEGIN_CASE(JSOP_INDEXBASE)
4929 * Here atoms can exceed script->atomMap.length as we use atoms as a
4930 * segment register for object literals as well.
4932 atoms += GET_INDEXBASE(regs.pc);
4933 END_CASE(JSOP_INDEXBASE)
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)
4941 BEGIN_CASE(JSOP_RESETBASE0)
4942 BEGIN_CASE(JSOP_RESETBASE)
4943 atoms = script->atomMap.vector;
4944 END_CASE(JSOP_RESETBASE)
4946 BEGIN_CASE(JSOP_DOUBLE)
4948 JS_ASSERT(!regs.fp->hasImacropc());
4950 LOAD_DOUBLE(0, dbl);
4953 END_CASE(JSOP_DOUBLE)
4955 BEGIN_CASE(JSOP_STRING)
4959 PUSH_STRING(ATOM_TO_STRING(atom));
4961 END_CASE(JSOP_STRING)
4963 BEGIN_CASE(JSOP_OBJECT)
4966 LOAD_OBJECT(0, obj);
4969 END_CASE(JSOP_OBJECT)
4971 BEGIN_CASE(JSOP_REGEXP)
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.
4978 * We avoid the GetScopeChain call here and pass fp->scopeChain as
4979 * js_GetClassPrototype uses the latter only to locate the global.
4981 jsatomid index = GET_FULL_INDEX(0);
4983 if (!js_GetClassPrototype(cx, ®s.fp->scopeChain(), JSProto_RegExp, &proto))
4986 JSObject *obj = js_CloneRegExpObject(cx, script->getRegExp(index), proto);
4991 END_CASE(JSOP_REGEXP)
4993 BEGIN_CASE(JSOP_ZERO)
4997 BEGIN_CASE(JSOP_ONE)
5001 BEGIN_CASE(JSOP_NULL)
5005 BEGIN_CASE(JSOP_FALSE)
5006 PUSH_BOOLEAN(false);
5007 END_CASE(JSOP_FALSE)
5009 BEGIN_CASE(JSOP_TRUE)
5014 BEGIN_CASE(JSOP_TABLESWITCH)
5016 jsbytecode *pc2 = regs.pc;
5017 len = GET_JUMP_OFFSET(pc2);
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.)
5024 const Value &rref = *--regs.sp;
5026 if (rref.isInt32()) {
5030 /* Don't use JSDOUBLE_IS_INT32; treat -0 (double) as 0. */
5031 if (!rref.isDouble() || (d = rref.toDouble()) != (i = int32_t(rref.toDouble())))
5035 pc2 += JUMP_OFFSET_LEN;
5036 jsint low = GET_JUMP_OFFSET(pc2);
5037 pc2 += JUMP_OFFSET_LEN;
5038 jsint high = GET_JUMP_OFFSET(pc2);
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);
5052 BEGIN_CASE(JSOP_TABLESWITCHX)
5054 jsbytecode *pc2 = regs.pc;
5055 len = GET_JUMPX_OFFSET(pc2);
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.)
5062 const Value &rref = *--regs.sp;
5064 if (rref.isInt32()) {
5066 } else if (rref.isDouble() && rref.toDouble() == 0) {
5067 /* Treat -0 (double) as 0. */
5073 pc2 += JUMPX_OFFSET_LEN;
5074 jsint low = GET_JUMP_OFFSET(pc2);
5075 pc2 += JUMP_OFFSET_LEN;
5076 jsint high = GET_JUMP_OFFSET(pc2);
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);
5090 BEGIN_CASE(JSOP_LOOKUPSWITCHX)
5093 off = JUMPX_OFFSET_LEN;
5094 goto do_lookup_switch;
5096 BEGIN_CASE(JSOP_LOOKUPSWITCH)
5097 off = JUMP_OFFSET_LEN;
5101 * JSOP_LOOKUPSWITCH and JSOP_LOOKUPSWITCHX are never used if any atom
5102 * index in it would exceed 64K limit.
5104 JS_ASSERT(!regs.fp->hasImacropc());
5105 JS_ASSERT(atoms == script->atomMap.vector);
5106 jsbytecode *pc2 = regs.pc;
5108 Value lval = regs.sp[-1];
5111 if (!lval.isPrimitive())
5112 goto end_lookup_switch;
5116 npairs = (jsint) GET_UINT16(pc2);
5118 JS_ASSERT(npairs); /* empty switch uses JSOP_TABLESWITCH */
5121 #define SEARCH_PAIRS(MATCH_CODE) \
5123 Value rval = script->getConst(GET_INDEX(pc2)); \
5129 if (--npairs == 0) { \
5135 if (lval.isString()) {
5136 JSLinearString *str = lval.toString()->ensureLinear(cx);
5139 JSLinearString *str2;
5141 match = (rval.isString() &&
5142 ((str2 = rval.toString()->assertIsLinear()) == str ||
5143 EqualStrings(str2, str)));
5145 } else if (lval.isNumber()) {
5146 double ldbl = lval.toNumber();
5148 match = rval.isNumber() && ldbl == rval.toNumber();
5152 match = (lval == rval);
5158 len = (op == JSOP_LOOKUPSWITCH)
5159 ? GET_JUMP_OFFSET(pc2)
5160 : GET_JUMPX_OFFSET(pc2);
5165 BEGIN_CASE(JSOP_TRAP)
5168 JSTrapStatus status = JS_HandleTrap(cx, script, regs.pc, Jsvalify(&rval));
5173 regs.fp->setReturnValue(rval);
5174 interpReturnOK = JS_TRUE;
5177 cx->setPendingException(rval);
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);
5190 BEGIN_CASE(JSOP_ARGUMENTS)
5193 if (!js_GetArgsValue(cx, regs.fp, &rval))
5197 END_CASE(JSOP_ARGUMENTS)
5199 BEGIN_CASE(JSOP_ARGSUB)
5201 jsid id = INT_TO_JSID(GET_ARGNO(regs.pc));
5203 if (!js_GetArgsProperty(cx, regs.fp, id, &rval))
5207 END_CASE(JSOP_ARGSUB)
5209 BEGIN_CASE(JSOP_ARGCNT)
5211 jsid id = ATOM_TO_JSID(rt->atomState.lengthAtom);
5213 if (!js_GetArgsProperty(cx, regs.fp, id, &rval))
5217 END_CASE(JSOP_ARGCNT)
5219 BEGIN_CASE(JSOP_GETARG)
5220 BEGIN_CASE(JSOP_CALLARG)
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)
5229 END_CASE(JSOP_GETARG)
5231 BEGIN_CASE(JSOP_SETARG)
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];
5238 END_SET_CASE(JSOP_SETARG)
5240 BEGIN_CASE(JSOP_GETLOCAL)
5242 uint32 slot = GET_SLOTNO(regs.pc);
5243 JS_ASSERT(slot < script->nslots);
5244 PUSH_COPY(regs.fp->slots()[slot]);
5246 END_CASE(JSOP_GETLOCAL)
5248 BEGIN_CASE(JSOP_CALLLOCAL)
5250 uint32 slot = GET_SLOTNO(regs.pc);
5251 JS_ASSERT(slot < script->nslots);
5252 PUSH_COPY(regs.fp->slots()[slot]);
5255 END_CASE(JSOP_CALLLOCAL)
5257 BEGIN_CASE(JSOP_SETLOCAL)
5259 uint32 slot = GET_SLOTNO(regs.pc);
5260 JS_ASSERT(slot < script->nslots);
5261 regs.fp->slots()[slot] = regs.sp[-1];
5263 END_SET_CASE(JSOP_SETLOCAL)
5265 BEGIN_CASE(JSOP_GETUPVAR_DBG)
5266 BEGIN_CASE(JSOP_CALLUPVAR_DBG)
5268 JSFunction *fun = regs.fp->fun();
5269 JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
5270 JS_ASSERT(fun->u.i.wrapper);
5272 /* Scope for tempPool mark and local names allocation in it. */
5273 JSObject *obj, *obj2;
5278 AutoLocalNameArray names(cx, fun);
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);
5286 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
5291 atomNotDefined = atom;
5292 goto atom_not_defined;
5295 /* Minimize footprint with generic code instead of NATIVE_GET. */
5296 Value *vp = regs.sp;
5298 if (!obj->getProperty(cx, id, vp))
5301 if (op == JSOP_CALLUPVAR_DBG)
5304 END_CASE(JSOP_GETUPVAR_DBG)
5306 BEGIN_CASE(JSOP_GETFCSLOT)
5307 BEGIN_CASE(JSOP_CALLFCSLOT)
5309 JS_ASSERT(regs.fp->isFunctionFrame() && !regs.fp->isEvalFrame());
5310 uintN index = GET_UINT16(regs.pc);
5311 JSObject *obj = &argv[-2].toObject();
5313 JS_ASSERT(index < obj->getFunctionPrivate()->script()->bindings.countUpvars());
5314 PUSH_COPY(obj->getFlatClosureUpvar(index));
5315 if (op == JSOP_CALLFCSLOT)
5318 END_CASE(JSOP_GETFCSLOT)
5320 BEGIN_CASE(JSOP_GETGLOBAL)
5321 BEGIN_CASE(JSOP_CALLGLOBAL)
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)
5331 END_CASE(JSOP_GETGLOBAL)
5333 BEGIN_CASE(JSOP_DEFCONST)
5334 BEGIN_CASE(JSOP_DEFVAR)
5336 uint32 index = GET_INDEX(regs.pc);
5337 JSAtom *atom = atoms[index];
5339 JSObject *obj = ®s.fp->varobj(cx);
5340 JS_ASSERT(!obj->getOps()->defineProperty);
5341 uintN attrs = JSPROP_ENUMERATE;
5342 if (!regs.fp->isEvalFrame())
5343 attrs |= JSPROP_PERMANENT;
5345 /* Lookup id in order to check for redeclaration problems. */
5346 jsid id = ATOM_TO_JSID(atom);
5348 if (op == JSOP_DEFVAR) {
5350 * Redundant declaration of a |var|, even one for a non-writable
5351 * property like |undefined| in ES5, does nothing.
5355 if (!obj->lookupProperty(cx, id, &obj2, &prop))
5357 shouldDefine = (!prop || obj2 != obj);
5359 JS_ASSERT(op == JSOP_DEFCONST);
5360 attrs |= JSPROP_READONLY;
5361 if (!CheckRedeclaration(cx, obj, id, attrs))
5365 * As attrs includes readonly, CheckRedeclaration can succeed only
5366 * if prop does not exist.
5368 shouldDefine = true;
5371 /* Bind a variable only if it's not yet defined. */
5373 !js_DefineNativeProperty(cx, obj, id, UndefinedValue(),
5374 PropertyStub, StrictPropertyStub, attrs, 0, 0, NULL)) {
5378 END_CASE(JSOP_DEFVAR)
5380 BEGIN_CASE(JSOP_DEFFUN)
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).
5390 JSObject *obj = FUN_OBJECT(fun);
5393 if (FUN_NULL_CLOSURE(fun)) {
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.
5399 obj2 = ®s.fp->scopeChain();
5401 JS_ASSERT(!fun->isFlatClosure());
5403 obj2 = GetScopeChainFast(cx, regs.fp, JSOP_DEFFUN, JSOP_DEFFUN_LENGTH);
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.
5417 if (obj->getParent() != obj2) {
5418 obj = CloneFunctionObject(cx, fun, obj2);
5424 * ECMA requires functions defined when entering Eval code to be
5427 uintN attrs = regs.fp->isEvalFrame()
5429 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
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.
5436 JSObject *parent = ®s.fp->varobj(cx);
5438 /* ES5 10.5 (NB: with subsequent errata). */
5439 jsid id = ATOM_TO_JSID(fun->atom);
5440 JSProperty *prop = NULL;
5442 if (!parent->lookupProperty(cx, id, &pobj, &prop))
5445 Value rval = ObjectValue(*obj);
5449 if (!prop || pobj != parent) {
5450 if (!parent->defineProperty(cx, id, rval, PropertyStub, StrictPropertyStub, attrs))
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))
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);
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).
5483 if (!parent->setProperty(cx, id, &rval, script->strictModeCode))
5487 END_CASE(JSOP_DEFFUN)
5489 BEGIN_CASE(JSOP_DEFFUN_FC)
5490 BEGIN_CASE(JSOP_DEFFUN_DBGFC)
5495 JSObject *obj = (op == JSOP_DEFFUN_FC)
5496 ? js_NewFlatClosure(cx, fun, JSOP_DEFFUN_FC, JSOP_DEFFUN_FC_LENGTH)
5497 : js_NewDebuggableFlatClosure(cx, fun);
5501 Value rval = ObjectValue(*obj);
5503 uintN attrs = regs.fp->isEvalFrame()
5505 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
5507 JSObject &parent = regs.fp->varobj(cx);
5509 jsid id = ATOM_TO_JSID(fun->atom);
5510 if (!CheckRedeclaration(cx, &parent, id, attrs))
5513 if ((attrs == JSPROP_ENUMERATE)
5514 ? !parent.setProperty(cx, id, &rval, script->strictModeCode)
5515 : !parent.defineProperty(cx, id, rval, PropertyStub, StrictPropertyStub, attrs)) {
5519 END_CASE(JSOP_DEFFUN_FC)
5521 BEGIN_CASE(JSOP_DEFLOCALFUN)
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
5531 LOAD_FUNCTION(SLOTNO_LEN);
5532 JS_ASSERT(fun->isInterpreted());
5533 JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
5534 JSObject *obj = FUN_OBJECT(fun);
5536 if (FUN_NULL_CLOSURE(fun)) {
5537 obj = CloneFunctionObject(cx, fun, ®s.fp->scopeChain());
5541 JSObject *parent = GetScopeChainFast(cx, regs.fp, JSOP_DEFLOCALFUN,
5542 JSOP_DEFLOCALFUN_LENGTH);
5546 if (obj->getParent() != parent) {
5548 if (TRACE_RECORDER(cx))
5549 AbortRecording(cx, "DEFLOCALFUN for closure");
5551 obj = CloneFunctionObject(cx, fun, parent);
5557 uint32 slot = GET_SLOTNO(regs.pc);
5558 TRACE_2(DefLocalFunSetSlot, slot, obj);
5560 regs.fp->slots()[slot].setObject(*obj);
5562 END_CASE(JSOP_DEFLOCALFUN)
5564 BEGIN_CASE(JSOP_DEFLOCALFUN_FC)
5567 LOAD_FUNCTION(SLOTNO_LEN);
5569 JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_DEFLOCALFUN_FC, JSOP_DEFLOCALFUN_FC_LENGTH);
5573 uint32 slot = GET_SLOTNO(regs.pc);
5574 TRACE_2(DefLocalFunSetSlot, slot, obj);
5576 regs.fp->slots()[slot].setObject(*obj);
5578 END_CASE(JSOP_DEFLOCALFUN_FC)
5580 BEGIN_CASE(JSOP_DEFLOCALFUN_DBGFC)
5583 LOAD_FUNCTION(SLOTNO_LEN);
5585 JSObject *obj = js_NewDebuggableFlatClosure(cx, fun);
5589 uint32 slot = GET_SLOTNO(regs.pc);
5590 regs.fp->slots()[slot].setObject(*obj);
5592 END_CASE(JSOP_DEFLOCALFUN_DBGFC)
5594 BEGIN_CASE(JSOP_LAMBDA)
5596 /* Load the specified function object literal. */
5599 JSObject *obj = FUN_OBJECT(fun);
5601 /* do-while(0) so we can break instead of using a goto. */
5604 if (FUN_NULL_CLOSURE(fun)) {
5605 parent = ®s.fp->scopeChain();
5607 if (obj->getParent() == parent) {
5608 jsbytecode *pc2 = AdvanceOverBlockchainOp(regs.pc + JSOP_LAMBDA_LENGTH);
5609 JSOp op2 = JSOp(*pc2);
5612 * Optimize var obj = {method: function () { ... }, ...},
5613 * this.method = function () { ... }; and other significant
5614 * single-use-of-null-closure bytecode sequences.
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).
5620 if (op2 == JSOP_INITMETHOD) {
5622 const Value &lref = regs.sp[-1];
5623 JS_ASSERT(lref.isObject());
5624 JSObject *obj2 = &lref.toObject();
5625 JS_ASSERT(obj2->getClass() == &js_ObjectClass);
5628 fun->setMethodAtom(script->getAtom(GET_FULL_INDEX(pc2 - regs.pc)));
5629 JS_FUNCTION_METER(cx, joinedinitmethod);
5633 if (op2 == JSOP_SETMETHOD) {
5635 op2 = JSOp(pc2[JSOP_SETMETHOD_LENGTH]);
5636 JS_ASSERT(op2 == JSOP_POP || op2 == JSOP_POPV);
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);
5644 } else if (fun->joinable()) {
5645 if (op2 == JSOP_CALL) {
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.
5653 int iargc = GET_ARGC(pc2);
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.
5660 const Value &cref = regs.sp[1 - (iargc + 2)];
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);
5670 if (iargc == 2 && native == str_replace) {
5671 JS_FUNCTION_METER(cx, joinedreplace);
5676 } else if (op2 == JSOP_NULL) {
5677 pc2 += JSOP_NULL_LENGTH;
5680 if (op2 == JSOP_CALL && GET_ARGC(pc2) == 0) {
5681 JS_FUNCTION_METER(cx, joinedmodulepat);
5689 if (rt->functionMeterFilename) {
5690 // No locking, this is mainly for js shell testing.
5691 ++rt->functionMeter.unjoined;
5693 typedef JSRuntime::FunctionCountMap HM;
5694 HM &h = rt->unjoinedFunctionCountMap;
5695 HM::AddPtr p = h.lookupForAdd(fun);
5699 JS_ASSERT(p->key == fun);
5705 parent = GetScopeChainFast(cx, regs.fp, JSOP_LAMBDA, JSOP_LAMBDA_LENGTH);
5710 obj = CloneFunctionObject(cx, fun, parent);
5717 END_CASE(JSOP_LAMBDA)
5719 BEGIN_CASE(JSOP_LAMBDA_FC)
5724 JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH);
5730 END_CASE(JSOP_LAMBDA_FC)
5732 BEGIN_CASE(JSOP_LAMBDA_DBGFC)
5737 JSObject *obj = js_NewDebuggableFlatClosure(cx, fun);
5743 END_CASE(JSOP_LAMBDA_DBGFC)
5745 BEGIN_CASE(JSOP_CALLEE)
5746 JS_ASSERT(regs.fp->isFunctionFrame() && !regs.fp->isEvalFrame());
5747 PUSH_COPY(argv[-2]);
5748 END_CASE(JSOP_CALLEE)
5750 BEGIN_CASE(JSOP_GETTER)
5751 BEGIN_CASE(JSOP_SETTER)
5754 JSOp op2 = (JSOp) *++regs.pc;
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;
5775 id = ATOM_TO_JSID(atom);
5785 FETCH_OBJECT(cx, i - 1, obj);
5790 JS_ASSERT(regs.sp - regs.fp->base() >= 2);
5795 id = ATOM_TO_JSID(atom);
5799 JS_ASSERT(op2 == JSOP_INITELEM);
5801 JS_ASSERT(regs.sp - regs.fp->base() >= 3);
5807 const Value &lref = regs.sp[i-1];
5808 JS_ASSERT(lref.isObject());
5809 obj = &lref.toObject();
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);
5818 if (!js_IsCallable(rval)) {
5819 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5820 JSMSG_BAD_GETTER_OR_SETTER,
5828 * Getters and setters are just like watchpoints from an access control
5833 if (!CheckAccess(cx, obj, id, JSACC_WATCH, &rtmp, &attrs))
5837 StrictPropertyOp setter;
5838 if (op == JSOP_GETTER) {
5839 getter = CastAsPropertyOp(&rval.toObject());
5840 setter = StrictPropertyStub;
5841 attrs = JSPROP_GETTER;
5843 getter = PropertyStub;
5844 setter = CastAsStrictPropertyOp(&rval.toObject());
5845 attrs = JSPROP_SETTER;
5847 attrs |= JSPROP_ENUMERATE | JSPROP_SHARED;
5849 /* Check for a readonly or permanent property of the same name. */
5850 if (!CheckRedeclaration(cx, obj, id, attrs))
5853 if (!obj->defineProperty(cx, id, UndefinedValue(), getter, setter, attrs))
5857 if (js_CodeSpec[op2].ndefs > js_CodeSpec[op2].nuses) {
5858 JS_ASSERT(js_CodeSpec[op2].ndefs == js_CodeSpec[op2].nuses + 1);
5860 assertSameCompartment(cx, regs.sp[-1]);
5862 len = js_CodeSpec[op2].length;
5866 BEGIN_CASE(JSOP_HOLE)
5870 BEGIN_CASE(JSOP_NEWINIT)
5872 jsint i = regs.pc[1];
5874 JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
5877 if (i == JSProto_Array) {
5878 obj = NewDenseEmptyArray(cx);
5880 gc::FinalizeKind kind = GuessObjectGCKind(0, false);
5881 obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
5888 CHECK_INTERRUPT_HANDLER();
5890 END_CASE(JSOP_NEWINIT)
5892 BEGIN_CASE(JSOP_NEWARRAY)
5894 unsigned count = GET_UINT24(regs.pc);
5895 JSObject *obj = NewDenseAllocatedArray(cx, count);
5900 CHECK_INTERRUPT_HANDLER();
5902 END_CASE(JSOP_NEWARRAY)
5904 BEGIN_CASE(JSOP_NEWOBJECT)
5907 LOAD_OBJECT(0, baseobj);
5909 JSObject *obj = CopyInitializerObject(cx, baseobj);
5915 CHECK_INTERRUPT_HANDLER();
5917 END_CASE(JSOP_NEWOBJECT)
5919 BEGIN_CASE(JSOP_ENDINIT)
5921 /* FIXME remove JSOP_ENDINIT bug 588522 */
5922 JS_ASSERT(regs.sp - regs.fp->base() >= 1);
5923 JS_ASSERT(regs.sp[-1].isObject());
5925 END_CASE(JSOP_ENDINIT)
5927 BEGIN_CASE(JSOP_INITPROP)
5928 BEGIN_CASE(JSOP_INITMETHOD)
5930 /* Load the property's initial value into rval. */
5931 JS_ASSERT(regs.sp - regs.fp->base() >= 2);
5932 Value rval = regs.sp[-1];
5934 /* Load the object being initialized into lval/obj. */
5935 JSObject *obj = ®s.sp[-2].toObject();
5936 JS_ASSERT(obj->isObject());
5939 * Probe the property cache.
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.
5945 PropertyCacheEntry *entry;
5947 if (JS_PROPERTY_CACHE(cx).testForInit(rt, regs.pc, obj, &shape, &entry) &&
5948 shape->hasDefaultSetter() &&
5949 shape->previous() == obj->lastProperty())
5951 /* Fast path. Property cache hit. */
5952 uint32 slot = shape->slot;
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());
5959 if (!obj->allocSlot(cx, &slot))
5961 JS_ASSERT(slot == shape->slot);
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);
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.
5974 TRACE_1(AddProperty, obj);
5975 obj->nativeSetSlot(slot, rval);
5977 PCMETER(JS_PROPERTY_CACHE(cx).inipcmisses++);
5979 /* Get the immediate property name into id. */
5982 jsid id = ATOM_TO_JSID(atom);
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,
5996 /* Common tail for property cache hit and miss cases. */
5999 END_CASE(JSOP_INITPROP);
6001 BEGIN_CASE(JSOP_INITELEM)
6003 /* Pop the element's value into rval. */
6004 JS_ASSERT(regs.sp - regs.fp->base() >= 3);
6005 const Value &rref = regs.sp[-1];
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();
6012 /* Fetch id now that we have obj. */
6014 FETCH_ELEMENT_ID(obj, -2, id);
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.
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))) {
6030 if (!obj->defineProperty(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE))
6035 END_CASE(JSOP_INITELEM)
6037 #if JS_HAS_SHARP_VARS
6039 BEGIN_CASE(JSOP_DEFSHARP)
6041 uint32 slot = GET_UINT16(regs.pc);
6042 JS_ASSERT(slot + 1 < regs.fp->numFixed());
6043 const Value &lref = regs.fp->slots()[slot];
6045 if (lref.isObject()) {
6046 obj = &lref.toObject();
6048 JS_ASSERT(lref.isUndefined());
6049 obj = NewDenseEmptyArray(cx);
6052 regs.fp->slots()[slot].setObject(*obj);
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()) {
6059 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
6060 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6061 JSMSG_BAD_SHARP_DEF, numBuf);
6064 if (!obj->defineProperty(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE))
6067 END_CASE(JSOP_DEFSHARP)
6069 BEGIN_CASE(JSOP_USESHARP)
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);
6076 if (lref.isUndefined()) {
6077 rval.setUndefined();
6079 JSObject *obj = ®s.fp->slots()[slot].toObject();
6080 jsid id = INT_TO_JSID(i);
6081 if (!obj->getProperty(cx, id, &rval))
6084 if (!rval.isObjectOrNull()) {
6087 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
6088 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6089 JSMSG_BAD_SHARP_USE, numBuf);
6094 END_CASE(JSOP_USESHARP)
6096 BEGIN_CASE(JSOP_SHARPINIT)
6098 uint32 slot = GET_UINT16(regs.pc);
6099 JS_ASSERT(slot + 1 < regs.fp->numFixed());
6100 Value *vp = ®s.fp->slots()[slot];
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.
6109 if (regs.pc[JSOP_SHARPINIT_LENGTH] != JSOP_ENDINIT) {
6110 rval.setInt32(rval.isUndefined() ? 1 : rval.toInt32() + 1);
6112 JS_ASSERT(rval.isInt32());
6113 rval.getInt32Ref() -= 1;
6114 if (rval.toInt32() == 0)
6115 vp[0].setUndefined();
6119 END_CASE(JSOP_SHARPINIT)
6121 #endif /* JS_HAS_SHARP_VARS */
6124 BEGIN_CASE(JSOP_GOSUB)
6125 PUSH_BOOLEAN(false);
6126 jsint i = (regs.pc - script->main) + JSOP_GOSUB_LENGTH;
6128 len = GET_JUMP_OFFSET(regs.pc);
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);
6142 BEGIN_CASE(JSOP_RETSUB)
6143 /* Pop [exception or hole, retsub pc-index]. */
6147 JS_ASSERT(lval.isBoolean());
6148 if (lval.toBoolean()) {
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.
6155 cx->setPendingException(rval);
6158 JS_ASSERT(rval.isInt32());
6159 len = rval.toInt32();
6160 regs.pc = script->main;
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();
6174 END_CASE(JSOP_EXCEPTION)
6176 BEGIN_CASE(JSOP_FINALLY)
6178 END_CASE(JSOP_FINALLY)
6180 BEGIN_CASE(JSOP_THROWING)
6182 JS_ASSERT(!cx->isExceptionPending());
6185 cx->setPendingException(v);
6187 END_CASE(JSOP_THROWING)
6189 BEGIN_CASE(JSOP_THROW)
6191 JS_ASSERT(!cx->isExceptionPending());
6195 cx->setPendingException(v);
6196 /* let the code at error try to catch the exception. */
6199 BEGIN_CASE(JSOP_SETLOCALPOP)
6202 * The stack must have a block with at least one local slot below the
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]);
6210 END_CASE(JSOP_SETLOCALPOP)
6212 BEGIN_CASE(JSOP_IFPRIMTOP)
6214 * If the top of stack is of primitive type, jump to our target. Otherwise
6215 * advance to the next opcode.
6217 JS_ASSERT(regs.sp > regs.fp->base());
6218 if (regs.sp[-1].isPrimitive()) {
6219 len = GET_JUMP_OFFSET(regs.pc);
6222 END_CASE(JSOP_IFPRIMTOP)
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));
6232 END_CASE(JSOP_PRIMTOP)
6234 BEGIN_CASE(JSOP_OBJTOP)
6235 if (regs.sp[-1].isPrimitive()) {
6236 js_ReportValueError(cx, GET_UINT16(regs.pc), -1, regs.sp[-1], NULL);
6239 END_CASE(JSOP_OBJTOP)
6241 BEGIN_CASE(JSOP_INSTANCEOF)
6243 const Value &rref = regs.sp[-1];
6244 if (rref.isPrimitive()) {
6245 js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, -1, rref, NULL);
6248 JSObject *obj = &rref.toObject();
6249 const Value &lref = regs.sp[-2];
6250 JSBool cond = JS_FALSE;
6251 if (!HasInstance(cx, obj, &lref, &cond))
6254 regs.sp[-1].setBoolean(cond);
6256 END_CASE(JSOP_INSTANCEOF)
6258 BEGIN_CASE(JSOP_DEBUGGER)
6260 JSDebuggerHandler handler = cx->debugHooks->debuggerHandler;
6263 switch (handler(cx, script, regs.pc, Jsvalify(&rval), cx->debugHooks->debuggerHandlerData)) {
6266 case JSTRAP_CONTINUE:
6269 regs.fp->setReturnValue(rval);
6270 interpReturnOK = JS_TRUE;
6273 cx->setPendingException(rval);
6277 CHECK_INTERRUPT_HANDLER();
6280 END_CASE(JSOP_DEBUGGER)
6282 #if JS_HAS_XML_SUPPORT
6283 BEGIN_CASE(JSOP_DEFXMLNS)
6285 if (!js_SetDefaultXMLNamespace(cx, regs.sp[-1]))
6289 END_CASE(JSOP_DEFXMLNS)
6291 BEGIN_CASE(JSOP_ANYNAME)
6294 if (!js_GetAnyName(cx, &id))
6296 PUSH_COPY(IdToValue(id));
6298 END_CASE(JSOP_ANYNAME)
6300 BEGIN_CASE(JSOP_QNAMEPART)
6304 PUSH_STRING(ATOM_TO_STRING(atom));
6306 END_CASE(JSOP_QNAMEPART)
6308 BEGIN_CASE(JSOP_QNAMECONST)
6312 Value rval = StringValue(ATOM_TO_STRING(atom));
6313 Value lval = regs.sp[-1];
6314 JSObject *obj = js_ConstructXMLQNameObject(cx, lval, rval);
6317 regs.sp[-1].setObject(*obj);
6319 END_CASE(JSOP_QNAMECONST)
6321 BEGIN_CASE(JSOP_QNAME)
6323 Value rval = regs.sp[-1];
6324 Value lval = regs.sp[-2];
6325 JSObject *obj = js_ConstructXMLQNameObject(cx, lval, rval);
6329 regs.sp[-1].setObject(*obj);
6331 END_CASE(JSOP_QNAME)
6333 BEGIN_CASE(JSOP_TOATTRNAME)
6337 if (!js_ToAttributeName(cx, &rval))
6341 END_CASE(JSOP_TOATTRNAME)
6343 BEGIN_CASE(JSOP_TOATTRVAL)
6347 JS_ASSERT(rval.isString());
6348 JSString *str = js_EscapeAttributeValue(cx, rval.toString(), JS_FALSE);
6351 regs.sp[-1].setString(str);
6353 END_CASE(JSOP_TOATTRVAL)
6355 BEGIN_CASE(JSOP_ADDATTRNAME)
6356 BEGIN_CASE(JSOP_ADDATTRVAL)
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);
6366 regs.sp[-1].setString(str);
6368 END_CASE(JSOP_ADDATTRNAME)
6370 BEGIN_CASE(JSOP_BINDXMLNAME)
6376 if (!js_FindXMLProperty(cx, lval, &obj, &id))
6378 regs.sp[-1].setObjectOrNull(obj);
6379 PUSH_COPY(IdToValue(id));
6381 END_CASE(JSOP_BINDXMLNAME)
6383 BEGIN_CASE(JSOP_SETXMLNAME)
6385 JSObject *obj = ®s.sp[-3].toObject();
6386 Value rval = regs.sp[-1];
6388 FETCH_ELEMENT_ID(obj, -2, id);
6389 if (!obj->setProperty(cx, id, &rval, script->strictModeCode))
6395 END_CASE(JSOP_SETXMLNAME)
6397 BEGIN_CASE(JSOP_CALLXMLNAME)
6398 BEGIN_CASE(JSOP_XMLNAME)
6400 Value lval = regs.sp[-1];
6403 if (!js_FindXMLProperty(cx, lval, &obj, &id))
6406 if (!obj->getProperty(cx, id, &rval))
6409 if (op == JSOP_CALLXMLNAME)
6410 PUSH_IMPLICIT_THIS(cx, obj, rval);
6412 END_CASE(JSOP_XMLNAME)
6414 BEGIN_CASE(JSOP_DESCENDANTS)
6415 BEGIN_CASE(JSOP_DELDESC)
6418 FETCH_OBJECT(cx, -2, obj);
6419 jsval rval = Jsvalify(regs.sp[-1]);
6420 if (!js_GetXMLDescendants(cx, obj, rval, &rval))
6423 if (op == JSOP_DELDESC) {
6424 regs.sp[-1] = Valueify(rval); /* set local root */
6425 if (!js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval)))
6427 rval = JSVAL_TRUE; /* always succeed */
6431 regs.sp[-1] = Valueify(rval);
6433 END_CASE(JSOP_DESCENDANTS)
6436 BEGIN_CASE(JSOP_FILTER)
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
6443 len = GET_JUMP_OFFSET(regs.pc);
6448 BEGIN_CASE(JSOP_ENDFILTER)
6450 bool cond = !regs.sp[-1].isMagic();
6452 /* Exit the "with" block left from the previous iteration. */
6455 if (!js_StepXMLListFilter(cx, cond))
6457 if (!regs.sp[-1].isNull()) {
6459 * Decrease sp after EnterWith returns as we use sp[-1] there to root
6462 JS_ASSERT(IsXML(regs.sp[-1]));
6463 if (!js_EnterWith(cx, -2, JSOP_ENDFILTER, JSOP_ENDFILTER_LENGTH))
6466 len = GET_JUMP_OFFSET(regs.pc);
6472 END_CASE(JSOP_ENDFILTER);
6474 BEGIN_CASE(JSOP_TOXML)
6476 Value rval = regs.sp[-1];
6477 JSObject *obj = js_ValueToXMLObject(cx, rval);
6480 regs.sp[-1].setObject(*obj);
6482 END_CASE(JSOP_TOXML)
6484 BEGIN_CASE(JSOP_TOXMLLIST)
6486 Value rval = regs.sp[-1];
6487 JSObject *obj = js_ValueToXMLListObject(cx, rval);
6490 regs.sp[-1].setObject(*obj);
6492 END_CASE(JSOP_TOXMLLIST)
6494 BEGIN_CASE(JSOP_XMLTAGEXPR)
6496 Value rval = regs.sp[-1];
6497 JSString *str = js_ValueToString(cx, rval);
6500 regs.sp[-1].setString(str);
6502 END_CASE(JSOP_XMLTAGEXPR)
6504 BEGIN_CASE(JSOP_XMLELTEXPR)
6506 Value rval = regs.sp[-1];
6509 str = js_ValueToXMLString(cx, rval);
6511 str = js_ValueToString(cx, rval);
6513 str = js_EscapeElementValue(cx, str);
6517 regs.sp[-1].setString(str);
6519 END_CASE(JSOP_XMLELTEXPR)
6521 BEGIN_CASE(JSOP_XMLCDATA)
6525 JSString *str = ATOM_TO_STRING(atom);
6526 JSObject *obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_TEXT, NULL, str);
6531 END_CASE(JSOP_XMLCDATA)
6533 BEGIN_CASE(JSOP_XMLCOMMENT)
6537 JSString *str = ATOM_TO_STRING(atom);
6538 JSObject *obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_COMMENT, NULL, str);
6543 END_CASE(JSOP_XMLCOMMENT)
6545 BEGIN_CASE(JSOP_XMLPI)
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);
6555 regs.sp[-1].setObject(*obj);
6557 END_CASE(JSOP_XMLPI)
6559 BEGIN_CASE(JSOP_GETFUNNS)
6562 if (!js_GetFunctionNamespace(cx, &rval))
6566 END_CASE(JSOP_GETFUNNS)
6567 #endif /* JS_HAS_XML_SUPPORT */
6569 BEGIN_CASE(JSOP_ENTERBLOCK)
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);
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
6589 JSObject *obj2 = ®s.fp->scopeChain();
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)
6603 END_CASE(JSOP_ENTERBLOCK)
6605 BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
6606 BEGIN_CASE(JSOP_LEAVEBLOCK)
6608 JSObject *blockChain;
6609 LOAD_OBJECT(UINT16_LEN, blockChain);
6611 JS_ASSERT(blockChain->isStaticBlock());
6612 uintN blockDepth = OBJ_BLOCK_DEPTH(cx, blockChain);
6613 JS_ASSERT(blockDepth <= StackDepth(script));
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.
6620 JSObject &obj = regs.fp->scopeChain();
6621 if (obj.getProto() == blockChain) {
6622 JS_ASSERT(obj.isClonedBlock());
6623 if (!js_PutBlockObject(cx, JS_TRUE))
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)
6631 regs.sp -= GET_UINT16(regs.pc);
6632 if (op == JSOP_LEAVEBLOCKEXPR) {
6633 JS_ASSERT(regs.fp->base() + blockDepth == regs.sp - 1);
6636 JS_ASSERT(regs.fp->base() + blockDepth == regs.sp);
6639 END_CASE(JSOP_LEAVEBLOCK)
6641 #if JS_HAS_GENERATORS
6642 BEGIN_CASE(JSOP_GENERATOR)
6644 JS_ASSERT(!cx->isExceptionPending());
6645 regs.pc += JSOP_GENERATOR_LENGTH;
6646 JSObject *obj = js_NewGenerator(cx);
6649 JS_ASSERT(!regs.fp->hasCallObj() && !regs.fp->hasArgsObj());
6650 regs.fp->setReturnValue(ObjectValue(*obj));
6651 interpReturnOK = true;
6652 if (entryFrame != regs.fp)
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);
6665 regs.fp->setReturnValue(regs.sp[-1]);
6666 regs.fp->setYielding();
6667 regs.pc += JSOP_YIELD_LENGTH;
6668 interpReturnOK = JS_TRUE;
6671 BEGIN_CASE(JSOP_ARRAYPUSH)
6673 uint32 slot = GET_UINT16(regs.pc);
6674 JS_ASSERT(script->nfixed <= slot);
6675 JS_ASSERT(slot < script->nslots);
6676 JSObject *obj = ®s.fp->slots()[slot].toObject();
6677 if (!js_ArrayCompPush(cx, obj, regs.sp[-1]))
6681 END_CASE(JSOP_ARRAYPUSH)
6682 #endif /* JS_HAS_GENERATORS */
6684 #if JS_THREADED_INTERP
6686 L_JSOP_BACKPATCH_POP:
6688 # if !JS_HAS_GENERATORS
6694 # if !JS_HAS_SHARP_VARS
6700 # if !JS_HAS_DESTRUCTURING
6701 L_JSOP_ENUMCONSTELEM:
6704 # if !JS_HAS_XML_SUPPORT
6706 L_JSOP_STARTXMLEXPR:
6734 #endif /* !JS_THREADED_INTERP */
6735 #if !JS_THREADED_INTERP
6740 JS_snprintf(numBuf, sizeof numBuf, "%d", op);
6741 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6742 JSMSG_BAD_BYTECODE, numBuf);
6746 #if !JS_THREADED_INTERP
6749 #endif /* !JS_THREADED_INTERP */
6752 JS_ASSERT(cx->regs == ®s);
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();
6761 JS_ASSERT(size_t((regs.fp->hasImacropc() ? regs.fp->imacropc() : regs.pc) - script->code) <
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.
6770 if (TRACE_RECORDER(cx))
6771 AbortRecording(cx, "error or exception while recording");
6772 # ifdef JS_METHODJIT
6773 if (TRACE_PROFILER(cx))
6778 if (!cx->isExceptionPending()) {
6779 /* This is an error, not a catchable exception, quit the frame ASAP. */
6780 interpReturnOK = JS_FALSE;
6782 JSThrowHook handler;
6783 JSTryNote *tn, *tnlimit;
6786 /* Restore atoms local in case we will resume. */
6787 atoms = script->atomMap.vector;
6789 /* Call debugger throw hook if set. */
6790 handler = cx->debugHooks->throwHook;
6793 switch (handler(cx, script, regs.pc, Jsvalify(&rval),
6794 cx->debugHooks->throwHookData)) {
6796 cx->clearPendingException();
6799 cx->clearPendingException();
6800 regs.fp->setReturnValue(rval);
6801 interpReturnOK = JS_TRUE;
6804 cx->setPendingException(rval);
6805 case JSTRAP_CONTINUE:
6808 CHECK_INTERRUPT_HANDLER();
6812 * Look for a try block in script that can catch this exception.
6814 if (!JSScript::isValidOffset(script->trynotesOffset))
6817 offset = (uint32)(regs.pc - script->main);
6818 tn = script->trynotes()->vector;
6819 tnlimit = tn + script->trynotes()->length;
6821 if (offset - tn->start >= tn->length)
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.
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.
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.
6843 if (tn->stackDepth > regs.sp - regs.fp->base())
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
6851 regs.pc = (script)->main + tn->start + tn->length;
6853 JSBool ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE);
6854 JS_ASSERT(regs.sp == regs.fp->base() + tn->stackDepth);
6857 * Restart the handler search with updated pc and stack depth
6858 * to properly notify the debugger.
6865 #if JS_HAS_GENERATORS
6866 /* Catch cannot intercept the closing of a generator. */
6867 if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING)))
6872 * Don't clear exceptions to save cx->exception from GC
6873 * until it is pushed to the stack via [exception] in the
6881 * Push (true, exception) pair for finally to indicate that
6882 * [retsub] should rethrow the exception.
6885 PUSH_COPY(cx->getPendingException());
6886 cx->clearPendingException();
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, ®s.sp[-1].toObject());
6899 cx->setPendingException(v);
6902 } while (++tn != tnlimit);
6906 * Propagate the exception or error to the caller unless the exception
6907 * is an asynchronous return from a generator.
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();
6922 * Unwind the scope making sure that interpReturnOK stays false even when
6923 * js_UnwindScope returns true.
6925 * When a trap handler returns JSTRAP_RETURN, we jump here with
6926 * interpReturnOK set to true bypassing any finally blocks.
6928 interpReturnOK &= js_UnwindScope(cx, 0, interpReturnOK || cx->isExceptionPending());
6929 JS_ASSERT(regs.sp == regs.fp->base());
6932 cx->logPrevPc = NULL;
6935 if (entryFrame != regs.fp)
6939 interpReturnOK = ScriptEpilogue(cx, regs.fp, interpReturnOK);
6940 regs.fp->setFinishedInInterpreter();
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).
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
6953 JS_ASSERT(entryFrame == regs.fp);
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))
6965 JS_ASSERT_IF(!regs.fp->isGeneratorFrame(), !js_IsActiveWithOrBlock(cx, ®s.fp->scopeChain(), 0));
6967 return interpReturnOK;
6971 JSAutoByteString printable;
6972 if (js_AtomToPrintableString(cx, atomNotDefined, &printable))
6973 js_ReportIsNotDefined(cx, printable.ptr());
6978 * This path is used when it's guaranteed the method can be finished
6981 #if defined(JS_METHODJIT)
6982 leave_on_safe_point:
6984 return interpReturnOK;
6987 } /* namespace js */
6989 #endif /* !defined jsinvoke_cpp___ */