Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / js / src / jsinterp.cpp
blobfde50aec9f10a1925bf11dfd14a96600cc9018b7
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
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
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.
25 * Contributor(s):
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.
44 #include <stdio.h>
45 #include <string.h>
46 #include <math.h>
47 #include "jstypes.h"
48 #include "jsstdint.h"
49 #include "jsarena.h"
50 #include "jsutil.h"
51 #include "jsprf.h"
52 #include "jsapi.h"
53 #include "jsarray.h"
54 #include "jsatom.h"
55 #include "jsbool.h"
56 #include "jscntxt.h"
57 #include "jsdate.h"
58 #include "jsversion.h"
59 #include "jsdbgapi.h"
60 #include "jsfun.h"
61 #include "jsgc.h"
62 #include "jsinterp.h"
63 #include "jsiter.h"
64 #include "jslock.h"
65 #include "jsnum.h"
66 #include "jsobj.h"
67 #include "jsopcode.h"
68 #include "jspropertycache.h"
69 #include "jsscan.h"
70 #include "jsemit.h"
71 #include "jsscope.h"
72 #include "jsscript.h"
73 #include "jsstr.h"
74 #include "jsstaticcheck.h"
75 #include "jstracer.h"
76 #include "jslibmath.h"
77 #include "jsvector.h"
78 #include "methodjit/MethodJIT.h"
79 #include "methodjit/MethodJIT-inl.h"
80 #include "methodjit/Logging.h"
82 #include "jsatominlines.h"
83 #include "jscntxtinlines.h"
84 #include "jsinterpinlines.h"
85 #include "jsobjinlines.h"
86 #include "jsprobes.h"
87 #include "jspropertycacheinlines.h"
88 #include "jsscopeinlines.h"
89 #include "jsscriptinlines.h"
90 #include "jsstrinlines.h"
91 #include "jsopcodeinlines.h"
93 #if JS_HAS_XML_SUPPORT
94 #include "jsxml.h"
95 #endif
97 #include "jsautooplen.h"
99 #if defined(JS_METHODJIT) && defined(JS_MONOIC)
100 #include "methodjit/MonoIC.h"
101 #endif
103 using namespace js;
104 using namespace js::gc;
106 /* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */
107 #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___
109 #ifdef DEBUG
110 JSObject *const JSStackFrame::sInvalidScopeChain = (JSObject *)0xbeef;
111 #endif
113 jsbytecode *
114 JSStackFrame::pc(JSContext *cx, JSStackFrame *next)
116 JS_ASSERT_IF(next, next->prev_ == this);
117 JS_ASSERT(cx->containingSegment(this) != NULL);
119 JSFrameRegs *regs;
120 if (cx->regs) {
121 regs = cx->regs;
122 } else {
123 StackSegment *segment = cx->getCurrentSegment();
124 regs = segment->getSuspendedRegs();
127 if (this == regs->fp)
128 return regs->pc;
130 if (!next)
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_);
139 #else
140 JS_NOT_REACHED("Unknown PC for frame");
141 return NULL;
142 #endif
145 JSObject *
146 js::GetScopeChain(JSContext *cx)
148 JSStackFrame *fp = js_GetTopStackFrame(cx);
149 if (!fp) {
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;
161 if (!obj) {
162 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE);
163 return NULL;
166 OBJ_TO_INNER_OBJECT(cx, obj);
167 return 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.
179 JSObject *
180 js::GetBlockChain(JSContext *cx, JSStackFrame *fp)
182 if (!fp->isScriptFrame())
183 return NULL;
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;
193 uintN indexBase = 0;
194 ptrdiff_t oplen;
195 for (jsbytecode *pc = start; pc < target; pc += oplen) {
196 JSOp op = js_GetOpcode(cx, script, pc);
197 const JSCodeSpec *cs = &js_CodeSpec[op];
198 oplen = cs->length;
199 if (oplen < 0)
200 oplen = js_GetVariableBytecodeLength(pc);
202 if (op == JSOP_INDEXBASE)
203 indexBase = GET_INDEXBASE(pc);
204 else if (op == JSOP_INDEXBASE1 || op == JSOP_INDEXBASE2 || op == JSOP_INDEXBASE3)
205 indexBase = (op - JSOP_INDEXBASE1 + 1) << 16;
206 else if (op == JSOP_RESETBASE || op == JSOP_RESETBASE0)
207 indexBase = 0;
208 else if (op == JSOP_ENTERBLOCK)
209 blockChain = script->getObject(indexBase + GET_INDEX(pc));
210 else if (op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR)
211 blockChain = blockChain->getParent();
212 else if (op == JSOP_BLOCKCHAIN)
213 blockChain = script->getObject(indexBase + GET_INDEX(pc));
214 else if (op == JSOP_NULLBLOCKCHAIN)
215 blockChain = NULL;
218 return blockChain;
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.
228 JSObject *
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);
235 pc += oplen;
236 op = JSOp(*pc);
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)
241 return NULL;
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.
278 static JSObject *
279 GetScopeChainFull(JSContext *cx, JSStackFrame *fp, JSObject *blockChain)
281 JSObject *sharedBlock = blockChain;
283 if (!sharedBlock) {
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(),
289 fp->hasCallObj());
290 return &fp->scopeChain();
293 /* We don't handle cloning blocks on trace. */
294 LeaveTrace(cx);
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))
308 return NULL;
310 /* We know we must clone everything on blockChain. */
311 limitBlock = limitClone = NULL;
312 } else {
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
354 * populate it below.
356 JSObject *innermostNewChild = js_CloneBlockObject(cx, sharedBlock, fp);
357 if (!innermostNewChild)
358 return NULL;
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;
366 for (;;) {
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)
372 break;
374 /* As in the call above, we don't know the real parent yet. */
375 JSObject *clone = js_CloneBlockObject(cx, sharedBlock, fp);
376 if (!clone)
377 return NULL;
379 newChild->setParent(clone);
380 newChild = 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),
392 sharedBlock);
394 /* Place our newly cloned blocks at the head of the scope chain. */
395 fp->setScopeChainNoCallObj(*innermostNewChild);
396 return innermostNewChild;
399 JSObject *
400 js::GetScopeChain(JSContext *cx, JSStackFrame *fp)
402 return GetScopeChainFull(cx, fp, GetBlockChain(cx, fp));
405 JSObject *
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);
416 if (!thisp)
417 return NULL;
418 argv[-1].setObject(*thisp);
419 return 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:
427 * // in window w1
428 * function f() { return this }
429 * function g() { return f }
431 * // in window w2
432 * var h = w1.g()
433 * alert(h() == w1)
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);
441 if (!thisp)
442 return false;
443 vp[1].setObject(*thisp);
444 return true;
447 namespace js {
449 void
450 ReportIncompatibleMethod(JSContext *cx, Value *vp, Class *clasp)
452 Value &thisv = vp[1];
454 #ifdef DEBUG
455 if (thisv.isObject()) {
456 JS_ASSERT(thisv.toObject().getClass() != clasp);
457 } else if (thisv.isString()) {
458 JS_ASSERT(clasp != &js_StringClass);
459 } else if (thisv.isNumber()) {
460 JS_ASSERT(clasp != &js_NumberClass);
461 } else if (thisv.isBoolean()) {
462 JS_ASSERT(clasp != &js_BooleanClass);
463 } else {
464 JS_ASSERT(thisv.isUndefined() || thisv.isNull());
466 #endif
468 if (JSFunction *fun = js_ValueToFunction(cx, &vp[0], 0)) {
469 const char *name = thisv.isObject()
470 ? thisv.toObject().getClass()->name
471 : thisv.isString()
472 ? "string"
473 : thisv.isNumber()
474 ? "number"
475 : thisv.isBoolean()
476 ? "boolean"
477 : thisv.isNull()
478 ? js_null_str
479 : thisv.isUndefined()
480 ? js_undefined_str
481 : "value";
482 JSAutoByteString funNameBytes;
483 if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
484 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
485 clasp->name, funName, name);
490 bool
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());
498 #ifdef DEBUG
499 JSFunction *fun = vp[0].toObject().isFunction() ? vp[0].toObject().getFunctionPrivate() : NULL;
500 JS_ASSERT_IF(fun && fun->isInterpreted(), !fun->inStrictMode());
501 #endif
503 if (vp[1].isNullOrUndefined())
504 return ComputeGlobalThis(cx, vp);
506 if (!vp[1].isObject())
507 return !!js_PrimitiveToObject(cx, &vp[1]);
509 return true;
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 = {
520 "NoSuchMethod",
521 JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
522 PropertyStub, /* addProperty */
523 PropertyStub, /* delProperty */
524 PropertyStub, /* getProperty */
525 StrictPropertyStub, /* setProperty */
526 EnumerateStub,
527 ResolveStub,
528 ConvertStub,
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
543 * parameters.
545 JSBool
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()))
554 return false;
555 if (tvr.value().isPrimitive()) {
556 vp[0] = tvr.value();
557 } else {
558 #if JS_HAS_XML_SUPPORT
559 /* Extract the function name from function::name qname. */
560 if (vp[0].isObject()) {
561 obj = &vp[0].toObject();
562 if (!js_IsFunctionQName(cx, obj, &id))
563 return false;
564 if (!JSID_IS_VOID(id))
565 vp[0] = IdToValue(id);
567 #endif
568 obj = js_NewGCObject(cx, FINALIZE_OBJECT2);
569 if (!obj)
570 return false;
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.
578 obj->map = NULL;
579 obj->init(cx, &js_NoSuchMethodClass, NULL, NULL, NULL, false);
580 obj->setSlot(JSSLOT_FOUND_FUNCTION, tvr.value());
581 obj->setSlot(JSSLOT_SAVED_ID, vp[0]);
582 vp[0].setObject(*obj);
584 return true;
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))
592 return JS_FALSE;
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);
603 if (!argsobj)
604 return JS_FALSE;
605 args[1].setObject(*argsobj);
606 JSBool ok = (flags & JSINVOKE_CONSTRUCT)
607 ? InvokeConstructor(cx, args)
608 : Invoke(cx, args, flags);
609 vp[0] = args.rval();
610 return ok;
613 #endif /* JS_HAS_NO_SUCH_METHOD */
615 namespace js {
617 JS_REQUIRES_STACK bool
618 RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp)
620 JS_ASSERT(script);
622 /* FIXME: Once bug 470510 is fixed, make this an assert. */
623 if (script->compileAndGo) {
624 int32 flags = fp->scopeChain().getGlobal()->getReservedSlot(JSRESERVED_GLOBAL_FLAGS).toInt32();
625 if (flags & JSGLOBAL_FLAGS_CLEARED) {
626 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CLEARED_SCOPE);
627 return false;
631 #ifdef JS_METHODJIT_SPEW
632 JMCheckLogging();
633 #endif
635 AutoInterpPreparer prepareInterp(cx, script);
637 JS_ASSERT(fp == cx->fp());
638 JS_ASSERT(fp->script() == script);
640 #ifdef JS_METHODJIT
641 mjit::CompileStatus status =
642 mjit::CanMethodJIT(cx, script, fp, mjit::CompileRequest_Interpreter);
643 if (status == mjit::Compile_Error)
644 return JS_FALSE;
646 if (status == mjit::Compile_Okay)
647 return mjit::JaegerShot(cx);
648 #endif
650 return Interpret(cx, fp);
654 * Find a function reference and its 'this' value implicit first parameter
655 * under argc arguments on cx's stack, and call the function. Push missing
656 * required arguments, allocate declared local variables, and pop everything
657 * when done. Then push the return value.
659 JS_REQUIRES_STACK bool
660 Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags)
662 /* N.B. Must be kept in sync with InvokeSessionGuard::start/invoke */
664 CallArgs args = argsRef;
665 JS_ASSERT(args.argc() <= JS_ARGS_LENGTH_MAX);
667 if (args.callee().isPrimitive()) {
668 js_ReportIsNotFunction(cx, &args.callee(), flags & JSINVOKE_FUNFLAGS);
669 return false;
672 JSObject &callee = args.callee().toObject();
673 Class *clasp = callee.getClass();
675 /* Invoke non-functions. */
676 if (JS_UNLIKELY(clasp != &js_FunctionClass)) {
677 #if JS_HAS_NO_SUCH_METHOD
678 if (JS_UNLIKELY(clasp == &js_NoSuchMethodClass))
679 return NoSuchMethod(cx, args.argc(), args.base(), 0);
680 #endif
681 JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !clasp->construct);
682 if (!clasp->call) {
683 js_ReportIsNotFunction(cx, &args.callee(), flags);
684 return false;
686 return CallJSNative(cx, clasp->call, args.argc(), args.base());
689 /* Invoke native functions. */
690 JSFunction *fun = callee.getFunctionPrivate();
691 JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !fun->isConstructor());
692 if (fun->isNative())
693 return CallJSNative(cx, fun->u.n.native, args.argc(), args.base());
695 /* Handle the empty-script special case. */
696 JSScript *script = fun->script();
697 if (JS_UNLIKELY(script->isEmpty())) {
698 if (flags & JSINVOKE_CONSTRUCT) {
699 JSObject *obj = js_CreateThisForFunction(cx, &callee);
700 if (!obj)
701 return false;
702 args.rval().setObject(*obj);
703 } else {
704 args.rval().setUndefined();
706 return true;
709 /* Get pointer to new frame/slots, prepare arguments. */
710 InvokeFrameGuard frame;
711 if (JS_UNLIKELY(!cx->stack().getInvokeFrame(cx, args, fun, script, &flags, &frame)))
712 return false;
714 /* Initialize frame, locals. */
715 JSStackFrame *fp = frame.fp();
716 fp->initCallFrame(cx, callee, fun, args.argc(), flags);
717 SetValueRangeToUndefined(fp->slots(), script->nfixed);
719 /* Officially push fp. frame's destructor pops. */
720 cx->stack().pushInvokeFrame(cx, args, &frame);
722 /* Now that the new frame is rooted, maybe create a call object. */
723 if (fun->isHeavyweight() && !js_GetCallObject(cx, fp))
724 return false;
726 /* Run function until JSOP_STOP, JSOP_RETURN or error. */
727 JSBool ok;
729 AutoPreserveEnumerators preserve(cx);
730 ok = RunScript(cx, script, fp);
733 args.rval() = fp->returnValue();
734 JS_ASSERT_IF(ok && (flags & JSINVOKE_CONSTRUCT), !args.rval().isPrimitive());
736 return ok;
739 bool
740 InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &thisv, uintN argc)
742 #ifdef JS_TRACER
743 if (TRACE_RECORDER(cx))
744 AbortRecording(cx, "attempt to reenter VM while recording");
745 LeaveTrace(cx);
746 #endif
748 /* Always push arguments, regardless of optimized/normal invoke. */
749 StackSpace &stack = cx->stack();
750 if (!stack.pushInvokeArgs(cx, argc, &args_))
751 return false;
753 /* Callees may clobber 'this' or 'callee'. */
754 savedCallee_ = args_.callee() = calleev;
755 savedThis_ = args_.thisv() = thisv;
757 do {
758 /* Hoist dynamic checks from scripted Invoke. */
759 if (!calleev.isObject())
760 break;
761 JSObject &callee = calleev.toObject();
762 if (callee.getClass() != &js_FunctionClass)
763 break;
764 JSFunction *fun = callee.getFunctionPrivate();
765 if (fun->isNative())
766 break;
767 script_ = fun->script();
768 if (fun->isHeavyweight() || script_->isEmpty() || cx->compartment->debugMode)
769 break;
771 /* Push the stack frame once for the session. */
772 uint32 flags = 0;
773 if (!stack.getInvokeFrame(cx, args_, fun, script_, &flags, &frame_))
774 return false;
775 JSStackFrame *fp = frame_.fp();
776 fp->initCallFrame(cx, calleev.toObject(), fun, argc, flags);
777 stack.pushInvokeFrame(cx, args_, &frame_);
779 #ifdef JS_METHODJIT
780 /* Hoist dynamic checks from RunScript. */
781 mjit::CompileStatus status = mjit::CanMethodJIT(cx, script_, fp, mjit::CompileRequest_JIT);
782 if (status == mjit::Compile_Error)
783 return false;
784 if (status != mjit::Compile_Okay)
785 break;
786 /* Cannot also cache the raw code pointer; it can change. */
788 /* Hoist dynamic checks from CheckStackAndEnterMethodJIT. */
789 JS_CHECK_RECURSION(cx, return JS_FALSE);
790 stackLimit_ = stack.getStackLimit(cx);
791 if (!stackLimit_)
792 return false;
794 stop_ = script_->code + script_->length - 1;
795 JS_ASSERT(*stop_ == JSOP_STOP);
796 #endif
798 /* Cached to avoid canonicalActualArg in InvokeSessionGuard::operator[]. */
799 nformals_ = fp->numFormalArgs();
800 formals_ = fp->formalArgs();
801 actuals_ = args_.argv();
802 JS_ASSERT(actuals_ == fp->actualArgs());
803 return true;
804 } while (0);
807 * Use the normal invoke path.
809 * The callee slot gets overwritten during an unoptimized Invoke, so we
810 * cache it here and restore it before every Invoke call. The 'this' value
811 * does not get overwritten, so we can fill it here once.
813 if (frame_.pushed())
814 frame_.pop();
815 formals_ = actuals_ = args_.argv();
816 nformals_ = (unsigned)-1;
817 return true;
820 bool
821 ExternalInvoke(JSContext *cx, const Value &thisv, const Value &fval,
822 uintN argc, Value *argv, Value *rval)
824 LeaveTrace(cx);
826 InvokeArgsGuard args;
827 if (!cx->stack().pushInvokeArgs(cx, argc, &args))
828 return false;
830 args.callee() = fval;
831 args.thisv() = thisv;
832 memcpy(args.argv(), argv, argc * sizeof(Value));
834 if (args.thisv().isObject()) {
836 * We must call the thisObject hook in case we are not called from the
837 * interpreter, where a prior bytecode has computed an appropriate
838 * |this| already.
840 JSObject *thisp = args.thisv().toObject().thisObject(cx);
841 if (!thisp)
842 return false;
843 args.thisv().setObject(*thisp);
846 if (!Invoke(cx, args, 0))
847 return false;
849 *rval = args.rval();
850 return true;
853 bool
854 ExternalInvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *argv,
855 Value *rval)
857 LeaveTrace(cx);
859 InvokeArgsGuard args;
860 if (!cx->stack().pushInvokeArgs(cx, argc, &args))
861 return false;
863 args.callee() = fval;
864 args.thisv().setMagic(JS_THIS_POISON);
865 memcpy(args.argv(), argv, argc * sizeof(Value));
867 if (!InvokeConstructor(cx, args))
868 return false;
870 *rval = args.rval();
871 return true;
874 bool
875 ExternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, const Value &fval,
876 JSAccessMode mode, uintN argc, Value *argv, Value *rval)
878 LeaveTrace(cx);
881 * ExternalInvoke could result in another try to get or set the same id
882 * again, see bug 355497.
884 JS_CHECK_RECURSION(cx, return JS_FALSE);
886 return ExternalInvoke(cx, ObjectValue(*obj), fval, argc, argv, rval);
889 bool
890 Execute(JSContext *cx, JSObject *chain, JSScript *script,
891 JSStackFrame *prev, uintN flags, Value *result)
893 JS_ASSERT(chain);
894 JS_ASSERT_IF(prev, !prev->isDummyFrame());
896 if (script->isEmpty()) {
897 if (result)
898 result->setUndefined();
899 return true;
902 LeaveTrace(cx);
905 * Get a pointer to new frame/slots. This memory is not "claimed", so the
906 * code before pushExecuteFrame must not reenter the interpreter.
908 ExecuteFrameGuard frame;
909 if (!cx->stack().getExecuteFrame(cx, script, &frame))
910 return false;
912 /* Initialize fixed slots (GVAR ops expect NULL). */
913 SetValueRangeToNull(frame.fp()->slots(), script->nfixed);
915 /* Initialize frame and locals. */
916 JSObject *initialVarObj;
917 if (prev) {
918 JS_ASSERT(chain == &prev->scopeChain());
919 frame.fp()->initEvalFrame(cx, script, prev, flags);
922 * We want to call |prev->varobj()|, but this requires knowing the
923 * CallStackSegment of |prev|. If |prev == cx->fp()|, the callstack is
924 * simply the context's active callstack, so we can use
925 * |prev->varobj(cx)|. When |prev != cx->fp()|, we need to do a slow
926 * linear search. Luckily, this only happens with EvaluateInFrame.
928 initialVarObj = (prev == cx->maybefp())
929 ? &prev->varobj(cx)
930 : &prev->varobj(cx->containingSegment(prev));
931 } else {
932 /* The scope chain could be anything, so innerize just in case. */
933 JSObject *innerizedChain = chain;
934 OBJ_TO_INNER_OBJECT(cx, innerizedChain);
935 if (!innerizedChain)
936 return false;
938 /* If we were handed a non-native object, complain bitterly. */
939 if (!innerizedChain->isNative()) {
940 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
941 JSMSG_NON_NATIVE_SCOPE);
942 return false;
945 /* Initialize frame. */
946 frame.fp()->initGlobalFrame(script, *innerizedChain, flags);
948 /* Compute 'this'. */
949 JSObject *thisp = chain->thisObject(cx);
950 if (!thisp)
951 return false;
952 frame.fp()->globalThis().setObject(*thisp);
954 initialVarObj = cx->hasRunOption(JSOPTION_VAROBJFIX) ? chain->getGlobal() : chain;
958 * Strict mode eval code receives its own, fresh lexical environment; thus
959 * strict mode eval can't mutate its calling frame's binding set.
961 if ((flags & JSFRAME_EVAL) && script->strictModeCode) {
962 AutoScriptRooter root(cx, script);
963 initialVarObj = NewCallObject(cx, &script->bindings, *initialVarObj, NULL);
964 if (!initialVarObj)
965 return false;
966 initialVarObj->setPrivate(frame.fp());
968 /* Clear the Call object propagated from the previous frame, if any. */
969 if (frame.fp()->hasCallObj())
970 frame.fp()->clearCallObj();
971 frame.fp()->setScopeChainAndCallObj(*initialVarObj);
973 JS_ASSERT(!initialVarObj->getOps()->defineProperty);
975 #if JS_HAS_SHARP_VARS
976 JS_STATIC_ASSERT(SHARP_NSLOTS == 2);
977 if (script->hasSharps) {
978 JS_ASSERT(script->nfixed >= SHARP_NSLOTS);
979 Value *sharps = &frame.fp()->slots()[script->nfixed - SHARP_NSLOTS];
980 if (prev && prev->script()->hasSharps) {
981 JS_ASSERT(prev->numFixed() >= SHARP_NSLOTS);
982 int base = (prev->isFunctionFrame() && !prev->isEvalOrDebuggerFrame())
983 ? prev->fun()->script()->bindings.sharpSlotBase(cx)
984 : prev->numFixed() - SHARP_NSLOTS;
985 if (base < 0)
986 return false;
987 sharps[0] = prev->slots()[base];
988 sharps[1] = prev->slots()[base + 1];
989 } else {
990 sharps[0].setUndefined();
991 sharps[1].setUndefined();
994 #endif
996 /* Officially push |fp|. |frame|'s destructor pops. */
997 cx->stack().pushExecuteFrame(cx, initialVarObj, &frame);
999 /* Now that the frame has been pushed, we can call the thisObject hook. */
1000 if (!prev) {
1001 JSObject *thisp = chain->thisObject(cx);
1002 if (!thisp)
1003 return false;
1004 frame.fp()->globalThis().setObject(*thisp);
1007 Probes::startExecution(cx, script);
1009 /* Run script until JSOP_STOP or error. */
1010 AutoPreserveEnumerators preserve(cx);
1011 JSBool ok = RunScript(cx, script, frame.fp());
1012 if (result)
1013 *result = frame.fp()->returnValue();
1015 Probes::stopExecution(cx, script);
1017 return !!ok;
1020 bool
1021 CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs)
1023 JSObject *obj2;
1024 JSProperty *prop;
1025 uintN oldAttrs;
1026 bool isFunction;
1027 const char *type, *name;
1029 if (!obj->lookupProperty(cx, id, &obj2, &prop))
1030 return false;
1031 if (!prop)
1032 return true;
1033 if (obj2->isNative()) {
1034 oldAttrs = ((Shape *) prop)->attributes();
1035 } else {
1036 if (!obj2->getAttributes(cx, id, &oldAttrs))
1037 return false;
1040 /* We allow redeclaring some non-readonly properties. */
1041 if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) {
1042 /* Allow redeclaration of variables and functions. */
1043 if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
1044 return true;
1047 * Allow adding a getter only if a property already has a setter
1048 * but no getter and similarly for adding a setter. That is, we
1049 * allow only the following transitions:
1051 * no-property --> getter --> getter + setter
1052 * no-property --> setter --> getter + setter
1054 if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0)
1055 return true;
1058 * Allow redeclaration of an impermanent property (in which case
1059 * anyone could delete it and redefine it, willy-nilly).
1061 if (!(oldAttrs & JSPROP_PERMANENT))
1062 return true;
1065 isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
1066 if (!isFunction) {
1067 Value value;
1068 if (!obj->getProperty(cx, id, &value))
1069 return JS_FALSE;
1070 isFunction = IsFunctionObject(value);
1073 type = (oldAttrs & attrs & JSPROP_GETTER)
1074 ? js_getter_str
1075 : (oldAttrs & attrs & JSPROP_SETTER)
1076 ? js_setter_str
1077 : (oldAttrs & JSPROP_READONLY)
1078 ? js_const_str
1079 : isFunction
1080 ? js_function_str
1081 : js_var_str;
1082 JSAutoByteString bytes;
1083 name = js_ValueToPrintable(cx, IdToValue(id), &bytes);
1084 if (!name)
1085 return false;
1086 JS_ALWAYS_FALSE(JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
1087 JSMSG_REDECLARED_VAR, type, name));
1088 return false;
1091 JSBool
1092 HasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
1094 Class *clasp = obj->getClass();
1095 if (clasp->hasInstance)
1096 return clasp->hasInstance(cx, obj, v, bp);
1097 js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
1098 JSDVG_SEARCH_STACK, ObjectValue(*obj), NULL);
1099 return JS_FALSE;
1102 bool
1103 StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref, JSBool *equal)
1105 Value lval = lref, rval = rref;
1106 if (SameType(lval, rval)) {
1107 if (lval.isString())
1108 return EqualStrings(cx, lval.toString(), rval.toString(), equal);
1109 if (lval.isDouble()) {
1110 *equal = JSDOUBLE_COMPARE(lval.toDouble(), ==, rval.toDouble(), JS_FALSE);
1111 return true;
1113 if (lval.isObject()) {
1114 *equal = &lval.toObject() == &rval.toObject();
1115 return true;
1117 if (lval.isUndefined()) {
1118 *equal = true;
1119 return true;
1121 *equal = lval.payloadAsRawUint32() == rval.payloadAsRawUint32();
1122 return true;
1125 if (lval.isDouble() && rval.isInt32()) {
1126 double ld = lval.toDouble();
1127 double rd = rval.toInt32();
1128 *equal = JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1129 return true;
1131 if (lval.isInt32() && rval.isDouble()) {
1132 double ld = lval.toInt32();
1133 double rd = rval.toDouble();
1134 *equal = JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1135 return true;
1138 *equal = false;
1139 return true;
1142 static inline bool
1143 IsNegativeZero(const Value &v)
1145 return v.isDouble() && JSDOUBLE_IS_NEGZERO(v.toDouble());
1148 static inline bool
1149 IsNaN(const Value &v)
1151 return v.isDouble() && JSDOUBLE_IS_NaN(v.toDouble());
1154 bool
1155 SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same)
1157 if (IsNegativeZero(v1)) {
1158 *same = IsNegativeZero(v2);
1159 return true;
1161 if (IsNegativeZero(v2)) {
1162 *same = false;
1163 return true;
1165 if (IsNaN(v1) && IsNaN(v2)) {
1166 *same = true;
1167 return true;
1169 return StrictlyEqual(cx, v1, v2, same);
1172 JSType
1173 TypeOfValue(JSContext *cx, const Value &vref)
1175 Value v = vref;
1176 if (v.isNumber())
1177 return JSTYPE_NUMBER;
1178 if (v.isString())
1179 return JSTYPE_STRING;
1180 if (v.isNull())
1181 return JSTYPE_OBJECT;
1182 if (v.isUndefined())
1183 return JSTYPE_VOID;
1184 if (v.isObject())
1185 return v.toObject().typeOf(cx);
1186 JS_ASSERT(v.isBoolean());
1187 return JSTYPE_BOOLEAN;
1190 bool
1191 InstanceOfSlow(JSContext *cx, JSObject *obj, Class *clasp, Value *argv)
1193 JS_ASSERT(!obj || obj->getClass() != clasp);
1194 if (argv) {
1195 JSFunction *fun = js_ValueToFunction(cx, &argv[-2], 0);
1196 if (fun) {
1197 JSAutoByteString funNameBytes;
1198 if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
1199 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
1200 clasp->name, funName,
1201 obj ? obj->getClass()->name : js_null_str);
1205 return false;
1208 JS_REQUIRES_STACK bool
1209 InvokeConstructor(JSContext *cx, const CallArgs &argsRef)
1211 JS_ASSERT(!js_FunctionClass.construct);
1212 CallArgs args = argsRef;
1214 JSObject *callee;
1215 if (args.callee().isPrimitive() || !(callee = &args.callee().toObject())->getParent()) {
1216 js_ReportIsNotFunction(cx, &args.callee(), JSV2F_CONSTRUCT);
1217 return false;
1220 /* Handle the fast-constructors cases before falling into the general case . */
1221 Class *clasp = callee->getClass();
1222 JSFunction *fun = NULL;
1223 if (clasp == &js_FunctionClass) {
1224 fun = callee->getFunctionPrivate();
1225 if (fun->isConstructor()) {
1226 args.thisv().setMagicWithObjectOrNullPayload(NULL);
1227 return CallJSNativeConstructor(cx, fun->u.n.native, args.argc(), args.base());
1229 } else if (clasp->construct) {
1230 args.thisv().setMagicWithObjectOrNullPayload(NULL);
1231 return CallJSNativeConstructor(cx, clasp->construct, args.argc(), args.base());
1234 /* Scripts create their own |this| in JSOP_BEGIN */
1235 if (!fun || !fun->isInterpreted()) {
1236 JSObject *obj = js_CreateThis(cx, callee);
1237 if (!obj)
1238 return false;
1239 args.thisv().setObject(*obj);
1242 if (!Invoke(cx, args, JSINVOKE_CONSTRUCT))
1243 return false;
1245 if (args.rval().isPrimitive()) {
1246 if (clasp != &js_FunctionClass) {
1247 /* native [[Construct]] returning primitive is error */
1248 JSAutoByteString bytes;
1249 if (js_ValueToPrintable(cx, args.rval(), &bytes)) {
1250 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1251 JSMSG_BAD_NEW_RESULT, bytes.ptr());
1252 return false;
1256 /* The interpreter fixes rval for us. */
1257 JS_ASSERT(!fun->isInterpreted());
1259 args.rval() = args.thisv();
1262 JS_RUNTIME_METER(cx->runtime, constructs);
1263 return true;
1266 bool
1267 InvokeConstructorWithGivenThis(JSContext *cx, JSObject *thisobj, const Value &fval,
1268 uintN argc, Value *argv, Value *rval)
1270 LeaveTrace(cx);
1272 InvokeArgsGuard args;
1273 if (!cx->stack().pushInvokeArgs(cx, argc, &args))
1274 return JS_FALSE;
1276 args.callee() = fval;
1277 /* Initialize args.thisv on all paths below. */
1278 memcpy(args.argv(), argv, argc * sizeof(Value));
1280 /* Handle the fast-constructor cases before calling the general case. */
1281 JSObject &callee = fval.toObject();
1282 Class *clasp = callee.getClass();
1283 JSFunction *fun;
1284 bool ok;
1285 if (clasp == &js_FunctionClass && (fun = callee.getFunctionPrivate())->isConstructor()) {
1286 args.thisv().setMagicWithObjectOrNullPayload(thisobj);
1287 ok = CallJSNativeConstructor(cx, fun->u.n.native, args.argc(), args.base());
1288 } else if (clasp->construct) {
1289 args.thisv().setMagicWithObjectOrNullPayload(thisobj);
1290 ok = CallJSNativeConstructor(cx, clasp->construct, args.argc(), args.base());
1291 } else {
1292 args.thisv().setObjectOrNull(thisobj);
1293 ok = Invoke(cx, args, JSINVOKE_CONSTRUCT);
1296 *rval = args.rval();
1297 return ok;
1300 bool
1301 DirectEval(JSContext *cx, JSFunction *evalfun, uint32 argc, Value *vp)
1303 JS_ASSERT(vp == cx->regs->sp - argc - 2);
1304 JS_ASSERT(vp[0].isObject());
1305 JS_ASSERT(vp[0].toObject().isFunction());
1306 JS_ASSERT(vp[0].toObject().getFunctionPrivate() == evalfun);
1307 JS_ASSERT(IsBuiltinEvalFunction(evalfun));
1309 JSStackFrame *caller = cx->fp();
1310 JS_ASSERT(caller->isScriptFrame());
1311 AutoFunctionCallProbe callProbe(cx, evalfun, caller->script());
1313 JSObject *scopeChain =
1314 GetScopeChainFast(cx, caller, JSOP_EVAL, JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH);
1315 if (!scopeChain || !EvalKernel(cx, argc, vp, DIRECT_EVAL, caller, scopeChain))
1316 return false;
1317 cx->regs->sp = vp + 1;
1318 return true;
1321 bool
1322 ValueToId(JSContext *cx, const Value &v, jsid *idp)
1324 int32_t i;
1325 if (ValueFitsInInt32(v, &i) && INT_FITS_IN_JSID(i)) {
1326 *idp = INT_TO_JSID(i);
1327 return true;
1330 #if JS_HAS_XML_SUPPORT
1331 if (v.isObject()) {
1332 JSObject *obj = &v.toObject();
1333 if (obj->isXMLId()) {
1334 *idp = OBJECT_TO_JSID(obj);
1335 return JS_TRUE;
1338 #endif
1340 return js_ValueToStringId(cx, v, idp);
1343 } /* namespace js */
1346 * Enter the new with scope using an object at sp[-1] and associate the depth
1347 * of the with block with sp + stackIndex.
1349 JS_STATIC_INTERPRET JS_REQUIRES_STACK JSBool
1350 js_EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen)
1352 JSStackFrame *fp = cx->fp();
1353 Value *sp = cx->regs->sp;
1354 JS_ASSERT(stackIndex < 0);
1355 JS_ASSERT(fp->base() <= sp + stackIndex);
1357 JSObject *obj;
1358 if (sp[-1].isObject()) {
1359 obj = &sp[-1].toObject();
1360 } else {
1361 obj = js_ValueToNonNullObject(cx, sp[-1]);
1362 if (!obj)
1363 return JS_FALSE;
1364 sp[-1].setObject(*obj);
1367 JSObject *parent = GetScopeChainFast(cx, fp, op, oplen);
1368 if (!parent)
1369 return JS_FALSE;
1371 OBJ_TO_INNER_OBJECT(cx, obj);
1372 if (!obj)
1373 return JS_FALSE;
1375 JSObject *withobj = js_NewWithObject(cx, obj, parent,
1376 sp + stackIndex - fp->base());
1377 if (!withobj)
1378 return JS_FALSE;
1380 fp->setScopeChainNoCallObj(*withobj);
1381 return JS_TRUE;
1384 JS_STATIC_INTERPRET JS_REQUIRES_STACK void
1385 js_LeaveWith(JSContext *cx)
1387 JSObject *withobj;
1389 withobj = &cx->fp()->scopeChain();
1390 JS_ASSERT(withobj->getClass() == &js_WithClass);
1391 JS_ASSERT(withobj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()));
1392 JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);
1393 withobj->setPrivate(NULL);
1394 cx->fp()->setScopeChainNoCallObj(*withobj->getParent());
1397 JS_REQUIRES_STACK Class *
1398 js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth)
1400 Class *clasp;
1402 clasp = obj->getClass();
1403 if ((clasp == &js_WithClass || clasp == &js_BlockClass) &&
1404 obj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()) &&
1405 OBJ_BLOCK_DEPTH(cx, obj) >= stackDepth) {
1406 return clasp;
1408 return NULL;
1412 * Unwind block and scope chains to match the given depth. The function sets
1413 * fp->sp on return to stackDepth.
1415 JS_REQUIRES_STACK JSBool
1416 js_UnwindScope(JSContext *cx, jsint stackDepth, JSBool normalUnwind)
1418 Class *clasp;
1420 JS_ASSERT(stackDepth >= 0);
1421 JS_ASSERT(cx->fp()->base() + stackDepth <= cx->regs->sp);
1423 JSStackFrame *fp = cx->fp();
1424 for (;;) {
1425 clasp = js_IsActiveWithOrBlock(cx, &fp->scopeChain(), stackDepth);
1426 if (!clasp)
1427 break;
1428 if (clasp == &js_BlockClass) {
1429 /* Don't fail until after we've updated all stacks. */
1430 normalUnwind &= js_PutBlockObject(cx, normalUnwind);
1431 } else {
1432 js_LeaveWith(cx);
1436 cx->regs->sp = fp->base() + stackDepth;
1437 return normalUnwind;
1440 JS_STATIC_INTERPRET JSBool
1441 js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, Value *vp, Value *vp2)
1443 if (cs->format & JOF_POST) {
1444 double d;
1445 if (!ValueToNumber(cx, *vp, &d))
1446 return JS_FALSE;
1447 vp->setNumber(d);
1448 (cs->format & JOF_INC) ? ++d : --d;
1449 vp2->setNumber(d);
1450 return JS_TRUE;
1453 double d;
1454 if (!ValueToNumber(cx, *vp, &d))
1455 return JS_FALSE;
1456 (cs->format & JOF_INC) ? ++d : --d;
1457 vp->setNumber(d);
1458 *vp2 = *vp;
1459 return JS_TRUE;
1462 const Value &
1463 js::GetUpvar(JSContext *cx, uintN closureLevel, UpvarCookie cookie)
1465 JS_ASSERT(closureLevel >= cookie.level() && cookie.level() > 0);
1466 const uintN targetLevel = closureLevel - cookie.level();
1467 JS_ASSERT(targetLevel < UpvarCookie::UPVAR_LEVEL_LIMIT);
1469 JSStackFrame *fp = cx->findFrameAtLevel(targetLevel);
1470 uintN slot = cookie.slot();
1471 Value *vp;
1473 if (!fp->isFunctionFrame() || fp->isEvalFrame()) {
1474 vp = fp->slots() + fp->numFixed();
1475 } else if (slot < fp->numFormalArgs()) {
1476 vp = fp->formalArgs();
1477 } else if (slot == UpvarCookie::CALLEE_SLOT) {
1478 vp = &fp->calleeValue();
1479 slot = 0;
1480 } else {
1481 slot -= fp->numFormalArgs();
1482 JS_ASSERT(slot < fp->numSlots());
1483 vp = fp->slots();
1486 return vp[slot];
1489 #ifdef DEBUG
1491 JS_STATIC_INTERPRET JS_REQUIRES_STACK void
1492 js_LogOpcode(JSContext *cx)
1494 FILE *logfp;
1495 JSStackFrame *fp;
1496 JSFrameRegs *regs;
1497 intN ndefs, n, nuses;
1498 JSOp op;
1500 logfp = (FILE *) cx->logfp;
1501 JS_ASSERT(logfp);
1502 fp = cx->fp();
1503 regs = cx->regs;
1506 * Operations in prologues don't produce interesting values, and
1507 * js_DecompileValueGenerator isn't set up to handle them anyway.
1509 if (cx->logPrevPc && regs->pc >= fp->script()->main) {
1510 JSOp logPrevOp = JSOp(*cx->logPrevPc);
1511 ndefs = js_GetStackDefs(cx, &js_CodeSpec[logPrevOp], logPrevOp,
1512 fp->script(), cx->logPrevPc);
1515 * If there aren't that many elements on the stack, then we have
1516 * probably entered a new frame, and printing output would just be
1517 * misleading.
1519 if (ndefs != 0 &&
1520 ndefs < regs->sp - fp->slots()) {
1521 for (n = -ndefs; n < 0; n++) {
1522 char *bytes = DecompileValueGenerator(cx, n, regs->sp[n], NULL);
1523 if (bytes) {
1524 fprintf(logfp, "%s %s",
1525 (n == -ndefs) ? " output:" : ",",
1526 bytes);
1527 cx->free(bytes);
1528 } else {
1529 JS_ClearPendingException(cx);
1532 fprintf(logfp, " @ %u\n", (uintN) (regs->sp - fp->base()));
1534 fprintf(logfp, " stack: ");
1535 for (Value *siter = fp->base(); siter < regs->sp; siter++) {
1536 if (siter->isObject() && siter->toObject().getClass() == &js_CallClass) {
1538 * Call objects have NULL convert ops so that we catch cases
1539 * where they escape. So js_ValueToString doesn't work on them.
1541 fputs("<call>", logfp);
1542 } else {
1543 JSString *str = js_ValueToString(cx, *siter);
1544 JSLinearString *linearStr = str ? str->ensureLinear(cx) : NULL;
1545 if (!linearStr) {
1546 fputs("<null>", logfp);
1547 JS_ClearPendingException(cx);
1548 } else {
1549 FileEscapedString(logfp, linearStr, 0);
1552 fputc(' ', logfp);
1554 fputc('\n', logfp);
1557 fprintf(logfp, "%4u: ",
1558 js_PCToLineNumber(cx, fp->script(),
1559 fp->hasImacropc() ? fp->imacropc() : regs->pc));
1560 js_Disassemble1(cx, fp->script(), regs->pc,
1561 regs->pc - fp->script()->code,
1562 JS_FALSE, logfp);
1563 op = (JSOp) *regs->pc;
1564 nuses = js_GetStackUses(&js_CodeSpec[op], op, regs->pc);
1565 if (nuses != 0) {
1566 for (n = -nuses; n < 0; n++) {
1567 char *bytes = DecompileValueGenerator(cx, n, regs->sp[n], NULL);
1568 if (bytes) {
1569 fprintf(logfp, "%s %s",
1570 (n == -nuses) ? " inputs:" : ",",
1571 bytes);
1572 cx->free(bytes);
1573 } else {
1574 JS_ClearPendingException(cx);
1577 fprintf(logfp, " @ %u\n", (uintN) (regs->sp - fp->base()));
1579 cx->logPrevPc = regs->pc;
1581 /* It's nice to have complete logs when debugging a crash. */
1582 fflush(logfp);
1585 #endif /* DEBUG */
1587 #ifdef JS_OPMETER
1589 # include <stdlib.h>
1591 # define HIST_NSLOTS 8
1594 * The second dimension is hardcoded at 256 because we know that many bits fit
1595 * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address
1596 * any particular row.
1598 static uint32 succeeds[JSOP_LIMIT][256];
1599 static uint32 slot_ops[JSOP_LIMIT][HIST_NSLOTS];
1601 JS_STATIC_INTERPRET void
1602 js_MeterOpcodePair(JSOp op1, JSOp op2)
1604 if (op1 != JSOP_STOP)
1605 ++succeeds[op1][op2];
1608 JS_STATIC_INTERPRET void
1609 js_MeterSlotOpcode(JSOp op, uint32 slot)
1611 if (slot < HIST_NSLOTS)
1612 ++slot_ops[op][slot];
1615 typedef struct Edge {
1616 const char *from;
1617 const char *to;
1618 uint32 count;
1619 } Edge;
1621 static int
1622 compare_edges(const void *a, const void *b)
1624 const Edge *ea = (const Edge *) a;
1625 const Edge *eb = (const Edge *) b;
1627 return (int32)eb->count - (int32)ea->count;
1630 void
1631 js_DumpOpMeters()
1633 const char *name, *from, *style;
1634 FILE *fp;
1635 uint32 total, count;
1636 uint32 i, j, nedges;
1637 Edge *graph;
1639 name = getenv("JS_OPMETER_FILE");
1640 if (!name)
1641 name = "/tmp/ops.dot";
1642 fp = fopen(name, "w");
1643 if (!fp) {
1644 perror(name);
1645 return;
1648 total = nedges = 0;
1649 for (i = 0; i < JSOP_LIMIT; i++) {
1650 for (j = 0; j < JSOP_LIMIT; j++) {
1651 count = succeeds[i][j];
1652 if (count != 0) {
1653 total += count;
1654 ++nedges;
1659 # define SIGNIFICANT(count,total) (200. * (count) >= (total))
1661 graph = (Edge *) js_calloc(nedges * sizeof graph[0]);
1662 if (!graph)
1663 return;
1664 for (i = nedges = 0; i < JSOP_LIMIT; i++) {
1665 from = js_CodeName[i];
1666 for (j = 0; j < JSOP_LIMIT; j++) {
1667 count = succeeds[i][j];
1668 if (count != 0 && SIGNIFICANT(count, total)) {
1669 graph[nedges].from = from;
1670 graph[nedges].to = js_CodeName[j];
1671 graph[nedges].count = count;
1672 ++nedges;
1676 qsort(graph, nedges, sizeof(Edge), compare_edges);
1678 # undef SIGNIFICANT
1680 fputs("digraph {\n", fp);
1681 for (i = 0, style = NULL; i < nedges; i++) {
1682 JS_ASSERT(i == 0 || graph[i-1].count >= graph[i].count);
1683 if (!style || graph[i-1].count != graph[i].count) {
1684 style = (i > nedges * .75) ? "dotted" :
1685 (i > nedges * .50) ? "dashed" :
1686 (i > nedges * .25) ? "solid" : "bold";
1688 fprintf(fp, " %s -> %s [label=\"%lu\" style=%s]\n",
1689 graph[i].from, graph[i].to,
1690 (unsigned long)graph[i].count, style);
1692 js_free(graph);
1693 fputs("}\n", fp);
1694 fclose(fp);
1696 name = getenv("JS_OPMETER_HIST");
1697 if (!name)
1698 name = "/tmp/ops.hist";
1699 fp = fopen(name, "w");
1700 if (!fp) {
1701 perror(name);
1702 return;
1704 fputs("bytecode", fp);
1705 for (j = 0; j < HIST_NSLOTS; j++)
1706 fprintf(fp, " slot %1u", (unsigned)j);
1707 putc('\n', fp);
1708 fputs("========", fp);
1709 for (j = 0; j < HIST_NSLOTS; j++)
1710 fputs(" =======", fp);
1711 putc('\n', fp);
1712 for (i = 0; i < JSOP_LIMIT; i++) {
1713 for (j = 0; j < HIST_NSLOTS; j++) {
1714 if (slot_ops[i][j] != 0) {
1715 /* Reuse j in the next loop, since we break after. */
1716 fprintf(fp, "%-8.8s", js_CodeName[i]);
1717 for (j = 0; j < HIST_NSLOTS; j++)
1718 fprintf(fp, " %7lu", (unsigned long)slot_ops[i][j]);
1719 putc('\n', fp);
1720 break;
1724 fclose(fp);
1727 #endif /* JS_OPSMETER */
1729 #endif /* !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___ */
1731 #ifndef jsinvoke_cpp___
1733 #ifdef JS_REPRMETER
1734 // jsval representation metering: this measures the kinds of jsvals that
1735 // are used as inputs to each JSOp.
1736 namespace reprmeter {
1737 enum Repr {
1738 NONE,
1739 INT,
1740 DOUBLE,
1741 BOOLEAN_PROPER,
1742 BOOLEAN_OTHER,
1743 STRING,
1744 OBJECT_NULL,
1745 OBJECT_PLAIN,
1746 FUNCTION_INTERPRETED,
1747 FUNCTION_FASTNATIVE,
1748 ARRAY_SLOW,
1749 ARRAY_DENSE
1752 // Return the |repr| value giving the representation of the given jsval.
1753 static Repr
1754 GetRepr(jsval v)
1756 if (JSVAL_IS_INT(v))
1757 return INT;
1758 if (JSVAL_IS_DOUBLE(v))
1759 return DOUBLE;
1760 if (JSVAL_IS_SPECIAL(v)) {
1761 return (v == JSVAL_TRUE || v == JSVAL_FALSE)
1762 ? BOOLEAN_PROPER
1763 : BOOLEAN_OTHER;
1765 if (JSVAL_IS_STRING(v))
1766 return STRING;
1768 JS_ASSERT(JSVAL_IS_OBJECT(v));
1770 JSObject *obj = JSVAL_TO_OBJECT(v);
1771 if (VALUE_IS_FUNCTION(cx, v)) {
1772 JSFunction *fun = obj->getFunctionPrivate();
1773 if (FUN_INTERPRETED(fun))
1774 return FUNCTION_INTERPRETED;
1775 return FUNCTION_FASTNATIVE;
1777 // This must come before the general array test, because that
1778 // one subsumes this one.
1779 if (!obj)
1780 return OBJECT_NULL;
1781 if (obj->isDenseArray())
1782 return ARRAY_DENSE;
1783 if (obj->isArray())
1784 return ARRAY_SLOW;
1785 return OBJECT_PLAIN;
1788 static const char *reprName[] = { "invalid", "int", "double", "bool", "special",
1789 "string", "null", "object",
1790 "fun:interp", "fun:native"
1791 "array:slow", "array:dense" };
1793 // Logically, a tuple of (JSOp, repr_1, ..., repr_n) where repr_i is
1794 // the |repr| of the ith input to the JSOp.
1795 struct OpInput {
1796 enum { max_uses = 16 };
1798 JSOp op;
1799 Repr uses[max_uses];
1801 OpInput() : op(JSOp(255)) {
1802 for (int i = 0; i < max_uses; ++i)
1803 uses[i] = NONE;
1806 OpInput(JSOp op) : op(op) {
1807 for (int i = 0; i < max_uses; ++i)
1808 uses[i] = NONE;
1811 // Hash function
1812 operator uint32() const {
1813 uint32 h = op;
1814 for (int i = 0; i < max_uses; ++i)
1815 h = h * 7 + uses[i] * 13;
1816 return h;
1819 bool operator==(const OpInput &opinput) const {
1820 if (op != opinput.op)
1821 return false;
1822 for (int i = 0; i < max_uses; ++i) {
1823 if (uses[i] != opinput.uses[i])
1824 return false;
1826 return true;
1829 OpInput &operator=(const OpInput &opinput) {
1830 op = opinput.op;
1831 for (int i = 0; i < max_uses; ++i)
1832 uses[i] = opinput.uses[i];
1833 return *this;
1837 typedef HashMap<OpInput, uint64, DefaultHasher<OpInput>, SystemAllocPolicy> OpInputHistogram;
1839 OpInputHistogram opinputs;
1840 bool opinputsInitialized = false;
1842 // Record an OpInput for the current op. This should be called just
1843 // before executing the op.
1844 static void
1845 MeterRepr(JSContext *cx)
1847 // Note that we simply ignore the possibility of errors (OOMs)
1848 // using the hash map, since this is only metering code.
1850 if (!opinputsInitialized) {
1851 opinputs.init();
1852 opinputsInitialized = true;
1855 JSOp op = JSOp(*cx->regs->pc);
1856 int nuses = js_GetStackUses(&js_CodeSpec[op], op, cx->regs->pc);
1858 // Build the OpInput.
1859 OpInput opinput(op);
1860 for (int i = 0; i < nuses; ++i) {
1861 jsval v = cx->regs->sp[-nuses+i];
1862 opinput.uses[i] = GetRepr(v);
1865 OpInputHistogram::AddPtr p = opinputs.lookupForAdd(opinput);
1866 if (p)
1867 ++p->value;
1868 else
1869 opinputs.add(p, opinput, 1);
1872 void
1873 js_DumpReprMeter()
1875 FILE *f = fopen("/tmp/reprmeter.txt", "w");
1876 JS_ASSERT(f);
1877 for (OpInputHistogram::Range r = opinputs.all(); !r.empty(); r.popFront()) {
1878 const OpInput &o = r.front().key;
1879 uint64 c = r.front().value;
1880 fprintf(f, "%3d,%s", o.op, js_CodeName[o.op]);
1881 for (int i = 0; i < OpInput::max_uses && o.uses[i] != NONE; ++i)
1882 fprintf(f, ",%s", reprName[o.uses[i]]);
1883 fprintf(f, ",%llu\n", c);
1885 fclose(f);
1888 #endif /* JS_REPRMETER */
1890 #define PUSH_COPY(v) do { *regs.sp++ = v; assertSameCompartment(cx, regs.sp[-1]); } while (0)
1891 #define PUSH_NULL() regs.sp++->setNull()
1892 #define PUSH_UNDEFINED() regs.sp++->setUndefined()
1893 #define PUSH_BOOLEAN(b) regs.sp++->setBoolean(b)
1894 #define PUSH_DOUBLE(d) regs.sp++->setDouble(d)
1895 #define PUSH_INT32(i) regs.sp++->setInt32(i)
1896 #define PUSH_STRING(s) do { regs.sp++->setString(s); assertSameCompartment(cx, regs.sp[-1]); } while (0)
1897 #define PUSH_OBJECT(obj) do { regs.sp++->setObject(obj); assertSameCompartment(cx, regs.sp[-1]); } while (0)
1898 #define PUSH_OBJECT_OR_NULL(obj) do { regs.sp++->setObjectOrNull(obj); assertSameCompartment(cx, regs.sp[-1]); } while (0)
1899 #define PUSH_HOLE() regs.sp++->setMagic(JS_ARRAY_HOLE)
1900 #define POP_COPY_TO(v) v = *--regs.sp
1901 #define POP_RETURN_VALUE() regs.fp->setReturnValue(*--regs.sp)
1903 #define POP_BOOLEAN(cx, vp, b) \
1904 JS_BEGIN_MACRO \
1905 vp = &regs.sp[-1]; \
1906 if (vp->isNull()) { \
1907 b = false; \
1908 } else if (vp->isBoolean()) { \
1909 b = vp->toBoolean(); \
1910 } else { \
1911 b = !!js_ValueToBoolean(*vp); \
1913 regs.sp--; \
1914 JS_END_MACRO
1916 #define VALUE_TO_OBJECT(cx, vp, obj) \
1917 JS_BEGIN_MACRO \
1918 if ((vp)->isObject()) { \
1919 obj = &(vp)->toObject(); \
1920 } else { \
1921 obj = js_ValueToNonNullObject(cx, *(vp)); \
1922 if (!obj) \
1923 goto error; \
1924 (vp)->setObject(*obj); \
1926 JS_END_MACRO
1928 #define FETCH_OBJECT(cx, n, obj) \
1929 JS_BEGIN_MACRO \
1930 Value *vp_ = &regs.sp[n]; \
1931 VALUE_TO_OBJECT(cx, vp_, obj); \
1932 JS_END_MACRO
1934 #define DEFAULT_VALUE(cx, n, hint, v) \
1935 JS_BEGIN_MACRO \
1936 JS_ASSERT(v.isObject()); \
1937 JS_ASSERT(v == regs.sp[n]); \
1938 if (!DefaultValue(cx, &v.toObject(), hint, &regs.sp[n])) \
1939 goto error; \
1940 v = regs.sp[n]; \
1941 JS_END_MACRO
1943 /* Test whether v is an int in the range [-2^31 + 1, 2^31 - 2] */
1944 static JS_ALWAYS_INLINE bool
1945 CanIncDecWithoutOverflow(int32_t i)
1947 return (i > JSVAL_INT_MIN) && (i < JSVAL_INT_MAX);
1951 * Define JS_OPMETER to instrument bytecode succession, generating a .dot file
1952 * on shutdown that shows the graph of significant predecessor/successor pairs
1953 * executed, where the edge labels give the succession counts. The .dot file
1954 * is named by the JS_OPMETER_FILE envariable, and defaults to /tmp/ops.dot.
1956 * Bonus feature: JS_OPMETER also enables counters for stack-addressing ops
1957 * such as JSOP_GETLOCAL, JSOP_INCARG, via METER_SLOT_OP. The resulting counts
1958 * are written to JS_OPMETER_HIST, defaulting to /tmp/ops.hist.
1960 #ifndef JS_OPMETER
1961 # define METER_OP_INIT(op) /* nothing */
1962 # define METER_OP_PAIR(op1,op2) /* nothing */
1963 # define METER_SLOT_OP(op,slot) /* nothing */
1964 #else
1967 * The second dimension is hardcoded at 256 because we know that many bits fit
1968 * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address
1969 * any particular row.
1971 # define METER_OP_INIT(op) ((op) = JSOP_STOP)
1972 # define METER_OP_PAIR(op1,op2) (js_MeterOpcodePair(op1, op2))
1973 # define METER_SLOT_OP(op,slot) (js_MeterSlotOpcode(op, slot))
1975 #endif
1977 #ifdef JS_REPRMETER
1978 # define METER_REPR(cx) (reprmeter::MeterRepr(cx))
1979 #else
1980 # define METER_REPR(cx) ((void) 0)
1981 #endif /* JS_REPRMETER */
1984 * Threaded interpretation via computed goto appears to be well-supported by
1985 * GCC 3 and higher. IBM's C compiler when run with the right options (e.g.,
1986 * -qlanglvl=extended) also supports threading. Ditto the SunPro C compiler.
1987 * Currently it's broken for JS_VERSION < 160, though this isn't worth fixing.
1988 * Add your compiler support macros here.
1990 #ifndef JS_THREADED_INTERP
1991 # if JS_VERSION >= 160 && ( \
1992 __GNUC__ >= 3 || \
1993 (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \
1994 __SUNPRO_C >= 0x570)
1995 # define JS_THREADED_INTERP 1
1996 # else
1997 # define JS_THREADED_INTERP 0
1998 # endif
1999 #endif
2002 * Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on
2003 * single-thread DEBUG js shell testing to verify property cache hits.
2005 #if defined DEBUG && !defined JS_THREADSAFE
2007 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) \
2008 JS_BEGIN_MACRO \
2009 if (!AssertValidPropertyCacheHit(cx, script, regs, pcoff, obj, pobj, \
2010 entry)) { \
2011 goto error; \
2013 JS_END_MACRO
2015 static bool
2016 AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
2017 ptrdiff_t pcoff, JSObject *start, JSObject *found,
2018 PropertyCacheEntry *entry)
2020 uint32 sample = cx->runtime->gcNumber;
2022 JSAtom *atom;
2023 if (pcoff >= 0)
2024 GET_ATOM_FROM_BYTECODE(script, regs.pc, pcoff, atom);
2025 else
2026 atom = cx->runtime->atomState.lengthAtom;
2028 JSObject *obj, *pobj;
2029 JSProperty *prop;
2030 JSBool ok;
2032 if (JOF_OPMODE(*regs.pc) == JOF_NAME) {
2033 ok = js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &pobj, &prop);
2034 } else {
2035 obj = start;
2036 ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop);
2038 if (!ok)
2039 return false;
2040 if (cx->runtime->gcNumber != sample || entry->vshape() != pobj->shape())
2041 return true;
2042 JS_ASSERT(prop);
2043 JS_ASSERT(pobj == found);
2045 const Shape *shape = (Shape *) prop;
2046 if (entry->vword.isSlot()) {
2047 JS_ASSERT(entry->vword.toSlot() == shape->slot);
2048 JS_ASSERT(!shape->isMethod());
2049 } else if (entry->vword.isShape()) {
2050 JS_ASSERT(entry->vword.toShape() == shape);
2051 JS_ASSERT_IF(shape->isMethod(),
2052 &shape->methodObject() == &pobj->nativeGetSlot(shape->slot).toObject());
2053 } else {
2054 Value v;
2055 JS_ASSERT(entry->vword.isFunObj());
2056 JS_ASSERT(!entry->vword.isNull());
2057 JS_ASSERT(pobj->brandedOrHasMethodBarrier());
2058 JS_ASSERT(shape->hasDefaultGetterOrIsMethod());
2059 JS_ASSERT(pobj->containsSlot(shape->slot));
2060 v = pobj->nativeGetSlot(shape->slot);
2061 JS_ASSERT(&entry->vword.toFunObj() == &v.toObject());
2063 if (shape->isMethod()) {
2064 JS_ASSERT(js_CodeSpec[*regs.pc].format & JOF_CALLOP);
2065 JS_ASSERT(&shape->methodObject() == &v.toObject());
2069 return true;
2072 #else
2073 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0)
2074 #endif
2077 * Ensure that the intrepreter switch can close call-bytecode cases in the
2078 * same way as non-call bytecodes.
2080 JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
2081 JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_GETFCSLOT_LENGTH);
2082 JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_CALLUPVAR_DBG_LENGTH);
2083 JS_STATIC_ASSERT(JSOP_GETFCSLOT_LENGTH == JSOP_CALLFCSLOT_LENGTH);
2084 JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
2085 JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
2086 JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH);
2089 * Same for debuggable flat closures defined at top level in another function
2090 * or program fragment.
2092 JS_STATIC_ASSERT(JSOP_DEFFUN_FC_LENGTH == JSOP_DEFFUN_DBGFC_LENGTH);
2095 * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
2096 * remain distinct for the decompiler. Likewise for JSOP_INIT{PROP,METHOD}.
2098 JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
2099 JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETMETHOD_LENGTH);
2100 JS_STATIC_ASSERT(JSOP_INITPROP_LENGTH == JSOP_INITMETHOD_LENGTH);
2102 /* See TRY_BRANCH_AFTER_COND. */
2103 JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH);
2104 JS_STATIC_ASSERT(JSOP_IFNE == JSOP_IFEQ + 1);
2106 /* For the fastest case inder JSOP_INCNAME, etc. */
2107 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_DECNAME_LENGTH);
2108 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEINC_LENGTH);
2109 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH);
2111 #ifdef JS_TRACER
2112 # define ABORT_RECORDING(cx, reason) \
2113 JS_BEGIN_MACRO \
2114 if (TRACE_RECORDER(cx)) \
2115 AbortRecording(cx, reason); \
2116 JS_END_MACRO
2117 #else
2118 # define ABORT_RECORDING(cx, reason) ((void) 0)
2119 #endif
2122 * Inline fast paths for iteration. js_IteratorMore and js_IteratorNext handle
2123 * all cases, but we inline the most frequently taken paths here.
2125 static inline bool
2126 IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, Value *rval)
2128 if (iterobj->getClass() == &js_IteratorClass) {
2129 NativeIterator *ni = (NativeIterator *) iterobj->getPrivate();
2130 if (ni->isKeyIter()) {
2131 *cond = (ni->props_cursor < ni->props_end);
2132 return true;
2135 if (!js_IteratorMore(cx, iterobj, rval))
2136 return false;
2137 *cond = rval->isTrue();
2138 return true;
2141 static inline bool
2142 IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
2144 if (iterobj->getClass() == &js_IteratorClass) {
2145 NativeIterator *ni = (NativeIterator *) iterobj->getPrivate();
2146 if (ni->isKeyIter()) {
2147 JS_ASSERT(ni->props_cursor < ni->props_end);
2148 jsid id = *ni->current();
2149 if (JSID_IS_ATOM(id)) {
2150 rval->setString(JSID_TO_STRING(id));
2151 ni->incCursor();
2152 return true;
2154 /* Take the slow path if we have to stringify a numeric property name. */
2157 return js_IteratorNext(cx, iterobj, rval);
2160 static inline bool
2161 ScriptPrologue(JSContext *cx, JSStackFrame *fp)
2163 if (fp->isConstructing()) {
2164 JSObject *obj = js_CreateThisForFunction(cx, &fp->callee());
2165 if (!obj)
2166 return false;
2167 fp->functionThis().setObject(*obj);
2169 if (fp->isExecuteFrame()) {
2170 if (JSInterpreterHook hook = cx->debugHooks->executeHook)
2171 fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->executeHookData));
2172 } else {
2173 if (JSInterpreterHook hook = cx->debugHooks->callHook)
2174 fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData));
2175 Probes::enterJSFun(cx, fp->maybeFun(), fp->maybeScript());
2178 return true;
2181 namespace js {
2183 JS_REQUIRES_STACK JS_NEVER_INLINE bool
2184 Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInterpMode interpMode)
2186 #ifdef MOZ_TRACEVIS
2187 TraceVisStateObj tvso(cx, S_INTERP);
2188 #endif
2189 JSAutoResolveFlags rf(cx, JSRESOLVE_INFER);
2191 # ifdef DEBUG
2193 * We call this macro from BEGIN_CASE in threaded interpreters,
2194 * and before entering the switch in non-threaded interpreters.
2195 * However, reaching such points doesn't mean we've actually
2196 * fetched an OP from the instruction stream: some opcodes use
2197 * 'op=x; DO_OP()' to let another opcode's implementation finish
2198 * their work, and many opcodes share entry points with a run of
2199 * consecutive BEGIN_CASEs.
2201 * Take care to trace OP only when it is the opcode fetched from
2202 * the instruction stream, so the trace matches what one would
2203 * expect from looking at the code. (We do omit POPs after SETs;
2204 * unfortunate, but not worth fixing.)
2206 # define LOG_OPCODE(OP) JS_BEGIN_MACRO \
2207 if (JS_UNLIKELY(cx->logfp != NULL) && \
2208 (OP) == *regs.pc) \
2209 js_LogOpcode(cx); \
2210 JS_END_MACRO
2211 # else
2212 # define LOG_OPCODE(OP) ((void) 0)
2213 # endif
2216 * Macros for threaded interpreter loop
2218 #if JS_THREADED_INTERP
2219 static void *const normalJumpTable[] = {
2220 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2221 JS_EXTENSION &&L_##op,
2222 # include "jsopcode.tbl"
2223 # undef OPDEF
2226 static void *const interruptJumpTable[] = {
2227 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2228 JS_EXTENSION &&interrupt,
2229 # include "jsopcode.tbl"
2230 # undef OPDEF
2233 register void * const *jumpTable = normalJumpTable;
2235 METER_OP_INIT(op); /* to nullify first METER_OP_PAIR */
2237 # define ENABLE_INTERRUPTS() ((void) (jumpTable = interruptJumpTable))
2239 # ifdef JS_TRACER
2240 # define CHECK_RECORDER() \
2241 JS_ASSERT_IF(TRACE_RECORDER(cx), jumpTable == interruptJumpTable)
2242 # else
2243 # define CHECK_RECORDER() ((void)0)
2244 # endif
2246 # define DO_OP() JS_BEGIN_MACRO \
2247 CHECK_RECORDER(); \
2248 JS_EXTENSION_(goto *jumpTable[op]); \
2249 JS_END_MACRO
2250 # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
2251 METER_OP_PAIR(op, JSOp(regs.pc[n])); \
2252 op = (JSOp) *(regs.pc += (n)); \
2253 METER_REPR(cx); \
2254 DO_OP(); \
2255 JS_END_MACRO
2257 # define BEGIN_CASE(OP) L_##OP: LOG_OPCODE(OP); CHECK_RECORDER();
2258 # define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH);
2259 # define END_VARLEN_CASE DO_NEXT_OP(len);
2260 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP) \
2261 JS_ASSERT(js_CodeSpec[OP].length == 1); \
2262 op = (JSOp) *++regs.pc; \
2263 DO_OP();
2265 # define END_EMPTY_CASES
2267 #else /* !JS_THREADED_INTERP */
2269 register intN switchMask = 0;
2270 intN switchOp;
2272 # define ENABLE_INTERRUPTS() ((void) (switchMask = -1))
2274 # ifdef JS_TRACER
2275 # define CHECK_RECORDER() \
2276 JS_ASSERT_IF(TRACE_RECORDER(cx), switchMask == -1)
2277 # else
2278 # define CHECK_RECORDER() ((void)0)
2279 # endif
2281 # define DO_OP() goto do_op
2282 # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
2283 JS_ASSERT((n) == len); \
2284 goto advance_pc; \
2285 JS_END_MACRO
2287 # define BEGIN_CASE(OP) case OP: CHECK_RECORDER();
2288 # define END_CASE(OP) END_CASE_LEN(OP##_LENGTH)
2289 # define END_CASE_LEN(n) END_CASE_LENX(n)
2290 # define END_CASE_LENX(n) END_CASE_LEN##n
2293 * To share the code for all len == 1 cases we use the specialized label with
2294 * code that falls through to advance_pc: .
2296 # define END_CASE_LEN1 goto advance_pc_by_one;
2297 # define END_CASE_LEN2 len = 2; goto advance_pc;
2298 # define END_CASE_LEN3 len = 3; goto advance_pc;
2299 # define END_CASE_LEN4 len = 4; goto advance_pc;
2300 # define END_CASE_LEN5 len = 5; goto advance_pc;
2301 # define END_VARLEN_CASE goto advance_pc;
2302 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)
2303 # define END_EMPTY_CASES goto advance_pc_by_one;
2305 #endif /* !JS_THREADED_INTERP */
2307 #define LOAD_ATOM(PCOFF, atom) \
2308 JS_BEGIN_MACRO \
2309 JS_ASSERT(regs.fp->hasImacropc() \
2310 ? atoms == COMMON_ATOMS_START(&rt->atomState) && \
2311 GET_INDEX(regs.pc + PCOFF) < js_common_atom_count \
2312 : (size_t)(atoms - script->atomMap.vector) < \
2313 (size_t)(script->atomMap.length - \
2314 GET_INDEX(regs.pc + PCOFF))); \
2315 atom = atoms[GET_INDEX(regs.pc + PCOFF)]; \
2316 JS_END_MACRO
2318 #define GET_FULL_INDEX(PCOFF) \
2319 (atoms - script->atomMap.vector + GET_INDEX(regs.pc + (PCOFF)))
2321 #define LOAD_OBJECT(PCOFF, obj) \
2322 (obj = script->getObject(GET_FULL_INDEX(PCOFF)))
2324 #define LOAD_FUNCTION(PCOFF) \
2325 (fun = script->getFunction(GET_FULL_INDEX(PCOFF)))
2327 #define LOAD_DOUBLE(PCOFF, dbl) \
2328 (dbl = script->getConst(GET_FULL_INDEX(PCOFF)).toDouble())
2330 #ifdef JS_METHODJIT
2331 bool useMethodJIT = cx->methodJitEnabled && interpMode == JSINTERP_NORMAL;
2332 #else
2333 bool useMethodJIT = false;
2334 #endif
2336 #ifdef JS_METHODJIT
2338 #define MONITOR_BRANCH_METHODJIT() \
2339 JS_BEGIN_MACRO \
2340 mjit::CompileStatus status = \
2341 mjit::CanMethodJITAtBranch(cx, script, regs.fp, regs.pc); \
2342 if (status == mjit::Compile_Error) \
2343 goto error; \
2344 if (status == mjit::Compile_Okay) { \
2345 void *ncode = \
2346 script->nativeCodeForPC(regs.fp->isConstructing(), regs.pc); \
2347 interpReturnOK = mjit::JaegerShotAtSafePoint(cx, ncode); \
2348 if (inlineCallCount) \
2349 goto jit_return; \
2350 goto leave_on_safe_point; \
2352 if (status == mjit::Compile_Abort) { \
2353 useMethodJIT = false; \
2355 JS_END_MACRO
2357 #else
2359 #define MONITOR_BRANCH_METHODJIT() ((void) 0)
2361 #endif
2363 #ifdef JS_TRACER
2365 #ifdef MOZ_TRACEVIS
2366 #if JS_THREADED_INTERP
2367 #define MONITOR_BRANCH_TRACEVIS \
2368 JS_BEGIN_MACRO \
2369 if (jumpTable != interruptJumpTable) \
2370 EnterTraceVisState(cx, S_RECORD, R_NONE); \
2371 JS_END_MACRO
2372 #else /* !JS_THREADED_INTERP */
2373 #define MONITOR_BRANCH_TRACEVIS \
2374 JS_BEGIN_MACRO \
2375 EnterTraceVisState(cx, S_RECORD, R_NONE); \
2376 JS_END_MACRO
2377 #endif
2378 #else
2379 #define MONITOR_BRANCH_TRACEVIS
2380 #endif
2382 #define RESTORE_INTERP_VARS() \
2383 JS_BEGIN_MACRO \
2384 script = regs.fp->script(); \
2385 argv = regs.fp->maybeFormalArgs(); \
2386 atoms = FrameAtomBase(cx, regs.fp); \
2387 JS_ASSERT(cx->regs == &regs); \
2388 if (cx->isExceptionPending()) \
2389 goto error; \
2390 JS_END_MACRO
2392 #define MONITOR_BRANCH() \
2393 JS_BEGIN_MACRO \
2394 if (TRACING_ENABLED(cx)) { \
2395 if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && useMethodJIT) { \
2396 MONITOR_BRANCH_METHODJIT(); \
2397 } else { \
2398 MonitorResult r = MonitorLoopEdge(cx, inlineCallCount, interpMode); \
2399 if (r == MONITOR_RECORDING) { \
2400 JS_ASSERT(TRACE_RECORDER(cx)); \
2401 JS_ASSERT(!TRACE_PROFILER(cx)); \
2402 MONITOR_BRANCH_TRACEVIS; \
2403 ENABLE_INTERRUPTS(); \
2404 CLEAR_LEAVE_ON_TRACE_POINT(); \
2406 RESTORE_INTERP_VARS(); \
2407 JS_ASSERT_IF(cx->isExceptionPending(), r == MONITOR_ERROR); \
2408 if (r == MONITOR_ERROR) \
2409 goto error; \
2411 } else { \
2412 MONITOR_BRANCH_METHODJIT(); \
2414 JS_END_MACRO
2416 #else /* !JS_TRACER */
2418 #define MONITOR_BRANCH() \
2419 JS_BEGIN_MACRO \
2420 MONITOR_BRANCH_METHODJIT(); \
2421 JS_END_MACRO
2423 #endif /* !JS_TRACER */
2426 * Prepare to call a user-supplied branch handler, and abort the script
2427 * if it returns false.
2429 #define CHECK_BRANCH() \
2430 JS_BEGIN_MACRO \
2431 if (JS_THREAD_DATA(cx)->interruptFlags && !js_HandleExecutionInterrupt(cx)) \
2432 goto error; \
2433 JS_END_MACRO
2435 #if defined(JS_TRACER) && defined(JS_METHODJIT)
2436 # define LEAVE_ON_SAFE_POINT() \
2437 do { \
2438 JS_ASSERT_IF(leaveOnSafePoint, !TRACE_RECORDER(cx)); \
2439 JS_ASSERT_IF(leaveOnSafePoint, !TRACE_PROFILER(cx)); \
2440 JS_ASSERT_IF(leaveOnSafePoint, interpMode != JSINTERP_NORMAL); \
2441 if (leaveOnSafePoint && !regs.fp->hasImacropc() && \
2442 script->maybeNativeCodeForPC(regs.fp->isConstructing(), regs.pc)) { \
2443 JS_ASSERT(!TRACE_RECORDER(cx)); \
2444 interpReturnOK = true; \
2445 goto leave_on_safe_point; \
2447 } while (0)
2448 #else
2449 # define LEAVE_ON_SAFE_POINT() /* nop */
2450 #endif
2452 #define BRANCH(n) \
2453 JS_BEGIN_MACRO \
2454 regs.pc += (n); \
2455 op = (JSOp) *regs.pc; \
2456 if ((n) <= 0) { \
2457 CHECK_BRANCH(); \
2458 if (op == JSOP_NOTRACE) { \
2459 if (TRACE_RECORDER(cx) || TRACE_PROFILER(cx)) { \
2460 MONITOR_BRANCH(); \
2461 op = (JSOp) *regs.pc; \
2463 } else if (op == JSOP_TRACE) { \
2464 MONITOR_BRANCH(); \
2465 op = (JSOp) *regs.pc; \
2468 LEAVE_ON_SAFE_POINT(); \
2469 DO_OP(); \
2470 JS_END_MACRO
2472 #define CHECK_INTERRUPT_HANDLER() \
2473 JS_BEGIN_MACRO \
2474 if (cx->debugHooks->interruptHook) \
2475 ENABLE_INTERRUPTS(); \
2476 JS_END_MACRO
2478 JSFrameRegs regs = *cx->regs;
2480 /* Repoint cx->regs to a local variable for faster access. */
2481 struct InterpExitGuard {
2482 JSContext *cx;
2483 const JSFrameRegs &regs;
2484 JSFrameRegs *prevContextRegs;
2485 InterpExitGuard(JSContext *cx, JSFrameRegs &regs)
2486 : cx(cx), regs(regs), prevContextRegs(cx->regs) {
2487 cx->setCurrentRegs(&regs);
2488 ++cx->interpLevel;
2490 ~InterpExitGuard() {
2491 --cx->interpLevel;
2492 JS_ASSERT(cx->regs == &regs);
2493 *prevContextRegs = regs;
2494 cx->setCurrentRegs(prevContextRegs);
2496 } interpGuard(cx, regs);
2498 /* Copy in hot values that change infrequently. */
2499 JSRuntime *const rt = cx->runtime;
2500 JSScript *script = regs.fp->script();
2501 Value *argv = regs.fp->maybeFormalArgs();
2502 CHECK_INTERRUPT_HANDLER();
2504 #if defined(JS_TRACER) && defined(JS_METHODJIT)
2505 bool leaveOnSafePoint = (interpMode == JSINTERP_SAFEPOINT);
2506 # define CLEAR_LEAVE_ON_TRACE_POINT() ((void) (leaveOnSafePoint = false))
2507 #else
2508 # define CLEAR_LEAVE_ON_TRACE_POINT() ((void) 0)
2509 #endif
2511 if (!entryFrame)
2512 entryFrame = regs.fp;
2515 * Initialize the index segment register used by LOAD_ATOM and
2516 * GET_FULL_INDEX macros below. As a register we use a pointer based on
2517 * the atom map to turn frequently executed LOAD_ATOM into simple array
2518 * access. For less frequent object and regexp loads we have to recover
2519 * the segment from atoms pointer first.
2521 JSAtom **atoms = script->atomMap.vector;
2523 #if JS_HAS_GENERATORS
2524 if (JS_UNLIKELY(regs.fp->isGeneratorFrame())) {
2525 JS_ASSERT(interpGuard.prevContextRegs == &cx->generatorFor(regs.fp)->regs);
2526 JS_ASSERT((size_t) (regs.pc - script->code) <= script->length);
2527 JS_ASSERT((size_t) (regs.sp - regs.fp->base()) <= StackDepth(script));
2530 * To support generator_throw and to catch ignored exceptions,
2531 * fail if cx->isExceptionPending() is true.
2533 if (cx->isExceptionPending())
2534 goto error;
2536 #endif
2538 #ifdef JS_TRACER
2540 * The method JIT may have already initiated a recording, in which case
2541 * there should already be a valid recorder. Otherwise...
2542 * we cannot reenter the interpreter while recording.
2544 if (interpMode == JSINTERP_RECORD) {
2545 JS_ASSERT(TRACE_RECORDER(cx));
2546 JS_ASSERT(!TRACE_PROFILER(cx));
2547 ENABLE_INTERRUPTS();
2548 } else if (interpMode == JSINTERP_PROFILE) {
2549 ENABLE_INTERRUPTS();
2550 } else if (TRACE_RECORDER(cx)) {
2551 AbortRecording(cx, "attempt to reenter interpreter while recording");
2554 if (regs.fp->hasImacropc())
2555 atoms = COMMON_ATOMS_START(&rt->atomState);
2556 #endif
2558 /* Don't call the script prologue if executing between Method and Trace JIT. */
2559 if (interpMode == JSINTERP_NORMAL) {
2560 JS_ASSERT_IF(!regs.fp->isGeneratorFrame(), regs.pc == script->code);
2561 if (!ScriptPrologue(cx, regs.fp))
2562 goto error;
2565 CHECK_INTERRUPT_HANDLER();
2567 /* State communicated between non-local jumps: */
2568 JSBool interpReturnOK;
2569 JSAtom *atomNotDefined;
2572 * It is important that "op" be initialized before calling DO_OP because
2573 * it is possible for "op" to be specially assigned during the normal
2574 * processing of an opcode while looping. We rely on DO_NEXT_OP to manage
2575 * "op" correctly in all other cases.
2577 JSOp op;
2578 jsint len;
2579 len = 0;
2581 /* Check for too deep of a native thread stack. */
2582 #ifdef JS_TRACER
2583 #ifdef JS_METHODJIT
2584 JS_CHECK_RECURSION(cx, do {
2585 if (TRACE_RECORDER(cx))
2586 AbortRecording(cx, "too much recursion");
2587 if (TRACE_PROFILER(cx))
2588 AbortProfiling(cx);
2589 goto error;
2590 } while (0););
2591 #else
2592 JS_CHECK_RECURSION(cx, do {
2593 if (TRACE_RECORDER(cx))
2594 AbortRecording(cx, "too much recursion");
2595 goto error;
2596 } while (0););
2597 #endif
2598 #else
2599 JS_CHECK_RECURSION(cx, return JS_FALSE);
2600 #endif
2602 #if JS_THREADED_INTERP
2603 DO_NEXT_OP(len);
2604 #else
2605 DO_NEXT_OP(len);
2606 #endif
2608 #if JS_THREADED_INTERP
2610 * This is a loop, but it does not look like a loop. The loop-closing
2611 * jump is distributed throughout goto *jumpTable[op] inside of DO_OP.
2612 * When interrupts are enabled, jumpTable is set to interruptJumpTable
2613 * where all jumps point to the interrupt label. The latter, after
2614 * calling the interrupt handler, dispatches through normalJumpTable to
2615 * continue the normal bytecode processing.
2618 #else /* !JS_THREADED_INTERP */
2619 for (;;) {
2620 advance_pc_by_one:
2621 JS_ASSERT(js_CodeSpec[op].length == 1);
2622 len = 1;
2623 advance_pc:
2624 regs.pc += len;
2625 op = (JSOp) *regs.pc;
2627 do_op:
2628 CHECK_RECORDER();
2629 LOG_OPCODE(op);
2630 switchOp = intN(op) | switchMask;
2631 do_switch:
2632 switch (switchOp) {
2633 #endif
2635 #if JS_THREADED_INTERP
2636 interrupt:
2637 #else /* !JS_THREADED_INTERP */
2638 case -1:
2639 JS_ASSERT(switchMask == -1);
2640 #endif /* !JS_THREADED_INTERP */
2642 bool moreInterrupts = false;
2643 JSInterruptHook hook = cx->debugHooks->interruptHook;
2644 if (hook) {
2645 #ifdef JS_TRACER
2646 if (TRACE_RECORDER(cx))
2647 AbortRecording(cx, "interrupt hook");
2648 #ifdef JS_METHODJIT
2649 if (TRACE_PROFILER(cx))
2650 AbortProfiling(cx);
2651 #endif
2652 #endif
2653 Value rval;
2654 switch (hook(cx, script, regs.pc, Jsvalify(&rval),
2655 cx->debugHooks->interruptHookData)) {
2656 case JSTRAP_ERROR:
2657 goto error;
2658 case JSTRAP_CONTINUE:
2659 break;
2660 case JSTRAP_RETURN:
2661 regs.fp->setReturnValue(rval);
2662 interpReturnOK = JS_TRUE;
2663 goto forced_return;
2664 case JSTRAP_THROW:
2665 cx->setPendingException(rval);
2666 goto error;
2667 default:;
2669 moreInterrupts = true;
2672 #ifdef JS_TRACER
2673 #ifdef JS_METHODJIT
2674 if (TRACE_PROFILER(cx) && interpMode == JSINTERP_PROFILE) {
2675 LoopProfile *prof = TRACE_PROFILER(cx);
2676 JS_ASSERT(!TRACE_RECORDER(cx));
2677 LoopProfile::ProfileAction act = prof->profileOperation(cx, op);
2678 switch (act) {
2679 case LoopProfile::ProfComplete:
2680 if (interpMode != JSINTERP_NORMAL) {
2681 leaveOnSafePoint = true;
2682 LEAVE_ON_SAFE_POINT();
2684 break;
2685 default:
2686 moreInterrupts = true;
2687 break;
2690 #endif
2691 if (TraceRecorder* tr = TRACE_RECORDER(cx)) {
2692 JS_ASSERT(!TRACE_PROFILER(cx));
2693 AbortableRecordingStatus status = tr->monitorRecording(op);
2694 JS_ASSERT_IF(cx->isExceptionPending(), status == ARECORD_ERROR);
2696 if (interpMode != JSINTERP_NORMAL) {
2697 JS_ASSERT(interpMode == JSINTERP_RECORD || JSINTERP_SAFEPOINT);
2698 switch (status) {
2699 case ARECORD_IMACRO_ABORTED:
2700 case ARECORD_ABORTED:
2701 case ARECORD_COMPLETED:
2702 case ARECORD_STOP:
2703 #ifdef JS_METHODJIT
2704 leaveOnSafePoint = true;
2705 LEAVE_ON_SAFE_POINT();
2706 #endif
2707 break;
2708 default:
2709 break;
2713 switch (status) {
2714 case ARECORD_CONTINUE:
2715 moreInterrupts = true;
2716 break;
2717 case ARECORD_IMACRO:
2718 case ARECORD_IMACRO_ABORTED:
2719 atoms = COMMON_ATOMS_START(&rt->atomState);
2720 op = JSOp(*regs.pc);
2721 CLEAR_LEAVE_ON_TRACE_POINT();
2722 if (status == ARECORD_IMACRO)
2723 DO_OP(); /* keep interrupting for op. */
2724 break;
2725 case ARECORD_ERROR:
2726 // The code at 'error:' aborts the recording.
2727 goto error;
2728 case ARECORD_ABORTED:
2729 case ARECORD_COMPLETED:
2730 break;
2731 case ARECORD_STOP:
2732 /* A 'stop' error should have already aborted recording. */
2733 default:
2734 JS_NOT_REACHED("Bad recording status");
2737 #endif /* !JS_TRACER */
2739 #if JS_THREADED_INTERP
2740 #ifdef MOZ_TRACEVIS
2741 if (!moreInterrupts)
2742 ExitTraceVisState(cx, R_ABORT);
2743 #endif
2744 jumpTable = moreInterrupts ? interruptJumpTable : normalJumpTable;
2745 JS_EXTENSION_(goto *normalJumpTable[op]);
2746 #else
2747 switchMask = moreInterrupts ? -1 : 0;
2748 switchOp = intN(op);
2749 goto do_switch;
2750 #endif
2753 /* No-ops for ease of decompilation. */
2754 ADD_EMPTY_CASE(JSOP_NOP)
2755 ADD_EMPTY_CASE(JSOP_CONDSWITCH)
2756 ADD_EMPTY_CASE(JSOP_TRY)
2757 #if JS_HAS_XML_SUPPORT
2758 ADD_EMPTY_CASE(JSOP_STARTXML)
2759 ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
2760 #endif
2761 ADD_EMPTY_CASE(JSOP_NULLBLOCKCHAIN)
2762 END_EMPTY_CASES
2764 BEGIN_CASE(JSOP_TRACE)
2765 BEGIN_CASE(JSOP_NOTRACE)
2766 LEAVE_ON_SAFE_POINT();
2767 END_CASE(JSOP_TRACE)
2769 /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
2770 BEGIN_CASE(JSOP_LINENO)
2771 END_CASE(JSOP_LINENO)
2773 BEGIN_CASE(JSOP_BLOCKCHAIN)
2774 END_CASE(JSOP_BLOCKCHAIN)
2776 BEGIN_CASE(JSOP_PUSH)
2777 PUSH_UNDEFINED();
2778 END_CASE(JSOP_PUSH)
2780 BEGIN_CASE(JSOP_POP)
2781 regs.sp--;
2782 END_CASE(JSOP_POP)
2784 BEGIN_CASE(JSOP_POPN)
2786 regs.sp -= GET_UINT16(regs.pc);
2787 #ifdef DEBUG
2788 JS_ASSERT(regs.fp->base() <= regs.sp);
2789 JSObject *obj = GetBlockChain(cx, regs.fp);
2790 JS_ASSERT_IF(obj,
2791 OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
2792 <= (size_t) (regs.sp - regs.fp->base()));
2793 for (obj = &regs.fp->scopeChain(); obj; obj = obj->getParent()) {
2794 Class *clasp = obj->getClass();
2795 if (clasp != &js_BlockClass && clasp != &js_WithClass)
2796 continue;
2797 if (obj->getPrivate() != js_FloatingFrameIfGenerator(cx, regs.fp))
2798 break;
2799 JS_ASSERT(regs.fp->base() + OBJ_BLOCK_DEPTH(cx, obj)
2800 + ((clasp == &js_BlockClass)
2801 ? OBJ_BLOCK_COUNT(cx, obj)
2802 : 1)
2803 <= regs.sp);
2805 #endif
2807 END_CASE(JSOP_POPN)
2809 BEGIN_CASE(JSOP_SETRVAL)
2810 BEGIN_CASE(JSOP_POPV)
2811 POP_RETURN_VALUE();
2812 END_CASE(JSOP_POPV)
2814 BEGIN_CASE(JSOP_ENTERWITH)
2815 if (!js_EnterWith(cx, -1, JSOP_ENTERWITH, JSOP_ENTERWITH_LENGTH))
2816 goto error;
2819 * We must ensure that different "with" blocks have different stack depth
2820 * associated with them. This allows the try handler search to properly
2821 * recover the scope chain. Thus we must keep the stack at least at the
2822 * current level.
2824 * We set sp[-1] to the current "with" object to help asserting the
2825 * enter/leave balance in [leavewith].
2827 regs.sp[-1].setObject(regs.fp->scopeChain());
2828 END_CASE(JSOP_ENTERWITH)
2830 BEGIN_CASE(JSOP_LEAVEWITH)
2831 JS_ASSERT(&regs.sp[-1].toObject() == &regs.fp->scopeChain());
2832 regs.sp--;
2833 js_LeaveWith(cx);
2834 END_CASE(JSOP_LEAVEWITH)
2836 BEGIN_CASE(JSOP_RETURN)
2837 POP_RETURN_VALUE();
2838 /* FALL THROUGH */
2840 BEGIN_CASE(JSOP_RETRVAL) /* fp return value already set */
2841 BEGIN_CASE(JSOP_STOP)
2844 * When the inlined frame exits with an exception or an error, ok will be
2845 * false after the inline_return label.
2847 CHECK_BRANCH();
2849 #ifdef JS_TRACER
2850 if (regs.fp->hasImacropc()) {
2852 * If we are at the end of an imacro, return to its caller in the
2853 * current frame.
2855 JS_ASSERT(op == JSOP_STOP);
2856 JS_ASSERT((uintN)(regs.sp - regs.fp->slots()) <= script->nslots);
2857 jsbytecode *imacpc = regs.fp->imacropc();
2858 regs.pc = imacpc + js_CodeSpec[*imacpc].length;
2859 regs.fp->clearImacropc();
2860 LEAVE_ON_SAFE_POINT();
2861 atoms = script->atomMap.vector;
2862 op = JSOp(*regs.pc);
2863 DO_OP();
2865 #endif
2867 interpReturnOK = true;
2868 if (entryFrame != regs.fp)
2869 inline_return:
2871 JS_ASSERT(!js_IsActiveWithOrBlock(cx, &regs.fp->scopeChain(), 0));
2872 interpReturnOK = ScriptEpilogue(cx, regs.fp, interpReturnOK);
2873 CHECK_INTERRUPT_HANDLER();
2875 /* The JIT inlines ScriptEpilogue. */
2876 jit_return:
2877 Value *newsp = regs.fp->actualArgs() - 1;
2878 newsp[-1] = regs.fp->returnValue();
2879 cx->stack().popInlineFrame(cx, regs.fp->prev(), newsp);
2881 /* Sync interpreter registers. */
2882 script = regs.fp->script();
2883 argv = regs.fp->maybeFormalArgs();
2884 atoms = FrameAtomBase(cx, regs.fp);
2886 /* Resume execution in the calling frame. */
2887 JS_ASSERT(inlineCallCount);
2888 inlineCallCount--;
2889 if (JS_LIKELY(interpReturnOK)) {
2890 JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, script, regs.pc)].length
2891 == JSOP_CALL_LENGTH);
2892 TRACE_0(LeaveFrame);
2893 len = JSOP_CALL_LENGTH;
2894 DO_NEXT_OP(len);
2896 goto error;
2897 } else {
2898 JS_ASSERT(regs.sp == regs.fp->base());
2900 interpReturnOK = true;
2901 goto exit;
2904 BEGIN_CASE(JSOP_DEFAULT)
2905 regs.sp--;
2906 /* FALL THROUGH */
2907 BEGIN_CASE(JSOP_GOTO)
2909 len = GET_JUMP_OFFSET(regs.pc);
2910 BRANCH(len);
2912 END_CASE(JSOP_GOTO)
2914 BEGIN_CASE(JSOP_IFEQ)
2916 bool cond;
2917 Value *_;
2918 POP_BOOLEAN(cx, _, cond);
2919 if (cond == false) {
2920 len = GET_JUMP_OFFSET(regs.pc);
2921 BRANCH(len);
2924 END_CASE(JSOP_IFEQ)
2926 BEGIN_CASE(JSOP_IFNE)
2928 bool cond;
2929 Value *_;
2930 POP_BOOLEAN(cx, _, cond);
2931 if (cond != false) {
2932 len = GET_JUMP_OFFSET(regs.pc);
2933 BRANCH(len);
2936 END_CASE(JSOP_IFNE)
2938 BEGIN_CASE(JSOP_OR)
2940 bool cond;
2941 Value *vp;
2942 POP_BOOLEAN(cx, vp, cond);
2943 if (cond == true) {
2944 len = GET_JUMP_OFFSET(regs.pc);
2945 PUSH_COPY(*vp);
2946 DO_NEXT_OP(len);
2949 END_CASE(JSOP_OR)
2951 BEGIN_CASE(JSOP_AND)
2953 bool cond;
2954 Value *vp;
2955 POP_BOOLEAN(cx, vp, cond);
2956 if (cond == false) {
2957 len = GET_JUMP_OFFSET(regs.pc);
2958 PUSH_COPY(*vp);
2959 DO_NEXT_OP(len);
2962 END_CASE(JSOP_AND)
2964 BEGIN_CASE(JSOP_DEFAULTX)
2965 regs.sp--;
2966 /* FALL THROUGH */
2967 BEGIN_CASE(JSOP_GOTOX)
2969 len = GET_JUMPX_OFFSET(regs.pc);
2970 BRANCH(len);
2972 END_CASE(JSOP_GOTOX);
2974 BEGIN_CASE(JSOP_IFEQX)
2976 bool cond;
2977 Value *_;
2978 POP_BOOLEAN(cx, _, cond);
2979 if (cond == false) {
2980 len = GET_JUMPX_OFFSET(regs.pc);
2981 BRANCH(len);
2984 END_CASE(JSOP_IFEQX)
2986 BEGIN_CASE(JSOP_IFNEX)
2988 bool cond;
2989 Value *_;
2990 POP_BOOLEAN(cx, _, cond);
2991 if (cond != false) {
2992 len = GET_JUMPX_OFFSET(regs.pc);
2993 BRANCH(len);
2996 END_CASE(JSOP_IFNEX)
2998 BEGIN_CASE(JSOP_ORX)
3000 bool cond;
3001 Value *vp;
3002 POP_BOOLEAN(cx, vp, cond);
3003 if (cond == true) {
3004 len = GET_JUMPX_OFFSET(regs.pc);
3005 PUSH_COPY(*vp);
3006 DO_NEXT_OP(len);
3009 END_CASE(JSOP_ORX)
3011 BEGIN_CASE(JSOP_ANDX)
3013 bool cond;
3014 Value *vp;
3015 POP_BOOLEAN(cx, vp, cond);
3016 if (cond == JS_FALSE) {
3017 len = GET_JUMPX_OFFSET(regs.pc);
3018 PUSH_COPY(*vp);
3019 DO_NEXT_OP(len);
3022 END_CASE(JSOP_ANDX)
3025 * If the index value at sp[n] is not an int that fits in a jsval, it could
3026 * be an object (an XML QName, AttributeName, or AnyName), but only if we are
3027 * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a
3028 * string atom id.
3030 #define FETCH_ELEMENT_ID(obj, n, id) \
3031 JS_BEGIN_MACRO \
3032 const Value &idval_ = regs.sp[n]; \
3033 int32_t i_; \
3034 if (ValueFitsInInt32(idval_, &i_) && INT_FITS_IN_JSID(i_)) { \
3035 id = INT_TO_JSID(i_); \
3036 } else { \
3037 if (!js_InternNonIntElementId(cx, obj, idval_, &id, &regs.sp[n])) \
3038 goto error; \
3040 JS_END_MACRO
3042 #define TRY_BRANCH_AFTER_COND(cond,spdec) \
3043 JS_BEGIN_MACRO \
3044 JS_ASSERT(js_CodeSpec[op].length == 1); \
3045 uintN diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ; \
3046 if (diff_ <= 1) { \
3047 regs.sp -= spdec; \
3048 if (cond == (diff_ != 0)) { \
3049 ++regs.pc; \
3050 len = GET_JUMP_OFFSET(regs.pc); \
3051 BRANCH(len); \
3053 len = 1 + JSOP_IFEQ_LENGTH; \
3054 DO_NEXT_OP(len); \
3056 JS_END_MACRO
3058 BEGIN_CASE(JSOP_IN)
3060 const Value &rref = regs.sp[-1];
3061 if (!rref.isObject()) {
3062 js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rref, NULL);
3063 goto error;
3065 JSObject *obj = &rref.toObject();
3066 jsid id;
3067 FETCH_ELEMENT_ID(obj, -2, id);
3068 JSObject *obj2;
3069 JSProperty *prop;
3070 if (!obj->lookupProperty(cx, id, &obj2, &prop))
3071 goto error;
3072 bool cond = prop != NULL;
3073 TRY_BRANCH_AFTER_COND(cond, 2);
3074 regs.sp--;
3075 regs.sp[-1].setBoolean(cond);
3077 END_CASE(JSOP_IN)
3079 BEGIN_CASE(JSOP_ITER)
3081 JS_ASSERT(regs.sp > regs.fp->base());
3082 uintN flags = regs.pc[1];
3083 if (!js_ValueToIterator(cx, flags, &regs.sp[-1]))
3084 goto error;
3085 CHECK_INTERRUPT_HANDLER();
3086 JS_ASSERT(!regs.sp[-1].isPrimitive());
3088 END_CASE(JSOP_ITER)
3090 BEGIN_CASE(JSOP_MOREITER)
3092 JS_ASSERT(regs.sp - 1 >= regs.fp->base());
3093 JS_ASSERT(regs.sp[-1].isObject());
3094 PUSH_NULL();
3095 bool cond;
3096 if (!IteratorMore(cx, &regs.sp[-2].toObject(), &cond, &regs.sp[-1]))
3097 goto error;
3098 CHECK_INTERRUPT_HANDLER();
3099 regs.sp[-1].setBoolean(cond);
3101 END_CASE(JSOP_MOREITER)
3103 BEGIN_CASE(JSOP_ENDITER)
3105 JS_ASSERT(regs.sp - 1 >= regs.fp->base());
3106 bool ok = !!js_CloseIterator(cx, &regs.sp[-1].toObject());
3107 regs.sp--;
3108 if (!ok)
3109 goto error;
3111 END_CASE(JSOP_ENDITER)
3113 BEGIN_CASE(JSOP_FORARG)
3115 JS_ASSERT(regs.sp - 1 >= regs.fp->base());
3116 uintN slot = GET_ARGNO(regs.pc);
3117 JS_ASSERT(slot < regs.fp->numFormalArgs());
3118 JS_ASSERT(regs.sp[-1].isObject());
3119 if (!IteratorNext(cx, &regs.sp[-1].toObject(), &argv[slot]))
3120 goto error;
3122 END_CASE(JSOP_FORARG)
3124 BEGIN_CASE(JSOP_FORLOCAL)
3126 JS_ASSERT(regs.sp - 1 >= regs.fp->base());
3127 uintN slot = GET_SLOTNO(regs.pc);
3128 JS_ASSERT(slot < regs.fp->numSlots());
3129 JS_ASSERT(regs.sp[-1].isObject());
3130 if (!IteratorNext(cx, &regs.sp[-1].toObject(), &regs.fp->slots()[slot]))
3131 goto error;
3133 END_CASE(JSOP_FORLOCAL)
3135 BEGIN_CASE(JSOP_FORNAME)
3136 BEGIN_CASE(JSOP_FORGNAME)
3138 JS_ASSERT(regs.sp - 1 >= regs.fp->base());
3139 JSAtom *atom;
3140 LOAD_ATOM(0, atom);
3141 jsid id = ATOM_TO_JSID(atom);
3142 JSObject *obj, *obj2;
3143 JSProperty *prop;
3144 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
3145 goto error;
3148 AutoValueRooter tvr(cx);
3149 JS_ASSERT(regs.sp[-1].isObject());
3150 if (!IteratorNext(cx, &regs.sp[-1].toObject(), tvr.addr()))
3151 goto error;
3152 if (!obj->setProperty(cx, id, tvr.addr(), script->strictModeCode))
3153 goto error;
3156 END_CASE(JSOP_FORNAME)
3158 BEGIN_CASE(JSOP_FORPROP)
3160 JS_ASSERT(regs.sp - 2 >= regs.fp->base());
3161 JSAtom *atom;
3162 LOAD_ATOM(0, atom);
3163 jsid id = ATOM_TO_JSID(atom);
3164 JSObject *obj;
3165 FETCH_OBJECT(cx, -1, obj);
3167 AutoValueRooter tvr(cx);
3168 JS_ASSERT(regs.sp[-2].isObject());
3169 if (!IteratorNext(cx, &regs.sp[-2].toObject(), tvr.addr()))
3170 goto error;
3171 if (!obj->setProperty(cx, id, tvr.addr(), script->strictModeCode))
3172 goto error;
3174 regs.sp--;
3176 END_CASE(JSOP_FORPROP)
3178 BEGIN_CASE(JSOP_FORELEM)
3180 * JSOP_FORELEM simply dups the property identifier at top of stack and
3181 * lets the subsequent JSOP_ENUMELEM opcode sequence handle the left-hand
3182 * side expression evaluation and assignment. This opcode exists solely to
3183 * help the decompiler.
3185 JS_ASSERT(regs.sp - 1 >= regs.fp->base());
3186 JS_ASSERT(regs.sp[-1].isObject());
3187 PUSH_NULL();
3188 if (!IteratorNext(cx, &regs.sp[-2].toObject(), &regs.sp[-1]))
3189 goto error;
3190 END_CASE(JSOP_FORELEM)
3192 BEGIN_CASE(JSOP_DUP)
3194 JS_ASSERT(regs.sp > regs.fp->base());
3195 const Value &rref = regs.sp[-1];
3196 PUSH_COPY(rref);
3198 END_CASE(JSOP_DUP)
3200 BEGIN_CASE(JSOP_DUP2)
3202 JS_ASSERT(regs.sp - 2 >= regs.fp->base());
3203 const Value &lref = regs.sp[-2];
3204 const Value &rref = regs.sp[-1];
3205 PUSH_COPY(lref);
3206 PUSH_COPY(rref);
3208 END_CASE(JSOP_DUP2)
3210 BEGIN_CASE(JSOP_SWAP)
3212 JS_ASSERT(regs.sp - 2 >= regs.fp->base());
3213 Value &lref = regs.sp[-2];
3214 Value &rref = regs.sp[-1];
3215 lref.swap(rref);
3217 END_CASE(JSOP_SWAP)
3219 BEGIN_CASE(JSOP_PICK)
3221 jsint i = regs.pc[1];
3222 JS_ASSERT(regs.sp - (i+1) >= regs.fp->base());
3223 Value lval = regs.sp[-(i+1)];
3224 memmove(regs.sp - (i+1), regs.sp - i, sizeof(Value)*i);
3225 regs.sp[-1] = lval;
3227 END_CASE(JSOP_PICK)
3229 #define NATIVE_GET(cx,obj,pobj,shape,getHow,vp) \
3230 JS_BEGIN_MACRO \
3231 if (shape->isDataDescriptor() && shape->hasDefaultGetter()) { \
3232 /* Fast path for Object instance properties. */ \
3233 JS_ASSERT((shape)->slot != SHAPE_INVALID_SLOT || \
3234 !shape->hasDefaultSetter()); \
3235 if (((shape)->slot != SHAPE_INVALID_SLOT)) \
3236 *(vp) = (pobj)->nativeGetSlot((shape)->slot); \
3237 else \
3238 (vp)->setUndefined(); \
3239 } else { \
3240 if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp)) \
3241 goto error; \
3243 JS_END_MACRO
3245 #define NATIVE_SET(cx,obj,shape,entry,strict,vp) \
3246 JS_BEGIN_MACRO \
3247 if (shape->hasDefaultSetter() && \
3248 (shape)->slot != SHAPE_INVALID_SLOT && \
3249 !(obj)->brandedOrHasMethodBarrier()) { \
3250 /* Fast path for, e.g., plain Object instance properties. */ \
3251 (obj)->nativeSetSlot((shape)->slot, *vp); \
3252 } else { \
3253 if (!js_NativeSet(cx, obj, shape, false, strict, vp)) \
3254 goto error; \
3256 JS_END_MACRO
3259 * Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is
3260 * the constant length of the SET opcode sequence, and spdec is the constant
3261 * by which to decrease the stack pointer to pop all of the SET op's operands.
3263 * NB: unlike macros that could conceivably be replaced by functions (ignoring
3264 * goto error), where a call should not have to be braced in order to expand
3265 * correctly (e.g., in if (cond) FOO(); else BAR()), these three macros lack
3266 * JS_{BEGIN,END}_MACRO brackets. They are also indented so as to align with
3267 * nearby opcode code.
3269 #define SKIP_POP_AFTER_SET(oplen,spdec) \
3270 if (regs.pc[oplen] == JSOP_POP) { \
3271 regs.sp -= spdec; \
3272 regs.pc += oplen + JSOP_POP_LENGTH; \
3273 op = (JSOp) *regs.pc; \
3274 DO_OP(); \
3277 #define END_SET_CASE(OP) \
3278 SKIP_POP_AFTER_SET(OP##_LENGTH, 1); \
3279 END_CASE(OP)
3281 #define END_SET_CASE_STORE_RVAL(OP,spdec) \
3282 SKIP_POP_AFTER_SET(OP##_LENGTH, spdec); \
3284 Value *newsp = regs.sp - ((spdec) - 1); \
3285 newsp[-1] = regs.sp[-1]; \
3286 regs.sp = newsp; \
3288 END_CASE(OP)
3290 BEGIN_CASE(JSOP_SETCONST)
3292 JSAtom *atom;
3293 LOAD_ATOM(0, atom);
3294 JSObject &obj = regs.fp->varobj(cx);
3295 const Value &ref = regs.sp[-1];
3296 if (!obj.defineProperty(cx, ATOM_TO_JSID(atom), ref,
3297 PropertyStub, StrictPropertyStub,
3298 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) {
3299 goto error;
3302 END_SET_CASE(JSOP_SETCONST);
3304 #if JS_HAS_DESTRUCTURING
3305 BEGIN_CASE(JSOP_ENUMCONSTELEM)
3307 const Value &ref = regs.sp[-3];
3308 JSObject *obj;
3309 FETCH_OBJECT(cx, -2, obj);
3310 jsid id;
3311 FETCH_ELEMENT_ID(obj, -1, id);
3312 if (!obj->defineProperty(cx, id, ref,
3313 PropertyStub, StrictPropertyStub,
3314 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) {
3315 goto error;
3317 regs.sp -= 3;
3319 END_CASE(JSOP_ENUMCONSTELEM)
3320 #endif
3322 BEGIN_CASE(JSOP_BINDGNAME)
3323 PUSH_OBJECT(*regs.fp->scopeChain().getGlobal());
3324 END_CASE(JSOP_BINDGNAME)
3326 BEGIN_CASE(JSOP_BINDNAME)
3328 JSObject *obj;
3329 do {
3331 * We can skip the property lookup for the global object. If the
3332 * property does not exist anywhere on the scope chain, JSOP_SETNAME
3333 * adds the property to the global.
3335 * As a consequence of this optimization for the global object we run
3336 * its JSRESOLVE_ASSIGNING-tolerant resolve hooks only in JSOP_SETNAME,
3337 * after the interpreter evaluates the right- hand-side of the
3338 * assignment, and not here.
3340 * This should be transparent to the hooks because the script, instead
3341 * of name = rhs, could have used global.name = rhs given a global
3342 * object reference, which also calls the hooks only after evaluating
3343 * the rhs. We desire such resolve hook equivalence between the two
3344 * forms.
3346 obj = &regs.fp->scopeChain();
3347 if (!obj->getParent())
3348 break;
3350 PropertyCacheEntry *entry;
3351 JSObject *obj2;
3352 JSAtom *atom;
3353 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
3354 if (!atom) {
3355 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
3356 break;
3359 jsid id = ATOM_TO_JSID(atom);
3360 obj = js_FindIdentifierBase(cx, &regs.fp->scopeChain(), id);
3361 if (!obj)
3362 goto error;
3363 } while (0);
3364 PUSH_OBJECT(*obj);
3366 END_CASE(JSOP_BINDNAME)
3368 BEGIN_CASE(JSOP_IMACOP)
3369 JS_ASSERT(JS_UPTRDIFF(regs.fp->imacropc(), script->code) < script->length);
3370 op = JSOp(*regs.fp->imacropc());
3371 DO_OP();
3373 #define BITWISE_OP(OP) \
3374 JS_BEGIN_MACRO \
3375 int32_t i, j; \
3376 if (!ValueToECMAInt32(cx, regs.sp[-2], &i)) \
3377 goto error; \
3378 if (!ValueToECMAInt32(cx, regs.sp[-1], &j)) \
3379 goto error; \
3380 i = i OP j; \
3381 regs.sp--; \
3382 regs.sp[-1].setInt32(i); \
3383 JS_END_MACRO
3385 BEGIN_CASE(JSOP_BITOR)
3386 BITWISE_OP(|);
3387 END_CASE(JSOP_BITOR)
3389 BEGIN_CASE(JSOP_BITXOR)
3390 BITWISE_OP(^);
3391 END_CASE(JSOP_BITXOR)
3393 BEGIN_CASE(JSOP_BITAND)
3394 BITWISE_OP(&);
3395 END_CASE(JSOP_BITAND)
3397 #undef BITWISE_OP
3400 * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies
3401 * because they begin if/else chains, so callers must not put semicolons after
3402 * the call expressions!
3404 #if JS_HAS_XML_SUPPORT
3405 #define XML_EQUALITY_OP(OP) \
3406 if ((lval.isObject() && lval.toObject().isXML()) || \
3407 (rval.isObject() && rval.toObject().isXML())) { \
3408 if (!js_TestXMLEquality(cx, lval, rval, &cond)) \
3409 goto error; \
3410 cond = cond OP JS_TRUE; \
3411 } else
3412 #else
3413 #define XML_EQUALITY_OP(OP) /* nothing */
3414 #endif
3416 #define EQUALITY_OP(OP, IFNAN) \
3417 JS_BEGIN_MACRO \
3418 JSBool cond; \
3419 Value rval = regs.sp[-1]; \
3420 Value lval = regs.sp[-2]; \
3421 XML_EQUALITY_OP(OP) \
3422 if (SameType(lval, rval)) { \
3423 if (lval.isString()) { \
3424 JSString *l = lval.toString(), *r = rval.toString(); \
3425 JSBool equal; \
3426 if (!EqualStrings(cx, l, r, &equal)) \
3427 goto error; \
3428 cond = equal OP JS_TRUE; \
3429 } else if (lval.isDouble()) { \
3430 double l = lval.toDouble(), r = rval.toDouble(); \
3431 cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN); \
3432 } else if (lval.isObject()) { \
3433 JSObject *l = &lval.toObject(), *r = &rval.toObject(); \
3434 l->assertSpecialEqualitySynced(); \
3435 if (EqualityOp eq = l->getClass()->ext.equality) { \
3436 if (!eq(cx, l, &rval, &cond)) \
3437 goto error; \
3438 cond = cond OP JS_TRUE; \
3439 } else { \
3440 cond = l OP r; \
3442 } else { \
3443 cond = lval.payloadAsRawUint32() OP rval.payloadAsRawUint32();\
3445 } else { \
3446 if (lval.isNullOrUndefined()) { \
3447 cond = rval.isNullOrUndefined() OP true; \
3448 } else if (rval.isNullOrUndefined()) { \
3449 cond = true OP false; \
3450 } else { \
3451 if (lval.isObject()) \
3452 DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval); \
3453 if (rval.isObject()) \
3454 DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); \
3455 if (lval.isString() && rval.isString()) { \
3456 JSString *l = lval.toString(), *r = rval.toString(); \
3457 JSBool equal; \
3458 if (!EqualStrings(cx, l, r, &equal)) \
3459 goto error; \
3460 cond = equal OP JS_TRUE; \
3461 } else { \
3462 double l, r; \
3463 if (!ValueToNumber(cx, lval, &l) || \
3464 !ValueToNumber(cx, rval, &r)) { \
3465 goto error; \
3467 cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN); \
3471 TRY_BRANCH_AFTER_COND(cond, 2); \
3472 regs.sp--; \
3473 regs.sp[-1].setBoolean(cond); \
3474 JS_END_MACRO
3476 BEGIN_CASE(JSOP_EQ)
3477 EQUALITY_OP(==, false);
3478 END_CASE(JSOP_EQ)
3480 BEGIN_CASE(JSOP_NE)
3481 EQUALITY_OP(!=, true);
3482 END_CASE(JSOP_NE)
3484 #undef EQUALITY_OP
3485 #undef XML_EQUALITY_OP
3486 #undef EXTENDED_EQUALITY_OP
3488 #define STRICT_EQUALITY_OP(OP, COND) \
3489 JS_BEGIN_MACRO \
3490 const Value &rref = regs.sp[-1]; \
3491 const Value &lref = regs.sp[-2]; \
3492 JSBool equal; \
3493 if (!StrictlyEqual(cx, lref, rref, &equal)) \
3494 goto error; \
3495 COND = equal OP JS_TRUE; \
3496 regs.sp--; \
3497 JS_END_MACRO
3499 BEGIN_CASE(JSOP_STRICTEQ)
3501 bool cond;
3502 STRICT_EQUALITY_OP(==, cond);
3503 regs.sp[-1].setBoolean(cond);
3505 END_CASE(JSOP_STRICTEQ)
3507 BEGIN_CASE(JSOP_STRICTNE)
3509 bool cond;
3510 STRICT_EQUALITY_OP(!=, cond);
3511 regs.sp[-1].setBoolean(cond);
3513 END_CASE(JSOP_STRICTNE)
3515 BEGIN_CASE(JSOP_CASE)
3517 bool cond;
3518 STRICT_EQUALITY_OP(==, cond);
3519 if (cond) {
3520 regs.sp--;
3521 len = GET_JUMP_OFFSET(regs.pc);
3522 BRANCH(len);
3525 END_CASE(JSOP_CASE)
3527 BEGIN_CASE(JSOP_CASEX)
3529 bool cond;
3530 STRICT_EQUALITY_OP(==, cond);
3531 if (cond) {
3532 regs.sp--;
3533 len = GET_JUMPX_OFFSET(regs.pc);
3534 BRANCH(len);
3537 END_CASE(JSOP_CASEX)
3539 #undef STRICT_EQUALITY_OP
3541 #define RELATIONAL_OP(OP) \
3542 JS_BEGIN_MACRO \
3543 Value rval = regs.sp[-1]; \
3544 Value lval = regs.sp[-2]; \
3545 bool cond; \
3546 /* Optimize for two int-tagged operands (typical loop control). */ \
3547 if (lval.isInt32() && rval.isInt32()) { \
3548 cond = lval.toInt32() OP rval.toInt32(); \
3549 } else { \
3550 if (lval.isObject()) \
3551 DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \
3552 if (rval.isObject()) \
3553 DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \
3554 if (lval.isString() && rval.isString()) { \
3555 JSString *l = lval.toString(), *r = rval.toString(); \
3556 int32 result; \
3557 if (!CompareStrings(cx, l, r, &result)) \
3558 goto error; \
3559 cond = result OP 0; \
3560 } else { \
3561 double l, r; \
3562 if (!ValueToNumber(cx, lval, &l) || \
3563 !ValueToNumber(cx, rval, &r)) { \
3564 goto error; \
3566 cond = JSDOUBLE_COMPARE(l, OP, r, false); \
3569 TRY_BRANCH_AFTER_COND(cond, 2); \
3570 regs.sp--; \
3571 regs.sp[-1].setBoolean(cond); \
3572 JS_END_MACRO
3574 BEGIN_CASE(JSOP_LT)
3575 RELATIONAL_OP(<);
3576 END_CASE(JSOP_LT)
3578 BEGIN_CASE(JSOP_LE)
3579 RELATIONAL_OP(<=);
3580 END_CASE(JSOP_LE)
3582 BEGIN_CASE(JSOP_GT)
3583 RELATIONAL_OP(>);
3584 END_CASE(JSOP_GT)
3586 BEGIN_CASE(JSOP_GE)
3587 RELATIONAL_OP(>=);
3588 END_CASE(JSOP_GE)
3590 #undef RELATIONAL_OP
3592 #define SIGNED_SHIFT_OP(OP) \
3593 JS_BEGIN_MACRO \
3594 int32_t i, j; \
3595 if (!ValueToECMAInt32(cx, regs.sp[-2], &i)) \
3596 goto error; \
3597 if (!ValueToECMAInt32(cx, regs.sp[-1], &j)) \
3598 goto error; \
3599 i = i OP (j & 31); \
3600 regs.sp--; \
3601 regs.sp[-1].setInt32(i); \
3602 JS_END_MACRO
3604 BEGIN_CASE(JSOP_LSH)
3605 SIGNED_SHIFT_OP(<<);
3606 END_CASE(JSOP_LSH)
3608 BEGIN_CASE(JSOP_RSH)
3609 SIGNED_SHIFT_OP(>>);
3610 END_CASE(JSOP_RSH)
3612 #undef SIGNED_SHIFT_OP
3614 BEGIN_CASE(JSOP_URSH)
3616 uint32_t u;
3617 if (!ValueToECMAUint32(cx, regs.sp[-2], &u))
3618 goto error;
3619 int32_t j;
3620 if (!ValueToECMAInt32(cx, regs.sp[-1], &j))
3621 goto error;
3623 u >>= (j & 31);
3625 regs.sp--;
3626 regs.sp[-1].setNumber(uint32(u));
3628 END_CASE(JSOP_URSH)
3630 BEGIN_CASE(JSOP_ADD)
3632 Value rval = regs.sp[-1];
3633 Value lval = regs.sp[-2];
3635 if (lval.isInt32() && rval.isInt32()) {
3636 int32_t l = lval.toInt32(), r = rval.toInt32();
3637 int32_t sum = l + r;
3638 regs.sp--;
3639 if (JS_UNLIKELY(bool((l ^ sum) & (r ^ sum) & 0x80000000)))
3640 regs.sp[-1].setDouble(double(l) + double(r));
3641 else
3642 regs.sp[-1].setInt32(sum);
3643 } else
3644 #if JS_HAS_XML_SUPPORT
3645 if (IsXML(lval) && IsXML(rval)) {
3646 if (!js_ConcatenateXML(cx, &lval.toObject(), &rval.toObject(), &rval))
3647 goto error;
3648 regs.sp--;
3649 regs.sp[-1] = rval;
3650 } else
3651 #endif
3653 if (lval.isObject())
3654 DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval);
3655 if (rval.isObject())
3656 DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval);
3657 bool lIsString, rIsString;
3658 if ((lIsString = lval.isString()) | (rIsString = rval.isString())) {
3659 JSString *lstr, *rstr;
3660 if (lIsString) {
3661 lstr = lval.toString();
3662 } else {
3663 lstr = js_ValueToString(cx, lval);
3664 if (!lstr)
3665 goto error;
3666 regs.sp[-2].setString(lstr);
3668 if (rIsString) {
3669 rstr = rval.toString();
3670 } else {
3671 rstr = js_ValueToString(cx, rval);
3672 if (!rstr)
3673 goto error;
3674 regs.sp[-1].setString(rstr);
3676 JSString *str = js_ConcatStrings(cx, lstr, rstr);
3677 if (!str)
3678 goto error;
3679 regs.sp--;
3680 regs.sp[-1].setString(str);
3681 } else {
3682 double l, r;
3683 if (!ValueToNumber(cx, lval, &l) || !ValueToNumber(cx, rval, &r))
3684 goto error;
3685 l += r;
3686 regs.sp--;
3687 regs.sp[-1].setNumber(l);
3691 END_CASE(JSOP_ADD)
3693 #define BINARY_OP(OP) \
3694 JS_BEGIN_MACRO \
3695 double d1, d2; \
3696 if (!ValueToNumber(cx, regs.sp[-2], &d1) || \
3697 !ValueToNumber(cx, regs.sp[-1], &d2)) { \
3698 goto error; \
3700 double d = d1 OP d2; \
3701 regs.sp--; \
3702 regs.sp[-1].setNumber(d); \
3703 JS_END_MACRO
3705 BEGIN_CASE(JSOP_SUB)
3706 BINARY_OP(-);
3707 END_CASE(JSOP_SUB)
3709 BEGIN_CASE(JSOP_MUL)
3710 BINARY_OP(*);
3711 END_CASE(JSOP_MUL)
3713 #undef BINARY_OP
3715 BEGIN_CASE(JSOP_DIV)
3717 double d1, d2;
3718 if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
3719 !ValueToNumber(cx, regs.sp[-1], &d2)) {
3720 goto error;
3722 regs.sp--;
3723 if (d2 == 0) {
3724 const Value *vp;
3725 #ifdef XP_WIN
3726 /* XXX MSVC miscompiles such that (NaN == 0) */
3727 if (JSDOUBLE_IS_NaN(d2))
3728 vp = &rt->NaNValue;
3729 else
3730 #endif
3731 if (d1 == 0 || JSDOUBLE_IS_NaN(d1))
3732 vp = &rt->NaNValue;
3733 else if (JSDOUBLE_IS_NEG(d1) != JSDOUBLE_IS_NEG(d2))
3734 vp = &rt->negativeInfinityValue;
3735 else
3736 vp = &rt->positiveInfinityValue;
3737 regs.sp[-1] = *vp;
3738 } else {
3739 d1 /= d2;
3740 regs.sp[-1].setNumber(d1);
3743 END_CASE(JSOP_DIV)
3745 BEGIN_CASE(JSOP_MOD)
3747 Value &lref = regs.sp[-2];
3748 Value &rref = regs.sp[-1];
3749 int32_t l, r;
3750 if (lref.isInt32() && rref.isInt32() &&
3751 (l = lref.toInt32()) >= 0 && (r = rref.toInt32()) > 0) {
3752 int32_t mod = l % r;
3753 regs.sp--;
3754 regs.sp[-1].setInt32(mod);
3755 } else {
3756 double d1, d2;
3757 if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
3758 !ValueToNumber(cx, regs.sp[-1], &d2)) {
3759 goto error;
3761 regs.sp--;
3762 if (d2 == 0) {
3763 regs.sp[-1].setDouble(js_NaN);
3764 } else {
3765 d1 = js_fmod(d1, d2);
3766 regs.sp[-1].setDouble(d1);
3770 END_CASE(JSOP_MOD)
3772 BEGIN_CASE(JSOP_NOT)
3774 Value *_;
3775 bool cond;
3776 POP_BOOLEAN(cx, _, cond);
3777 PUSH_BOOLEAN(!cond);
3779 END_CASE(JSOP_NOT)
3781 BEGIN_CASE(JSOP_BITNOT)
3783 int32_t i;
3784 if (!ValueToECMAInt32(cx, regs.sp[-1], &i))
3785 goto error;
3786 i = ~i;
3787 regs.sp[-1].setInt32(i);
3789 END_CASE(JSOP_BITNOT)
3791 BEGIN_CASE(JSOP_NEG)
3794 * When the operand is int jsval, INT32_FITS_IN_JSVAL(i) implies
3795 * INT32_FITS_IN_JSVAL(-i) unless i is 0 or INT32_MIN when the
3796 * results, -0.0 or INT32_MAX + 1, are jsdouble values.
3798 const Value &ref = regs.sp[-1];
3799 int32_t i;
3800 if (ref.isInt32() && (i = ref.toInt32()) != 0 && i != INT32_MIN) {
3801 i = -i;
3802 regs.sp[-1].setInt32(i);
3803 } else {
3804 double d;
3805 if (!ValueToNumber(cx, regs.sp[-1], &d))
3806 goto error;
3807 d = -d;
3808 regs.sp[-1].setDouble(d);
3811 END_CASE(JSOP_NEG)
3813 BEGIN_CASE(JSOP_POS)
3814 if (!ValueToNumber(cx, &regs.sp[-1]))
3815 goto error;
3816 END_CASE(JSOP_POS)
3818 BEGIN_CASE(JSOP_DELNAME)
3820 JSAtom *atom;
3821 LOAD_ATOM(0, atom);
3822 jsid id = ATOM_TO_JSID(atom);
3823 JSObject *obj, *obj2;
3824 JSProperty *prop;
3825 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
3826 goto error;
3828 /* Strict mode code should never contain JSOP_DELNAME opcodes. */
3829 JS_ASSERT(!script->strictModeCode);
3831 /* ECMA says to return true if name is undefined or inherited. */
3832 PUSH_BOOLEAN(true);
3833 if (prop) {
3834 if (!obj->deleteProperty(cx, id, &regs.sp[-1], false))
3835 goto error;
3838 END_CASE(JSOP_DELNAME)
3840 BEGIN_CASE(JSOP_DELPROP)
3842 JSAtom *atom;
3843 LOAD_ATOM(0, atom);
3844 jsid id = ATOM_TO_JSID(atom);
3846 JSObject *obj;
3847 FETCH_OBJECT(cx, -1, obj);
3849 Value rval;
3850 if (!obj->deleteProperty(cx, id, &rval, script->strictModeCode))
3851 goto error;
3853 regs.sp[-1] = rval;
3855 END_CASE(JSOP_DELPROP)
3857 BEGIN_CASE(JSOP_DELELEM)
3859 /* Fetch the left part and resolve it to a non-null object. */
3860 JSObject *obj;
3861 FETCH_OBJECT(cx, -2, obj);
3863 /* Fetch index and convert it to id suitable for use with obj. */
3864 jsid id;
3865 FETCH_ELEMENT_ID(obj, -1, id);
3867 /* Get or set the element. */
3868 if (!obj->deleteProperty(cx, id, &regs.sp[-2], script->strictModeCode))
3869 goto error;
3871 regs.sp--;
3873 END_CASE(JSOP_DELELEM)
3875 BEGIN_CASE(JSOP_TYPEOFEXPR)
3876 BEGIN_CASE(JSOP_TYPEOF)
3878 const Value &ref = regs.sp[-1];
3879 JSType type = JS_TypeOfValue(cx, Jsvalify(ref));
3880 JSAtom *atom = rt->atomState.typeAtoms[type];
3881 regs.sp[-1].setString(ATOM_TO_STRING(atom));
3883 END_CASE(JSOP_TYPEOF)
3885 BEGIN_CASE(JSOP_VOID)
3886 regs.sp[-1].setUndefined();
3887 END_CASE(JSOP_VOID)
3890 JSObject *obj;
3891 JSAtom *atom;
3892 jsid id;
3893 jsint i;
3895 BEGIN_CASE(JSOP_INCELEM)
3896 BEGIN_CASE(JSOP_DECELEM)
3897 BEGIN_CASE(JSOP_ELEMINC)
3898 BEGIN_CASE(JSOP_ELEMDEC)
3901 * Delay fetching of id until we have the object to ensure the proper
3902 * evaluation order. See bug 372331.
3904 id = JSID_VOID;
3905 i = -2;
3906 goto fetch_incop_obj;
3908 BEGIN_CASE(JSOP_INCPROP)
3909 BEGIN_CASE(JSOP_DECPROP)
3910 BEGIN_CASE(JSOP_PROPINC)
3911 BEGIN_CASE(JSOP_PROPDEC)
3912 LOAD_ATOM(0, atom);
3913 id = ATOM_TO_JSID(atom);
3914 i = -1;
3916 fetch_incop_obj:
3917 FETCH_OBJECT(cx, i, obj);
3918 if (JSID_IS_VOID(id))
3919 FETCH_ELEMENT_ID(obj, -1, id);
3920 goto do_incop;
3922 BEGIN_CASE(JSOP_INCNAME)
3923 BEGIN_CASE(JSOP_DECNAME)
3924 BEGIN_CASE(JSOP_NAMEINC)
3925 BEGIN_CASE(JSOP_NAMEDEC)
3926 BEGIN_CASE(JSOP_INCGNAME)
3927 BEGIN_CASE(JSOP_DECGNAME)
3928 BEGIN_CASE(JSOP_GNAMEINC)
3929 BEGIN_CASE(JSOP_GNAMEDEC)
3931 obj = &regs.fp->scopeChain();
3932 if (js_CodeSpec[op].format & JOF_GNAME)
3933 obj = obj->getGlobal();
3935 JSObject *obj2;
3936 PropertyCacheEntry *entry;
3937 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
3938 if (!atom) {
3939 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
3940 if (obj == obj2 && entry->vword.isSlot()) {
3941 uint32 slot = entry->vword.toSlot();
3942 Value &rref = obj->nativeGetSlotRef(slot);
3943 int32_t tmp;
3944 if (JS_LIKELY(rref.isInt32() && CanIncDecWithoutOverflow(tmp = rref.toInt32()))) {
3945 int32_t inc = tmp + ((js_CodeSpec[op].format & JOF_INC) ? 1 : -1);
3946 if (!(js_CodeSpec[op].format & JOF_POST))
3947 tmp = inc;
3948 rref.getInt32Ref() = inc;
3949 PUSH_INT32(tmp);
3950 len = JSOP_INCNAME_LENGTH;
3951 DO_NEXT_OP(len);
3954 LOAD_ATOM(0, atom);
3957 id = ATOM_TO_JSID(atom);
3958 JSProperty *prop;
3959 if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
3960 goto error;
3961 if (!prop) {
3962 atomNotDefined = atom;
3963 goto atom_not_defined;
3967 do_incop:
3970 * We need a root to store the value to leave on the stack until
3971 * we have done with obj->setProperty.
3973 PUSH_NULL();
3974 if (!obj->getProperty(cx, id, &regs.sp[-1]))
3975 goto error;
3977 const JSCodeSpec *cs = &js_CodeSpec[op];
3978 JS_ASSERT(cs->ndefs == 1);
3979 JS_ASSERT((cs->format & JOF_TMPSLOT_MASK) >= JOF_TMPSLOT2);
3980 Value &ref = regs.sp[-1];
3981 int32_t tmp;
3982 if (JS_LIKELY(ref.isInt32() && CanIncDecWithoutOverflow(tmp = ref.toInt32()))) {
3983 int incr = (cs->format & JOF_INC) ? 1 : -1;
3984 if (cs->format & JOF_POST)
3985 ref.getInt32Ref() = tmp + incr;
3986 else
3987 ref.getInt32Ref() = tmp += incr;
3988 regs.fp->setAssigning();
3989 JSBool ok = obj->setProperty(cx, id, &ref, script->strictModeCode);
3990 regs.fp->clearAssigning();
3991 if (!ok)
3992 goto error;
3995 * We must set regs.sp[-1] to tmp for both post and pre increments
3996 * as the setter overwrites regs.sp[-1].
3998 ref.setInt32(tmp);
3999 } else {
4000 /* We need an extra root for the result. */
4001 PUSH_NULL();
4002 if (!js_DoIncDec(cx, cs, &regs.sp[-2], &regs.sp[-1]))
4003 goto error;
4004 regs.fp->setAssigning();
4005 JSBool ok = obj->setProperty(cx, id, &regs.sp[-1], script->strictModeCode);
4006 regs.fp->clearAssigning();
4007 if (!ok)
4008 goto error;
4009 regs.sp--;
4012 if (cs->nuses == 0) {
4013 /* regs.sp[-1] already contains the result of name increment. */
4014 } else {
4015 regs.sp[-1 - cs->nuses] = regs.sp[-1];
4016 regs.sp -= cs->nuses;
4018 len = cs->length;
4019 DO_NEXT_OP(len);
4024 int incr, incr2;
4025 uint32 slot;
4026 Value *vp;
4028 /* Position cases so the most frequent i++ does not need a jump. */
4029 BEGIN_CASE(JSOP_DECARG)
4030 incr = -1; incr2 = -1; goto do_arg_incop;
4031 BEGIN_CASE(JSOP_ARGDEC)
4032 incr = -1; incr2 = 0; goto do_arg_incop;
4033 BEGIN_CASE(JSOP_INCARG)
4034 incr = 1; incr2 = 1; goto do_arg_incop;
4035 BEGIN_CASE(JSOP_ARGINC)
4036 incr = 1; incr2 = 0;
4038 do_arg_incop:
4039 slot = GET_ARGNO(regs.pc);
4040 JS_ASSERT(slot < regs.fp->numFormalArgs());
4041 METER_SLOT_OP(op, slot);
4042 vp = argv + slot;
4043 goto do_int_fast_incop;
4045 BEGIN_CASE(JSOP_DECLOCAL)
4046 incr = -1; incr2 = -1; goto do_local_incop;
4047 BEGIN_CASE(JSOP_LOCALDEC)
4048 incr = -1; incr2 = 0; goto do_local_incop;
4049 BEGIN_CASE(JSOP_INCLOCAL)
4050 incr = 1; incr2 = 1; goto do_local_incop;
4051 BEGIN_CASE(JSOP_LOCALINC)
4052 incr = 1; incr2 = 0;
4055 * do_local_incop comes right before do_int_fast_incop as we want to
4056 * avoid an extra jump for variable cases as local++ is more frequent
4057 * than arg++.
4059 do_local_incop:
4060 slot = GET_SLOTNO(regs.pc);
4061 JS_ASSERT(slot < regs.fp->numSlots());
4062 METER_SLOT_OP(op, slot);
4063 vp = regs.fp->slots() + slot;
4065 do_int_fast_incop:
4066 int32_t tmp;
4067 if (JS_LIKELY(vp->isInt32() && CanIncDecWithoutOverflow(tmp = vp->toInt32()))) {
4068 vp->getInt32Ref() = tmp + incr;
4069 JS_ASSERT(JSOP_INCARG_LENGTH == js_CodeSpec[op].length);
4070 SKIP_POP_AFTER_SET(JSOP_INCARG_LENGTH, 0);
4071 PUSH_INT32(tmp + incr2);
4072 } else {
4073 PUSH_COPY(*vp);
4074 if (!js_DoIncDec(cx, &js_CodeSpec[op], &regs.sp[-1], vp))
4075 goto error;
4077 len = JSOP_INCARG_LENGTH;
4078 JS_ASSERT(len == js_CodeSpec[op].length);
4079 DO_NEXT_OP(len);
4082 BEGIN_CASE(JSOP_THIS)
4083 if (!regs.fp->computeThis(cx))
4084 goto error;
4085 PUSH_COPY(regs.fp->thisValue());
4086 END_CASE(JSOP_THIS)
4088 BEGIN_CASE(JSOP_UNBRANDTHIS)
4090 if (!regs.fp->computeThis(cx))
4091 goto error;
4092 Value &thisv = regs.fp->thisValue();
4093 if (thisv.isObject()) {
4094 JSObject *obj = &thisv.toObject();
4095 if (obj->isNative())
4096 obj->unbrand(cx);
4099 END_CASE(JSOP_UNBRANDTHIS)
4102 JSObject *obj;
4103 Value *vp;
4104 jsint i;
4106 BEGIN_CASE(JSOP_GETTHISPROP)
4107 if (!regs.fp->computeThis(cx))
4108 goto error;
4109 i = 0;
4110 PUSH_COPY(regs.fp->thisValue());
4111 goto do_getprop_body;
4113 BEGIN_CASE(JSOP_GETARGPROP)
4115 i = ARGNO_LEN;
4116 uint32 slot = GET_ARGNO(regs.pc);
4117 JS_ASSERT(slot < regs.fp->numFormalArgs());
4118 PUSH_COPY(argv[slot]);
4119 goto do_getprop_body;
4122 BEGIN_CASE(JSOP_GETLOCALPROP)
4124 i = SLOTNO_LEN;
4125 uint32 slot = GET_SLOTNO(regs.pc);
4126 JS_ASSERT(slot < script->nslots);
4127 PUSH_COPY(regs.fp->slots()[slot]);
4128 goto do_getprop_body;
4131 BEGIN_CASE(JSOP_GETPROP)
4132 BEGIN_CASE(JSOP_GETXPROP)
4133 i = 0;
4135 do_getprop_body:
4136 vp = &regs.sp[-1];
4138 do_getprop_with_lval:
4139 VALUE_TO_OBJECT(cx, vp, obj);
4142 Value rval;
4143 do {
4145 * We do not impose the method read barrier if in an imacro,
4146 * assuming any property gets it does (e.g., for 'toString'
4147 * from JSOP_NEW) will not be leaked to the calling script.
4149 JSObject *aobj = js_GetProtoIfDenseArray(obj);
4151 PropertyCacheEntry *entry;
4152 JSObject *obj2;
4153 JSAtom *atom;
4154 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
4155 if (!atom) {
4156 ASSERT_VALID_PROPERTY_CACHE_HIT(i, aobj, obj2, entry);
4157 if (entry->vword.isFunObj()) {
4158 rval.setObject(entry->vword.toFunObj());
4159 } else if (entry->vword.isSlot()) {
4160 uint32 slot = entry->vword.toSlot();
4161 rval = obj2->nativeGetSlot(slot);
4162 } else {
4163 JS_ASSERT(entry->vword.isShape());
4164 const Shape *shape = entry->vword.toShape();
4165 NATIVE_GET(cx, obj, obj2, shape,
4166 regs.fp->hasImacropc() ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER,
4167 &rval);
4169 break;
4172 jsid id = ATOM_TO_JSID(atom);
4173 if (JS_LIKELY(!aobj->getOps()->getProperty)
4174 ? !js_GetPropertyHelper(cx, obj, id,
4175 (regs.fp->hasImacropc() ||
4176 regs.pc[JSOP_GETPROP_LENGTH + i] == JSOP_IFEQ)
4177 ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
4178 : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER,
4179 &rval)
4180 : !obj->getProperty(cx, id, &rval)) {
4181 goto error;
4183 } while (0);
4185 regs.sp[-1] = rval;
4186 assertSameCompartment(cx, regs.sp[-1]);
4187 JS_ASSERT(JSOP_GETPROP_LENGTH + i == js_CodeSpec[op].length);
4188 len = JSOP_GETPROP_LENGTH + i;
4190 END_VARLEN_CASE
4192 BEGIN_CASE(JSOP_LENGTH)
4193 vp = &regs.sp[-1];
4194 if (vp->isString()) {
4195 vp->setInt32(vp->toString()->length());
4196 } else if (vp->isObject()) {
4197 JSObject *obj = &vp->toObject();
4198 if (obj->isArray()) {
4199 jsuint length = obj->getArrayLength();
4200 regs.sp[-1].setNumber(length);
4201 } else if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
4202 uint32 length = obj->getArgsInitialLength();
4203 JS_ASSERT(length < INT32_MAX);
4204 regs.sp[-1].setInt32(int32_t(length));
4205 } else {
4206 i = -2;
4207 goto do_getprop_with_lval;
4209 } else {
4210 i = -2;
4211 goto do_getprop_with_lval;
4213 END_CASE(JSOP_LENGTH)
4217 BEGIN_CASE(JSOP_CALLPROP)
4219 Value lval = regs.sp[-1];
4221 Value objv;
4222 if (lval.isObject()) {
4223 objv = lval;
4224 } else {
4225 JSProtoKey protoKey;
4226 if (lval.isString()) {
4227 protoKey = JSProto_String;
4228 } else if (lval.isNumber()) {
4229 protoKey = JSProto_Number;
4230 } else if (lval.isBoolean()) {
4231 protoKey = JSProto_Boolean;
4232 } else {
4233 JS_ASSERT(lval.isNull() || lval.isUndefined());
4234 js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
4235 goto error;
4237 JSObject *pobj;
4238 if (!js_GetClassPrototype(cx, NULL, protoKey, &pobj))
4239 goto error;
4240 objv.setObject(*pobj);
4243 JSObject *aobj = js_GetProtoIfDenseArray(&objv.toObject());
4244 Value rval;
4246 PropertyCacheEntry *entry;
4247 JSObject *obj2;
4248 JSAtom *atom;
4249 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
4250 if (!atom) {
4251 ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry);
4252 if (entry->vword.isFunObj()) {
4253 rval.setObject(entry->vword.toFunObj());
4254 } else if (entry->vword.isSlot()) {
4255 uint32 slot = entry->vword.toSlot();
4256 rval = obj2->nativeGetSlot(slot);
4257 } else {
4258 JS_ASSERT(entry->vword.isShape());
4259 const Shape *shape = entry->vword.toShape();
4260 NATIVE_GET(cx, &objv.toObject(), obj2, shape, JSGET_NO_METHOD_BARRIER, &rval);
4262 regs.sp[-1] = rval;
4263 assertSameCompartment(cx, regs.sp[-1]);
4264 PUSH_COPY(lval);
4265 } else {
4267 * Cache miss: use the immediate atom that was loaded for us under
4268 * PropertyCache::test.
4270 jsid id;
4271 id = ATOM_TO_JSID(atom);
4273 PUSH_NULL();
4274 if (lval.isObject()) {
4275 if (!js_GetMethod(cx, &objv.toObject(), id,
4276 JS_LIKELY(!aobj->getOps()->getProperty)
4277 ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
4278 : JSGET_NO_METHOD_BARRIER,
4279 &rval)) {
4280 goto error;
4282 regs.sp[-1] = objv;
4283 regs.sp[-2] = rval;
4284 assertSameCompartment(cx, regs.sp[-1], regs.sp[-2]);
4285 } else {
4286 JS_ASSERT(!objv.toObject().getOps()->getProperty);
4287 if (!js_GetPropertyHelper(cx, &objv.toObject(), id,
4288 JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER,
4289 &rval)) {
4290 goto error;
4292 regs.sp[-1] = lval;
4293 regs.sp[-2] = rval;
4294 assertSameCompartment(cx, regs.sp[-1], regs.sp[-2]);
4297 #if JS_HAS_NO_SUCH_METHOD
4298 if (JS_UNLIKELY(rval.isUndefined()) && regs.sp[-1].isObject()) {
4299 LOAD_ATOM(0, atom);
4300 regs.sp[-2].setString(ATOM_TO_STRING(atom));
4301 if (!js_OnUnknownMethod(cx, regs.sp - 2))
4302 goto error;
4304 #endif
4306 END_CASE(JSOP_CALLPROP)
4308 BEGIN_CASE(JSOP_UNBRAND)
4309 JS_ASSERT(regs.sp - regs.fp->slots() >= 1);
4310 regs.sp[-1].toObject().unbrand(cx);
4311 END_CASE(JSOP_UNBRAND)
4313 BEGIN_CASE(JSOP_SETGNAME)
4314 BEGIN_CASE(JSOP_SETNAME)
4315 BEGIN_CASE(JSOP_SETPROP)
4316 BEGIN_CASE(JSOP_SETMETHOD)
4318 Value rval = regs.sp[-1];
4319 JS_ASSERT_IF(op == JSOP_SETMETHOD, IsFunctionObject(rval));
4320 Value &lref = regs.sp[-2];
4321 JS_ASSERT_IF(op == JSOP_SETNAME, lref.isObject());
4322 JSObject *obj;
4323 VALUE_TO_OBJECT(cx, &lref, obj);
4325 JS_ASSERT_IF(op == JSOP_SETGNAME, obj == regs.fp->scopeChain().getGlobal());
4327 do {
4328 PropertyCache *cache = &JS_PROPERTY_CACHE(cx);
4331 * Probe the property cache, specializing for two important
4332 * set-property cases. First:
4334 * function f(a, b, c) {
4335 * var o = {p:a, q:b, r:c};
4336 * return o;
4339 * or similar real-world cases, which evolve a newborn native
4340 * object predicatably through some bounded number of property
4341 * additions. And second:
4343 * o.p = x;
4345 * in a frequently executed method or loop body, where p will
4346 * (possibly after the first iteration) always exist in native
4347 * object o.
4349 PropertyCacheEntry *entry;
4350 JSObject *obj2;
4351 JSAtom *atom;
4352 if (cache->testForSet(cx, regs.pc, obj, &entry, &obj2, &atom)) {
4354 * Property cache hit, only partially confirmed by testForSet. We
4355 * know that the entry applies to regs.pc and that obj's shape
4356 * matches.
4358 * The entry predicts either a new property to be added directly to
4359 * obj by this set, or on an existing "own" property, or on a
4360 * prototype property that has a setter.
4362 const Shape *shape = entry->vword.toShape();
4363 JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable());
4364 JS_ASSERT_IF(shape->hasSlot(), entry->vcapTag() == 0);
4367 * Fastest path: check whether obj already has the cached shape and
4368 * call NATIVE_SET and break to get out of the do-while(0). But we
4369 * can call NATIVE_SET only for a direct or proto-setter hit.
4371 if (!entry->adding()) {
4372 if (entry->vcapTag() == 0 ||
4373 ((obj2 = obj->getProto()) && obj2->shape() == entry->vshape()))
4375 #ifdef DEBUG
4376 if (entry->directHit()) {
4377 JS_ASSERT(obj->nativeContains(*shape));
4378 } else {
4379 JS_ASSERT(obj2->nativeContains(*shape));
4380 JS_ASSERT(entry->vcapTag() == 1);
4381 JS_ASSERT(entry->kshape != entry->vshape());
4382 JS_ASSERT(!shape->hasSlot());
4384 #endif
4386 PCMETER(cache->pchits++);
4387 PCMETER(cache->setpchits++);
4388 NATIVE_SET(cx, obj, shape, entry, script->strictModeCode, &rval);
4389 break;
4391 } else {
4392 JS_ASSERT(obj->isExtensible());
4394 if (obj->nativeEmpty()) {
4395 if (!obj->ensureClassReservedSlotsForEmptyObject(cx))
4396 goto error;
4399 uint32 slot;
4400 if (shape->previous() == obj->lastProperty() &&
4401 entry->vshape() == rt->protoHazardShape &&
4402 shape->hasDefaultSetter()) {
4403 slot = shape->slot;
4404 JS_ASSERT(slot == obj->slotSpan());
4407 * Fast path: adding a plain old property that was once at
4408 * the frontier of the property tree, whose slot is next to
4409 * claim among the already-allocated slots in obj, where
4410 * shape->table has not been created yet.
4412 PCMETER(cache->pchits++);
4413 PCMETER(cache->addpchits++);
4415 if (slot < obj->numSlots()) {
4416 JS_ASSERT(obj->getSlot(slot).isUndefined());
4417 } else {
4418 if (!obj->allocSlot(cx, &slot))
4419 goto error;
4420 JS_ASSERT(slot == shape->slot);
4423 /* Simply extend obj's property tree path with shape! */
4424 obj->extend(cx, shape);
4427 * No method change check here because here we are adding a
4428 * new property, not updating an existing slot's value that
4429 * might contain a method of a branded shape.
4431 TRACE_1(AddProperty, obj);
4432 obj->nativeSetSlot(slot, rval);
4435 * Purge the property cache of the id we may have just
4436 * shadowed in obj's scope and proto chains.
4438 js_PurgeScopeChain(cx, obj, shape->id);
4439 break;
4442 PCMETER(cache->setpcmisses++);
4444 LOAD_ATOM(0, atom);
4445 } else {
4446 JS_ASSERT(atom);
4449 jsid id = ATOM_TO_JSID(atom);
4450 if (entry && JS_LIKELY(!obj->getOps()->setProperty)) {
4451 uintN defineHow;
4452 if (op == JSOP_SETMETHOD)
4453 defineHow = JSDNP_CACHE_RESULT | JSDNP_SET_METHOD;
4454 else if (op == JSOP_SETNAME)
4455 defineHow = JSDNP_CACHE_RESULT | JSDNP_UNQUALIFIED;
4456 else
4457 defineHow = JSDNP_CACHE_RESULT;
4458 if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rval, script->strictModeCode))
4459 goto error;
4460 } else {
4461 if (!obj->setProperty(cx, id, &rval, script->strictModeCode))
4462 goto error;
4463 ABORT_RECORDING(cx, "Non-native set");
4465 } while (0);
4467 END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2);
4469 BEGIN_CASE(JSOP_GETELEM)
4471 Value &lref = regs.sp[-2];
4472 Value &rref = regs.sp[-1];
4473 if (lref.isString() && rref.isInt32()) {
4474 JSString *str = lref.toString();
4475 int32_t i = rref.toInt32();
4476 if (size_t(i) < str->length()) {
4477 str = JSString::getUnitString(cx, str, size_t(i));
4478 if (!str)
4479 goto error;
4480 regs.sp--;
4481 regs.sp[-1].setString(str);
4482 len = JSOP_GETELEM_LENGTH;
4483 DO_NEXT_OP(len);
4487 JSObject *obj;
4488 VALUE_TO_OBJECT(cx, &lref, obj);
4490 const Value *copyFrom;
4491 Value rval;
4492 jsid id;
4493 if (rref.isInt32()) {
4494 int32_t i = rref.toInt32();
4495 if (obj->isDenseArray()) {
4496 jsuint idx = jsuint(i);
4498 if (idx < obj->getDenseArrayCapacity()) {
4499 copyFrom = obj->addressOfDenseArrayElement(idx);
4500 if (!copyFrom->isMagic())
4501 goto end_getelem;
4503 } else if (obj->isArguments()) {
4504 uint32 arg = uint32(i);
4506 if (arg < obj->getArgsInitialLength()) {
4507 copyFrom = obj->addressOfArgsElement(arg);
4508 if (!copyFrom->isMagic(JS_ARGS_HOLE)) {
4509 if (JSStackFrame *afp = (JSStackFrame *) obj->getPrivate())
4510 copyFrom = &afp->canonicalActualArg(arg);
4511 goto end_getelem;
4515 if (JS_LIKELY(INT_FITS_IN_JSID(i)))
4516 id = INT_TO_JSID(i);
4517 else
4518 goto intern_big_int;
4519 } else {
4520 int32_t i;
4521 if (ValueFitsInInt32(rref, &i) && INT_FITS_IN_JSID(i)) {
4522 id = INT_TO_JSID(i);
4523 } else {
4524 intern_big_int:
4525 if (!js_InternNonIntElementId(cx, obj, rref, &id))
4526 goto error;
4530 if (!obj->getProperty(cx, id, &rval))
4531 goto error;
4532 copyFrom = &rval;
4534 end_getelem:
4535 regs.sp--;
4536 regs.sp[-1] = *copyFrom;
4537 assertSameCompartment(cx, regs.sp[-1]);
4539 END_CASE(JSOP_GETELEM)
4541 BEGIN_CASE(JSOP_CALLELEM)
4543 /* Find the object on which to look for |this|'s properties. */
4544 Value thisv = regs.sp[-2];
4545 JSObject *thisObj = ValuePropertyBearer(cx, thisv, -2);
4546 if (!thisObj)
4547 goto error;
4549 /* Fetch index and convert it to id suitable for use with obj. */
4550 jsid id;
4551 FETCH_ELEMENT_ID(thisObj, -1, id);
4553 /* Get the method. */
4554 if (!js_GetMethod(cx, thisObj, id, JSGET_NO_METHOD_BARRIER, &regs.sp[-2]))
4555 goto error;
4557 #if JS_HAS_NO_SUCH_METHOD
4558 if (JS_UNLIKELY(regs.sp[-2].isUndefined()) && thisv.isObject()) {
4559 /* For js_OnUnknownMethod, sp[-2] is the index, and sp[-1] is the object missing it. */
4560 regs.sp[-2] = regs.sp[-1];
4561 regs.sp[-1].setObject(*thisObj);
4562 if (!js_OnUnknownMethod(cx, regs.sp - 2))
4563 goto error;
4564 } else
4565 #endif
4567 regs.sp[-1] = thisv;
4570 END_CASE(JSOP_CALLELEM)
4572 BEGIN_CASE(JSOP_SETELEM)
4574 JSObject *obj;
4575 FETCH_OBJECT(cx, -3, obj);
4576 jsid id;
4577 FETCH_ELEMENT_ID(obj, -2, id);
4578 Value rval;
4579 do {
4580 if (obj->isDenseArray() && JSID_IS_INT(id)) {
4581 jsuint length = obj->getDenseArrayCapacity();
4582 jsint i = JSID_TO_INT(id);
4583 if ((jsuint)i < length) {
4584 if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
4585 if (js_PrototypeHasIndexedProperties(cx, obj))
4586 break;
4587 if ((jsuint)i >= obj->getArrayLength())
4588 obj->setArrayLength(i + 1);
4590 obj->setDenseArrayElement(i, regs.sp[-1]);
4591 goto end_setelem;
4594 } while (0);
4595 rval = regs.sp[-1];
4596 if (!obj->setProperty(cx, id, &rval, script->strictModeCode))
4597 goto error;
4598 end_setelem:;
4600 END_SET_CASE_STORE_RVAL(JSOP_SETELEM, 3)
4602 BEGIN_CASE(JSOP_ENUMELEM)
4604 /* Funky: the value to set is under the [obj, id] pair. */
4605 JSObject *obj;
4606 FETCH_OBJECT(cx, -2, obj);
4607 jsid id;
4608 FETCH_ELEMENT_ID(obj, -1, id);
4609 Value rval = regs.sp[-3];
4610 if (!obj->setProperty(cx, id, &rval, script->strictModeCode))
4611 goto error;
4612 regs.sp -= 3;
4614 END_CASE(JSOP_ENUMELEM)
4616 { // begin block around calling opcodes
4617 JSFunction *newfun;
4618 JSObject *callee;
4619 uint32 flags;
4620 uintN argc;
4621 Value *vp;
4623 BEGIN_CASE(JSOP_NEW)
4625 /* Get immediate argc and find the constructor function. */
4626 argc = GET_ARGC(regs.pc);
4627 vp = regs.sp - (2 + argc);
4628 JS_ASSERT(vp >= regs.fp->base());
4631 * Assign lval, callee, and newfun exactly as the code at inline_call: expects to
4632 * find them, to avoid nesting a js_Interpret call via js_InvokeConstructor.
4634 if (IsFunctionObject(vp[0], &callee)) {
4635 newfun = callee->getFunctionPrivate();
4636 if (newfun->isInterpreted()) {
4637 if (newfun->u.i.script->isEmpty()) {
4638 JSObject *obj2 = js_CreateThisForFunction(cx, callee);
4639 if (!obj2)
4640 goto error;
4641 vp[0].setObject(*obj2);
4642 regs.sp = vp + 1;
4643 goto end_new;
4646 flags = JSFRAME_CONSTRUCTING;
4647 goto inline_call;
4651 if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(vp, argc)))
4652 goto error;
4653 regs.sp = vp + 1;
4654 CHECK_INTERRUPT_HANDLER();
4655 TRACE_0(NativeCallComplete);
4657 end_new:;
4659 END_CASE(JSOP_NEW)
4661 BEGIN_CASE(JSOP_EVAL)
4663 argc = GET_ARGC(regs.pc);
4664 vp = regs.sp - (argc + 2);
4666 if (!IsFunctionObject(*vp, &callee))
4667 goto call_using_invoke;
4669 newfun = callee->getFunctionPrivate();
4670 if (!IsBuiltinEvalFunction(newfun))
4671 goto call_using_invoke;
4673 if (!DirectEval(cx, newfun, argc, vp))
4674 goto error;
4676 END_CASE(JSOP_EVAL)
4678 BEGIN_CASE(JSOP_CALL)
4679 BEGIN_CASE(JSOP_FUNAPPLY)
4680 BEGIN_CASE(JSOP_FUNCALL)
4682 argc = GET_ARGC(regs.pc);
4683 vp = regs.sp - (argc + 2);
4685 if (IsFunctionObject(*vp, &callee)) {
4686 newfun = callee->getFunctionPrivate();
4688 /* Clear frame flags since this is not a constructor call. */
4689 flags = 0;
4690 if (newfun->isInterpreted())
4691 inline_call:
4693 JSScript *newscript = newfun->script();
4694 if (JS_UNLIKELY(newscript->isEmpty())) {
4695 vp->setUndefined();
4696 regs.sp = vp + 1;
4697 goto end_call;
4700 /* Restrict recursion of lightweight functions. */
4701 if (JS_UNLIKELY(inlineCallCount >= JS_MAX_INLINE_CALL_COUNT)) {
4702 js_ReportOverRecursed(cx);
4703 goto error;
4706 /* Get pointer to new frame/slots, prepare arguments. */
4707 StackSpace &stack = cx->stack();
4708 JSStackFrame *newfp = stack.getInlineFrame(cx, regs.sp, argc, newfun,
4709 newscript, &flags);
4710 if (JS_UNLIKELY(!newfp))
4711 goto error;
4713 /* Initialize frame, locals. */
4714 newfp->initCallFrame(cx, *callee, newfun, argc, flags);
4715 SetValueRangeToUndefined(newfp->slots(), newscript->nfixed);
4717 /* Officially push the frame. */
4718 stack.pushInlineFrame(cx, newscript, newfp, &regs);
4720 /* Refresh interpreter locals. */
4721 JS_ASSERT(newfp == regs.fp);
4722 script = newscript;
4723 argv = regs.fp->formalArgsEnd() - newfun->nargs;
4724 atoms = script->atomMap.vector;
4726 /* Now that the new frame is rooted, maybe create a call object. */
4727 if (newfun->isHeavyweight() && !js_GetCallObject(cx, regs.fp))
4728 goto error;
4730 inlineCallCount++;
4731 JS_RUNTIME_METER(rt, inlineCalls);
4733 TRACE_0(EnterFrame);
4735 CHECK_INTERRUPT_HANDLER();
4737 #ifdef JS_METHODJIT
4738 /* Try to ensure methods are method JIT'd. */
4739 mjit::CompileRequest request = (interpMode == JSINTERP_NORMAL)
4740 ? mjit::CompileRequest_Interpreter
4741 : mjit::CompileRequest_JIT;
4742 mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp, request);
4743 if (status == mjit::Compile_Error)
4744 goto error;
4745 if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && status == mjit::Compile_Okay) {
4746 interpReturnOK = mjit::JaegerShot(cx);
4747 CHECK_INTERRUPT_HANDLER();
4748 goto jit_return;
4750 #endif
4752 if (!ScriptPrologue(cx, regs.fp))
4753 goto error;
4755 CHECK_INTERRUPT_HANDLER();
4757 /* Load first op and dispatch it (safe since JSOP_STOP). */
4758 op = (JSOp) *regs.pc;
4759 DO_OP();
4762 Probes::enterJSFun(cx, newfun, script);
4763 JSBool ok = CallJSNative(cx, newfun->u.n.native, argc, vp);
4764 Probes::exitJSFun(cx, newfun, script);
4765 regs.sp = vp + 1;
4766 if (!ok)
4767 goto error;
4768 TRACE_0(NativeCallComplete);
4769 goto end_call;
4772 call_using_invoke:
4773 bool ok;
4774 ok = Invoke(cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0);
4775 regs.sp = vp + 1;
4776 CHECK_INTERRUPT_HANDLER();
4777 if (!ok)
4778 goto error;
4779 JS_RUNTIME_METER(rt, nonInlineCalls);
4780 TRACE_0(NativeCallComplete);
4782 end_call:;
4784 END_CASE(JSOP_CALL)
4786 } // end block around calling opcodes
4788 BEGIN_CASE(JSOP_SETCALL)
4790 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_LEFTSIDE_OF_ASS);
4791 goto error;
4793 END_CASE(JSOP_SETCALL)
4795 #define SLOW_PUSH_THISV(cx, obj) \
4796 JS_BEGIN_MACRO \
4797 Class *clasp; \
4798 JSObject *thisp = obj; \
4799 if (!thisp->getParent() || \
4800 (clasp = thisp->getClass()) == &js_CallClass || \
4801 clasp == &js_BlockClass || \
4802 clasp == &js_DeclEnvClass) { \
4803 /* Push the ImplicitThisValue for the Environment Record */ \
4804 /* associated with obj. See ES5 sections 10.2.1.1.6 and */ \
4805 /* 10.2.1.2.6 (ImplicitThisValue) and section 11.2.3 */ \
4806 /* (Function Calls). */ \
4807 PUSH_UNDEFINED(); \
4808 } else { \
4809 thisp = thisp->thisObject(cx); \
4810 if (!thisp) \
4811 goto error; \
4812 PUSH_OBJECT(*thisp); \
4814 JS_END_MACRO
4816 BEGIN_CASE(JSOP_GETGNAME)
4817 BEGIN_CASE(JSOP_CALLGNAME)
4818 BEGIN_CASE(JSOP_NAME)
4819 BEGIN_CASE(JSOP_CALLNAME)
4821 JSObject *obj = &regs.fp->scopeChain();
4823 const Shape *shape;
4824 Value rval;
4826 PropertyCacheEntry *entry;
4827 JSObject *obj2;
4828 JSAtom *atom;
4829 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
4830 if (!atom) {
4831 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
4832 if (entry->vword.isFunObj()) {
4833 PUSH_OBJECT(entry->vword.toFunObj());
4834 } else if (entry->vword.isSlot()) {
4835 uintN slot = entry->vword.toSlot();
4836 PUSH_COPY(obj2->nativeGetSlot(slot));
4837 } else {
4838 JS_ASSERT(entry->vword.isShape());
4839 shape = entry->vword.toShape();
4840 NATIVE_GET(cx, obj, obj2, shape, JSGET_METHOD_BARRIER, &rval);
4841 PUSH_COPY(rval);
4845 * Push results, the same as below, but with a prop$ hit there
4846 * is no need to test for the unusual and uncacheable case where
4847 * the caller determines |this|.
4849 #if DEBUG
4850 Class *clasp;
4851 JS_ASSERT(!obj->getParent() ||
4852 (clasp = obj->getClass()) == &js_CallClass ||
4853 clasp == &js_BlockClass ||
4854 clasp == &js_DeclEnvClass);
4855 #endif
4856 if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME)
4857 PUSH_UNDEFINED();
4858 len = JSOP_NAME_LENGTH;
4859 DO_NEXT_OP(len);
4862 jsid id;
4863 id = ATOM_TO_JSID(atom);
4864 JSProperty *prop;
4865 if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
4866 goto error;
4867 if (!prop) {
4868 /* Kludge to allow (typeof foo == "undefined") tests. */
4869 JSOp op2 = js_GetOpcode(cx, script, regs.pc + JSOP_NAME_LENGTH);
4870 if (op2 == JSOP_TYPEOF) {
4871 PUSH_UNDEFINED();
4872 len = JSOP_NAME_LENGTH;
4873 DO_NEXT_OP(len);
4875 atomNotDefined = atom;
4876 goto atom_not_defined;
4879 /* Take the slow path if prop was not found in a native object. */
4880 if (!obj->isNative() || !obj2->isNative()) {
4881 if (!obj->getProperty(cx, id, &rval))
4882 goto error;
4883 } else {
4884 shape = (Shape *)prop;
4885 JSObject *normalized = obj;
4886 if (normalized->getClass() == &js_WithClass && !shape->hasDefaultGetter())
4887 normalized = js_UnwrapWithObject(cx, normalized);
4888 NATIVE_GET(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, &rval);
4891 PUSH_COPY(rval);
4893 /* obj must be on the scope chain, thus not a function. */
4894 if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME)
4895 SLOW_PUSH_THISV(cx, obj);
4897 END_CASE(JSOP_NAME)
4899 BEGIN_CASE(JSOP_UINT16)
4900 PUSH_INT32((int32_t) GET_UINT16(regs.pc));
4901 END_CASE(JSOP_UINT16)
4903 BEGIN_CASE(JSOP_UINT24)
4904 PUSH_INT32((int32_t) GET_UINT24(regs.pc));
4905 END_CASE(JSOP_UINT24)
4907 BEGIN_CASE(JSOP_INT8)
4908 PUSH_INT32(GET_INT8(regs.pc));
4909 END_CASE(JSOP_INT8)
4911 BEGIN_CASE(JSOP_INT32)
4912 PUSH_INT32(GET_INT32(regs.pc));
4913 END_CASE(JSOP_INT32)
4915 BEGIN_CASE(JSOP_INDEXBASE)
4917 * Here atoms can exceed script->atomMap.length as we use atoms as a
4918 * segment register for object literals as well.
4920 atoms += GET_INDEXBASE(regs.pc);
4921 END_CASE(JSOP_INDEXBASE)
4923 BEGIN_CASE(JSOP_INDEXBASE1)
4924 BEGIN_CASE(JSOP_INDEXBASE2)
4925 BEGIN_CASE(JSOP_INDEXBASE3)
4926 atoms += (op - JSOP_INDEXBASE1 + 1) << 16;
4927 END_CASE(JSOP_INDEXBASE3)
4929 BEGIN_CASE(JSOP_RESETBASE0)
4930 BEGIN_CASE(JSOP_RESETBASE)
4931 atoms = script->atomMap.vector;
4932 END_CASE(JSOP_RESETBASE)
4934 BEGIN_CASE(JSOP_DOUBLE)
4936 JS_ASSERT(!regs.fp->hasImacropc());
4937 double dbl;
4938 LOAD_DOUBLE(0, dbl);
4939 PUSH_DOUBLE(dbl);
4941 END_CASE(JSOP_DOUBLE)
4943 BEGIN_CASE(JSOP_STRING)
4945 JSAtom *atom;
4946 LOAD_ATOM(0, atom);
4947 PUSH_STRING(ATOM_TO_STRING(atom));
4949 END_CASE(JSOP_STRING)
4951 BEGIN_CASE(JSOP_OBJECT)
4953 JSObject *obj;
4954 LOAD_OBJECT(0, obj);
4955 PUSH_OBJECT(*obj);
4957 END_CASE(JSOP_OBJECT)
4959 BEGIN_CASE(JSOP_REGEXP)
4962 * Push a regexp object cloned from the regexp literal object mapped by the
4963 * bytecode at pc. ES5 finally fixed this bad old ES3 design flaw which was
4964 * flouted by many browser-based implementations.
4966 * We avoid the GetScopeChain call here and pass fp->scopeChain as
4967 * js_GetClassPrototype uses the latter only to locate the global.
4969 jsatomid index = GET_FULL_INDEX(0);
4970 JSObject *proto;
4971 if (!js_GetClassPrototype(cx, &regs.fp->scopeChain(), JSProto_RegExp, &proto))
4972 goto error;
4973 JS_ASSERT(proto);
4974 JSObject *obj = js_CloneRegExpObject(cx, script->getRegExp(index), proto);
4975 if (!obj)
4976 goto error;
4977 PUSH_OBJECT(*obj);
4979 END_CASE(JSOP_REGEXP)
4981 BEGIN_CASE(JSOP_ZERO)
4982 PUSH_INT32(0);
4983 END_CASE(JSOP_ZERO)
4985 BEGIN_CASE(JSOP_ONE)
4986 PUSH_INT32(1);
4987 END_CASE(JSOP_ONE)
4989 BEGIN_CASE(JSOP_NULL)
4990 PUSH_NULL();
4991 END_CASE(JSOP_NULL)
4993 BEGIN_CASE(JSOP_FALSE)
4994 PUSH_BOOLEAN(false);
4995 END_CASE(JSOP_FALSE)
4997 BEGIN_CASE(JSOP_TRUE)
4998 PUSH_BOOLEAN(true);
4999 END_CASE(JSOP_TRUE)
5002 BEGIN_CASE(JSOP_TABLESWITCH)
5004 jsbytecode *pc2 = regs.pc;
5005 len = GET_JUMP_OFFSET(pc2);
5008 * ECMAv2+ forbids conversion of discriminant, so we will skip to the
5009 * default case if the discriminant isn't already an int jsval. (This
5010 * opcode is emitted only for dense jsint-domain switches.)
5012 const Value &rref = *--regs.sp;
5013 int32_t i;
5014 if (rref.isInt32()) {
5015 i = rref.toInt32();
5016 } else {
5017 double d;
5018 /* Don't use JSDOUBLE_IS_INT32; treat -0 (double) as 0. */
5019 if (!rref.isDouble() || (d = rref.toDouble()) != (i = int32_t(rref.toDouble())))
5020 DO_NEXT_OP(len);
5023 pc2 += JUMP_OFFSET_LEN;
5024 jsint low = GET_JUMP_OFFSET(pc2);
5025 pc2 += JUMP_OFFSET_LEN;
5026 jsint high = GET_JUMP_OFFSET(pc2);
5028 i -= low;
5029 if ((jsuint)i < (jsuint)(high - low + 1)) {
5030 pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i;
5031 jsint off = (jsint) GET_JUMP_OFFSET(pc2);
5032 if (off)
5033 len = off;
5036 END_VARLEN_CASE
5040 BEGIN_CASE(JSOP_TABLESWITCHX)
5042 jsbytecode *pc2 = regs.pc;
5043 len = GET_JUMPX_OFFSET(pc2);
5046 * ECMAv2+ forbids conversion of discriminant, so we will skip to the
5047 * default case if the discriminant isn't already an int jsval. (This
5048 * opcode is emitted only for dense jsint-domain switches.)
5050 const Value &rref = *--regs.sp;
5051 int32_t i;
5052 if (rref.isInt32()) {
5053 i = rref.toInt32();
5054 } else if (rref.isDouble() && rref.toDouble() == 0) {
5055 /* Treat -0 (double) as 0. */
5056 i = 0;
5057 } else {
5058 DO_NEXT_OP(len);
5061 pc2 += JUMPX_OFFSET_LEN;
5062 jsint low = GET_JUMP_OFFSET(pc2);
5063 pc2 += JUMP_OFFSET_LEN;
5064 jsint high = GET_JUMP_OFFSET(pc2);
5066 i -= low;
5067 if ((jsuint)i < (jsuint)(high - low + 1)) {
5068 pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i;
5069 jsint off = (jsint) GET_JUMPX_OFFSET(pc2);
5070 if (off)
5071 len = off;
5074 END_VARLEN_CASE
5078 BEGIN_CASE(JSOP_LOOKUPSWITCHX)
5080 jsint off;
5081 off = JUMPX_OFFSET_LEN;
5082 goto do_lookup_switch;
5084 BEGIN_CASE(JSOP_LOOKUPSWITCH)
5085 off = JUMP_OFFSET_LEN;
5087 do_lookup_switch:
5089 * JSOP_LOOKUPSWITCH and JSOP_LOOKUPSWITCHX are never used if any atom
5090 * index in it would exceed 64K limit.
5092 JS_ASSERT(!regs.fp->hasImacropc());
5093 JS_ASSERT(atoms == script->atomMap.vector);
5094 jsbytecode *pc2 = regs.pc;
5096 Value lval = regs.sp[-1];
5097 regs.sp--;
5099 if (!lval.isPrimitive())
5100 goto end_lookup_switch;
5102 pc2 += off;
5103 jsint npairs;
5104 npairs = (jsint) GET_UINT16(pc2);
5105 pc2 += UINT16_LEN;
5106 JS_ASSERT(npairs); /* empty switch uses JSOP_TABLESWITCH */
5108 bool match;
5109 #define SEARCH_PAIRS(MATCH_CODE) \
5110 for (;;) { \
5111 Value rval = script->getConst(GET_INDEX(pc2)); \
5112 MATCH_CODE \
5113 pc2 += INDEX_LEN; \
5114 if (match) \
5115 break; \
5116 pc2 += off; \
5117 if (--npairs == 0) { \
5118 pc2 = regs.pc; \
5119 break; \
5123 if (lval.isString()) {
5124 JSLinearString *str = lval.toString()->ensureLinear(cx);
5125 if (!str)
5126 goto error;
5127 JSLinearString *str2;
5128 SEARCH_PAIRS(
5129 match = (rval.isString() &&
5130 ((str2 = rval.toString()->assertIsLinear()) == str ||
5131 EqualStrings(str2, str)));
5133 } else if (lval.isNumber()) {
5134 double ldbl = lval.toNumber();
5135 SEARCH_PAIRS(
5136 match = rval.isNumber() && ldbl == rval.toNumber();
5138 } else {
5139 SEARCH_PAIRS(
5140 match = (lval == rval);
5143 #undef SEARCH_PAIRS
5145 end_lookup_switch:
5146 len = (op == JSOP_LOOKUPSWITCH)
5147 ? GET_JUMP_OFFSET(pc2)
5148 : GET_JUMPX_OFFSET(pc2);
5150 END_VARLEN_CASE
5153 BEGIN_CASE(JSOP_TRAP)
5155 Value rval;
5156 JSTrapStatus status = JS_HandleTrap(cx, script, regs.pc, Jsvalify(&rval));
5157 switch (status) {
5158 case JSTRAP_ERROR:
5159 goto error;
5160 case JSTRAP_RETURN:
5161 regs.fp->setReturnValue(rval);
5162 interpReturnOK = JS_TRUE;
5163 goto forced_return;
5164 case JSTRAP_THROW:
5165 cx->setPendingException(rval);
5166 goto error;
5167 default:
5168 break;
5170 JS_ASSERT(status == JSTRAP_CONTINUE);
5171 CHECK_INTERRUPT_HANDLER();
5172 JS_ASSERT(rval.isInt32());
5173 op = (JSOp) rval.toInt32();
5174 JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
5175 DO_OP();
5178 BEGIN_CASE(JSOP_ARGUMENTS)
5180 Value rval;
5181 if (!js_GetArgsValue(cx, regs.fp, &rval))
5182 goto error;
5183 PUSH_COPY(rval);
5185 END_CASE(JSOP_ARGUMENTS)
5187 BEGIN_CASE(JSOP_ARGSUB)
5189 jsid id = INT_TO_JSID(GET_ARGNO(regs.pc));
5190 Value rval;
5191 if (!js_GetArgsProperty(cx, regs.fp, id, &rval))
5192 goto error;
5193 PUSH_COPY(rval);
5195 END_CASE(JSOP_ARGSUB)
5197 BEGIN_CASE(JSOP_ARGCNT)
5199 jsid id = ATOM_TO_JSID(rt->atomState.lengthAtom);
5200 Value rval;
5201 if (!js_GetArgsProperty(cx, regs.fp, id, &rval))
5202 goto error;
5203 PUSH_COPY(rval);
5205 END_CASE(JSOP_ARGCNT)
5207 BEGIN_CASE(JSOP_GETARG)
5208 BEGIN_CASE(JSOP_CALLARG)
5210 uint32 slot = GET_ARGNO(regs.pc);
5211 JS_ASSERT(slot < regs.fp->numFormalArgs());
5212 METER_SLOT_OP(op, slot);
5213 PUSH_COPY(argv[slot]);
5214 if (op == JSOP_CALLARG)
5215 PUSH_UNDEFINED();
5217 END_CASE(JSOP_GETARG)
5219 BEGIN_CASE(JSOP_SETARG)
5221 uint32 slot = GET_ARGNO(regs.pc);
5222 JS_ASSERT(slot < regs.fp->numFormalArgs());
5223 METER_SLOT_OP(op, slot);
5224 argv[slot] = regs.sp[-1];
5226 END_SET_CASE(JSOP_SETARG)
5228 BEGIN_CASE(JSOP_GETLOCAL)
5230 uint32 slot = GET_SLOTNO(regs.pc);
5231 JS_ASSERT(slot < script->nslots);
5232 PUSH_COPY(regs.fp->slots()[slot]);
5234 END_CASE(JSOP_GETLOCAL)
5236 BEGIN_CASE(JSOP_CALLLOCAL)
5238 uint32 slot = GET_SLOTNO(regs.pc);
5239 JS_ASSERT(slot < script->nslots);
5240 PUSH_COPY(regs.fp->slots()[slot]);
5241 PUSH_UNDEFINED();
5243 END_CASE(JSOP_CALLLOCAL)
5245 BEGIN_CASE(JSOP_SETLOCAL)
5247 uint32 slot = GET_SLOTNO(regs.pc);
5248 JS_ASSERT(slot < script->nslots);
5249 regs.fp->slots()[slot] = regs.sp[-1];
5251 END_SET_CASE(JSOP_SETLOCAL)
5253 BEGIN_CASE(JSOP_GETUPVAR_DBG)
5254 BEGIN_CASE(JSOP_CALLUPVAR_DBG)
5256 JSFunction *fun = regs.fp->fun();
5257 JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
5258 JS_ASSERT(fun->u.i.wrapper);
5260 /* Scope for tempPool mark and local names allocation in it. */
5261 JSObject *obj, *obj2;
5262 JSProperty *prop;
5263 jsid id;
5264 JSAtom *atom;
5266 AutoLocalNameArray names(cx, fun);
5267 if (!names)
5268 goto error;
5270 uintN index = fun->script()->bindings.countArgsAndVars() + GET_UINT16(regs.pc);
5271 atom = JS_LOCAL_NAME_TO_ATOM(names[index]);
5272 id = ATOM_TO_JSID(atom);
5274 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
5275 goto error;
5278 if (!prop) {
5279 atomNotDefined = atom;
5280 goto atom_not_defined;
5283 /* Minimize footprint with generic code instead of NATIVE_GET. */
5284 Value *vp = regs.sp;
5285 PUSH_NULL();
5286 if (!obj->getProperty(cx, id, vp))
5287 goto error;
5289 if (op == JSOP_CALLUPVAR_DBG)
5290 PUSH_UNDEFINED();
5292 END_CASE(JSOP_GETUPVAR_DBG)
5294 BEGIN_CASE(JSOP_GETFCSLOT)
5295 BEGIN_CASE(JSOP_CALLFCSLOT)
5297 JS_ASSERT(regs.fp->isFunctionFrame() && !regs.fp->isEvalFrame());
5298 uintN index = GET_UINT16(regs.pc);
5299 JSObject *obj = &argv[-2].toObject();
5301 JS_ASSERT(index < obj->getFunctionPrivate()->script()->bindings.countUpvars());
5302 PUSH_COPY(obj->getFlatClosureUpvar(index));
5303 if (op == JSOP_CALLFCSLOT)
5304 PUSH_UNDEFINED();
5306 END_CASE(JSOP_GETFCSLOT)
5308 BEGIN_CASE(JSOP_GETGLOBAL)
5309 BEGIN_CASE(JSOP_CALLGLOBAL)
5311 uint32 slot = GET_SLOTNO(regs.pc);
5312 slot = script->getGlobalSlot(slot);
5313 JSObject *obj = regs.fp->scopeChain().getGlobal();
5314 JS_ASSERT(obj->containsSlot(slot));
5315 PUSH_COPY(obj->getSlot(slot));
5316 if (op == JSOP_CALLGLOBAL)
5317 PUSH_UNDEFINED();
5319 END_CASE(JSOP_GETGLOBAL)
5321 BEGIN_CASE(JSOP_DEFCONST)
5322 BEGIN_CASE(JSOP_DEFVAR)
5324 uint32 index = GET_INDEX(regs.pc);
5325 JSAtom *atom = atoms[index];
5327 JSObject *obj = &regs.fp->varobj(cx);
5328 JS_ASSERT(!obj->getOps()->defineProperty);
5329 uintN attrs = JSPROP_ENUMERATE;
5330 if (!regs.fp->isEvalFrame())
5331 attrs |= JSPROP_PERMANENT;
5333 /* Lookup id in order to check for redeclaration problems. */
5334 jsid id = ATOM_TO_JSID(atom);
5335 bool shouldDefine;
5336 if (op == JSOP_DEFVAR) {
5338 * Redundant declaration of a |var|, even one for a non-writable
5339 * property like |undefined| in ES5, does nothing.
5341 JSProperty *prop;
5342 JSObject *obj2;
5343 if (!obj->lookupProperty(cx, id, &obj2, &prop))
5344 goto error;
5345 shouldDefine = (!prop || obj2 != obj);
5346 } else {
5347 JS_ASSERT(op == JSOP_DEFCONST);
5348 attrs |= JSPROP_READONLY;
5349 if (!CheckRedeclaration(cx, obj, id, attrs))
5350 goto error;
5353 * As attrs includes readonly, CheckRedeclaration can succeed only
5354 * if prop does not exist.
5356 shouldDefine = true;
5359 /* Bind a variable only if it's not yet defined. */
5360 if (shouldDefine &&
5361 !js_DefineNativeProperty(cx, obj, id, UndefinedValue(),
5362 PropertyStub, StrictPropertyStub, attrs, 0, 0, NULL)) {
5363 goto error;
5366 END_CASE(JSOP_DEFVAR)
5368 BEGIN_CASE(JSOP_DEFFUN)
5371 * A top-level function defined in Global or Eval code (see ECMA-262
5372 * Ed. 3), or else a SpiderMonkey extension: a named function statement in
5373 * a compound statement (not at the top statement level of global code, or
5374 * at the top level of a function body).
5376 JSFunction *fun;
5377 LOAD_FUNCTION(0);
5378 JSObject *obj = FUN_OBJECT(fun);
5380 JSObject *obj2;
5381 if (FUN_NULL_CLOSURE(fun)) {
5383 * Even a null closure needs a parent for principals finding.
5384 * FIXME: bug 476950, although debugger users may also demand some kind
5385 * of scope link for debugger-assisted eval-in-frame.
5387 obj2 = &regs.fp->scopeChain();
5388 } else {
5389 JS_ASSERT(!fun->isFlatClosure());
5391 obj2 = GetScopeChainFast(cx, regs.fp, JSOP_DEFFUN, JSOP_DEFFUN_LENGTH);
5392 if (!obj2)
5393 goto error;
5397 * If static link is not current scope, clone fun's object to link to the
5398 * current scope via parent. We do this to enable sharing of compiled
5399 * functions among multiple equivalent scopes, amortizing the cost of
5400 * compilation over a number of executions. Examples include XUL scripts
5401 * and event handlers shared among Firefox or other Mozilla app chrome
5402 * windows, and user-defined JS functions precompiled and then shared among
5403 * requests in server-side JS.
5405 if (obj->getParent() != obj2) {
5406 obj = CloneFunctionObject(cx, fun, obj2);
5407 if (!obj)
5408 goto error;
5412 * ECMA requires functions defined when entering Eval code to be
5413 * impermanent.
5415 uintN attrs = regs.fp->isEvalFrame()
5416 ? JSPROP_ENUMERATE
5417 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
5420 * We define the function as a property of the variable object and not the
5421 * current scope chain even for the case of function expression statements
5422 * and functions defined by eval inside let or with blocks.
5424 JSObject *parent = &regs.fp->varobj(cx);
5426 /* ES5 10.5 (NB: with subsequent errata). */
5427 jsid id = ATOM_TO_JSID(fun->atom);
5428 JSProperty *prop = NULL;
5429 JSObject *pobj;
5430 if (!parent->lookupProperty(cx, id, &pobj, &prop))
5431 goto error;
5433 Value rval = ObjectValue(*obj);
5435 do {
5436 /* Steps 5d, 5f. */
5437 if (!prop || pobj != parent) {
5438 if (!parent->defineProperty(cx, id, rval, PropertyStub, StrictPropertyStub, attrs))
5439 goto error;
5440 break;
5443 /* Step 5e. */
5444 JS_ASSERT(parent->isNative());
5445 Shape *shape = reinterpret_cast<Shape *>(prop);
5446 if (parent->isGlobal()) {
5447 if (shape->configurable()) {
5448 if (!parent->defineProperty(cx, id, rval, PropertyStub, StrictPropertyStub, attrs))
5449 goto error;
5450 break;
5453 if (shape->isAccessorDescriptor() || !shape->writable() || !shape->enumerable()) {
5454 JSAutoByteString bytes;
5455 if (const char *name = js_ValueToPrintable(cx, IdToValue(id), &bytes)) {
5456 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5457 JSMSG_CANT_REDEFINE_PROP, name);
5459 goto error;
5464 * Non-global properties, and global properties which we aren't simply
5465 * redefining, must be set. First, this preserves their attributes.
5466 * Second, this will produce warnings and/or errors as necessary if the
5467 * specified Call object property is not writable (const).
5470 /* Step 5f. */
5471 if (!parent->setProperty(cx, id, &rval, script->strictModeCode))
5472 goto error;
5473 } while (false);
5475 END_CASE(JSOP_DEFFUN)
5477 BEGIN_CASE(JSOP_DEFFUN_FC)
5478 BEGIN_CASE(JSOP_DEFFUN_DBGFC)
5480 JSFunction *fun;
5481 LOAD_FUNCTION(0);
5483 JSObject *obj = (op == JSOP_DEFFUN_FC)
5484 ? js_NewFlatClosure(cx, fun, JSOP_DEFFUN_FC, JSOP_DEFFUN_FC_LENGTH)
5485 : js_NewDebuggableFlatClosure(cx, fun);
5486 if (!obj)
5487 goto error;
5489 Value rval = ObjectValue(*obj);
5491 uintN attrs = regs.fp->isEvalFrame()
5492 ? JSPROP_ENUMERATE
5493 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
5495 JSObject &parent = regs.fp->varobj(cx);
5497 jsid id = ATOM_TO_JSID(fun->atom);
5498 if (!CheckRedeclaration(cx, &parent, id, attrs))
5499 goto error;
5501 if ((attrs == JSPROP_ENUMERATE)
5502 ? !parent.setProperty(cx, id, &rval, script->strictModeCode)
5503 : !parent.defineProperty(cx, id, rval, PropertyStub, StrictPropertyStub, attrs)) {
5504 goto error;
5507 END_CASE(JSOP_DEFFUN_FC)
5509 BEGIN_CASE(JSOP_DEFLOCALFUN)
5512 * Define a local function (i.e., one nested at the top level of another
5513 * function), parented by the current scope chain, stored in a local
5514 * variable slot that the compiler allocated. This is an optimization over
5515 * JSOP_DEFFUN that avoids requiring a call object for the outer function's
5516 * activation.
5518 JSFunction *fun;
5519 LOAD_FUNCTION(SLOTNO_LEN);
5520 JS_ASSERT(fun->isInterpreted());
5521 JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
5522 JSObject *obj = FUN_OBJECT(fun);
5524 if (FUN_NULL_CLOSURE(fun)) {
5525 obj = CloneFunctionObject(cx, fun, &regs.fp->scopeChain());
5526 if (!obj)
5527 goto error;
5528 } else {
5529 JSObject *parent = GetScopeChainFast(cx, regs.fp, JSOP_DEFLOCALFUN,
5530 JSOP_DEFLOCALFUN_LENGTH);
5531 if (!parent)
5532 goto error;
5534 if (obj->getParent() != parent) {
5535 #ifdef JS_TRACER
5536 if (TRACE_RECORDER(cx))
5537 AbortRecording(cx, "DEFLOCALFUN for closure");
5538 #endif
5539 obj = CloneFunctionObject(cx, fun, parent);
5540 if (!obj)
5541 goto error;
5545 uint32 slot = GET_SLOTNO(regs.pc);
5546 TRACE_2(DefLocalFunSetSlot, slot, obj);
5548 regs.fp->slots()[slot].setObject(*obj);
5550 END_CASE(JSOP_DEFLOCALFUN)
5552 BEGIN_CASE(JSOP_DEFLOCALFUN_FC)
5554 JSFunction *fun;
5555 LOAD_FUNCTION(SLOTNO_LEN);
5557 JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_DEFLOCALFUN_FC, JSOP_DEFLOCALFUN_FC_LENGTH);
5558 if (!obj)
5559 goto error;
5561 uint32 slot = GET_SLOTNO(regs.pc);
5562 TRACE_2(DefLocalFunSetSlot, slot, obj);
5564 regs.fp->slots()[slot].setObject(*obj);
5566 END_CASE(JSOP_DEFLOCALFUN_FC)
5568 BEGIN_CASE(JSOP_DEFLOCALFUN_DBGFC)
5570 JSFunction *fun;
5571 LOAD_FUNCTION(SLOTNO_LEN);
5573 JSObject *obj = js_NewDebuggableFlatClosure(cx, fun);
5574 if (!obj)
5575 goto error;
5577 uint32 slot = GET_SLOTNO(regs.pc);
5578 regs.fp->slots()[slot].setObject(*obj);
5580 END_CASE(JSOP_DEFLOCALFUN_DBGFC)
5582 BEGIN_CASE(JSOP_LAMBDA)
5584 /* Load the specified function object literal. */
5585 JSFunction *fun;
5586 LOAD_FUNCTION(0);
5587 JSObject *obj = FUN_OBJECT(fun);
5589 /* do-while(0) so we can break instead of using a goto. */
5590 do {
5591 JSObject *parent;
5592 if (FUN_NULL_CLOSURE(fun)) {
5593 parent = &regs.fp->scopeChain();
5595 if (obj->getParent() == parent) {
5596 jsbytecode *pc2 = AdvanceOverBlockchainOp(regs.pc + JSOP_LAMBDA_LENGTH);
5597 JSOp op2 = JSOp(*pc2);
5600 * Optimize var obj = {method: function () { ... }, ...},
5601 * this.method = function () { ... }; and other significant
5602 * single-use-of-null-closure bytecode sequences.
5604 * WARNING: code in TraceRecorder::record_JSOP_LAMBDA must
5605 * match the optimization cases in the following code that
5606 * break from the outer do-while(0).
5608 if (op2 == JSOP_INITMETHOD) {
5609 #ifdef DEBUG
5610 const Value &lref = regs.sp[-1];
5611 JS_ASSERT(lref.isObject());
5612 JSObject *obj2 = &lref.toObject();
5613 JS_ASSERT(obj2->getClass() == &js_ObjectClass);
5614 #endif
5616 fun->setMethodAtom(script->getAtom(GET_FULL_INDEX(pc2 - regs.pc)));
5617 JS_FUNCTION_METER(cx, joinedinitmethod);
5618 break;
5621 if (op2 == JSOP_SETMETHOD) {
5622 #ifdef DEBUG
5623 op2 = JSOp(pc2[JSOP_SETMETHOD_LENGTH]);
5624 JS_ASSERT(op2 == JSOP_POP || op2 == JSOP_POPV);
5625 #endif
5626 const Value &lref = regs.sp[-1];
5627 if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) {
5628 fun->setMethodAtom(script->getAtom(GET_FULL_INDEX(pc2 - regs.pc)));
5629 JS_FUNCTION_METER(cx, joinedsetmethod);
5630 break;
5632 } else if (fun->joinable()) {
5633 if (op2 == JSOP_CALL) {
5635 * Array.prototype.sort and String.prototype.replace are
5636 * optimized as if they are special form. We know that they
5637 * won't leak the joined function object in obj, therefore
5638 * we don't need to clone that compiler- created function
5639 * object for identity/mutation reasons.
5641 int iargc = GET_ARGC(pc2);
5644 * Note that we have not yet pushed obj as the final argument,
5645 * so regs.sp[1 - (iargc + 2)], and not regs.sp[-(iargc + 2)],
5646 * is the callee for this JSOP_CALL.
5648 const Value &cref = regs.sp[1 - (iargc + 2)];
5649 JSObject *callee;
5651 if (IsFunctionObject(cref, &callee)) {
5652 JSFunction *calleeFun = GET_FUNCTION_PRIVATE(cx, callee);
5653 if (Native native = calleeFun->maybeNative()) {
5654 if (iargc == 1 && native == array_sort) {
5655 JS_FUNCTION_METER(cx, joinedsort);
5656 break;
5658 if (iargc == 2 && native == str_replace) {
5659 JS_FUNCTION_METER(cx, joinedreplace);
5660 break;
5664 } else if (op2 == JSOP_NULL) {
5665 pc2 += JSOP_NULL_LENGTH;
5666 op2 = JSOp(*pc2);
5668 if (op2 == JSOP_CALL && GET_ARGC(pc2) == 0) {
5669 JS_FUNCTION_METER(cx, joinedmodulepat);
5670 break;
5676 #ifdef DEBUG
5677 if (rt->functionMeterFilename) {
5678 // No locking, this is mainly for js shell testing.
5679 ++rt->functionMeter.unjoined;
5681 typedef JSRuntime::FunctionCountMap HM;
5682 HM &h = rt->unjoinedFunctionCountMap;
5683 HM::AddPtr p = h.lookupForAdd(fun);
5684 if (!p) {
5685 h.add(p, fun, 1);
5686 } else {
5687 JS_ASSERT(p->key == fun);
5688 ++p->value;
5691 #endif
5692 } else {
5693 parent = GetScopeChainFast(cx, regs.fp, JSOP_LAMBDA, JSOP_LAMBDA_LENGTH);
5694 if (!parent)
5695 goto error;
5698 obj = CloneFunctionObject(cx, fun, parent);
5699 if (!obj)
5700 goto error;
5701 } while (0);
5703 PUSH_OBJECT(*obj);
5705 END_CASE(JSOP_LAMBDA)
5707 BEGIN_CASE(JSOP_LAMBDA_FC)
5709 JSFunction *fun;
5710 LOAD_FUNCTION(0);
5712 JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH);
5713 if (!obj)
5714 goto error;
5716 PUSH_OBJECT(*obj);
5718 END_CASE(JSOP_LAMBDA_FC)
5720 BEGIN_CASE(JSOP_LAMBDA_DBGFC)
5722 JSFunction *fun;
5723 LOAD_FUNCTION(0);
5725 JSObject *obj = js_NewDebuggableFlatClosure(cx, fun);
5726 if (!obj)
5727 goto error;
5729 PUSH_OBJECT(*obj);
5731 END_CASE(JSOP_LAMBDA_DBGFC)
5733 BEGIN_CASE(JSOP_CALLEE)
5734 JS_ASSERT(regs.fp->isFunctionFrame() && !regs.fp->isEvalFrame());
5735 PUSH_COPY(argv[-2]);
5736 END_CASE(JSOP_CALLEE)
5738 BEGIN_CASE(JSOP_GETTER)
5739 BEGIN_CASE(JSOP_SETTER)
5741 do_getter_setter:
5742 JSOp op2 = (JSOp) *++regs.pc;
5743 jsid id;
5744 Value rval;
5745 jsint i;
5746 JSObject *obj;
5747 switch (op2) {
5748 case JSOP_INDEXBASE:
5749 atoms += GET_INDEXBASE(regs.pc);
5750 regs.pc += JSOP_INDEXBASE_LENGTH - 1;
5751 goto do_getter_setter;
5752 case JSOP_INDEXBASE1:
5753 case JSOP_INDEXBASE2:
5754 case JSOP_INDEXBASE3:
5755 atoms += (op2 - JSOP_INDEXBASE1 + 1) << 16;
5756 goto do_getter_setter;
5758 case JSOP_SETNAME:
5759 case JSOP_SETPROP:
5761 JSAtom *atom;
5762 LOAD_ATOM(0, atom);
5763 id = ATOM_TO_JSID(atom);
5764 rval = regs.sp[-1];
5765 i = -1;
5766 goto gs_pop_lval;
5768 case JSOP_SETELEM:
5769 rval = regs.sp[-1];
5770 id = JSID_VOID;
5771 i = -2;
5772 gs_pop_lval:
5773 FETCH_OBJECT(cx, i - 1, obj);
5774 break;
5776 case JSOP_INITPROP:
5778 JS_ASSERT(regs.sp - regs.fp->base() >= 2);
5779 rval = regs.sp[-1];
5780 i = -1;
5781 JSAtom *atom;
5782 LOAD_ATOM(0, atom);
5783 id = ATOM_TO_JSID(atom);
5784 goto gs_get_lval;
5786 default:
5787 JS_ASSERT(op2 == JSOP_INITELEM);
5789 JS_ASSERT(regs.sp - regs.fp->base() >= 3);
5790 rval = regs.sp[-1];
5791 id = JSID_VOID;
5792 i = -2;
5793 gs_get_lval:
5795 const Value &lref = regs.sp[i-1];
5796 JS_ASSERT(lref.isObject());
5797 obj = &lref.toObject();
5798 break;
5802 /* Ensure that id has a type suitable for use with obj. */
5803 if (JSID_IS_VOID(id))
5804 FETCH_ELEMENT_ID(obj, i, id);
5806 if (!js_IsCallable(rval)) {
5807 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5808 JSMSG_BAD_GETTER_OR_SETTER,
5809 (op == JSOP_GETTER)
5810 ? js_getter_str
5811 : js_setter_str);
5812 goto error;
5816 * Getters and setters are just like watchpoints from an access control
5817 * point of view.
5819 Value rtmp;
5820 uintN attrs;
5821 if (!CheckAccess(cx, obj, id, JSACC_WATCH, &rtmp, &attrs))
5822 goto error;
5824 PropertyOp getter;
5825 StrictPropertyOp setter;
5826 if (op == JSOP_GETTER) {
5827 getter = CastAsPropertyOp(&rval.toObject());
5828 setter = StrictPropertyStub;
5829 attrs = JSPROP_GETTER;
5830 } else {
5831 getter = PropertyStub;
5832 setter = CastAsStrictPropertyOp(&rval.toObject());
5833 attrs = JSPROP_SETTER;
5835 attrs |= JSPROP_ENUMERATE | JSPROP_SHARED;
5837 /* Check for a readonly or permanent property of the same name. */
5838 if (!CheckRedeclaration(cx, obj, id, attrs))
5839 goto error;
5841 if (!obj->defineProperty(cx, id, UndefinedValue(), getter, setter, attrs))
5842 goto error;
5844 regs.sp += i;
5845 if (js_CodeSpec[op2].ndefs > js_CodeSpec[op2].nuses) {
5846 JS_ASSERT(js_CodeSpec[op2].ndefs == js_CodeSpec[op2].nuses + 1);
5847 regs.sp[-1] = rval;
5848 assertSameCompartment(cx, regs.sp[-1]);
5850 len = js_CodeSpec[op2].length;
5851 DO_NEXT_OP(len);
5854 BEGIN_CASE(JSOP_HOLE)
5855 PUSH_HOLE();
5856 END_CASE(JSOP_HOLE)
5858 BEGIN_CASE(JSOP_NEWINIT)
5860 jsint i = regs.pc[1];
5862 JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
5863 JSObject *obj;
5865 if (i == JSProto_Array) {
5866 obj = NewDenseEmptyArray(cx);
5867 } else {
5868 gc::FinalizeKind kind = GuessObjectGCKind(0, false);
5869 obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
5872 if (!obj)
5873 goto error;
5875 PUSH_OBJECT(*obj);
5876 CHECK_INTERRUPT_HANDLER();
5878 END_CASE(JSOP_NEWINIT)
5880 BEGIN_CASE(JSOP_NEWARRAY)
5882 unsigned count = GET_UINT24(regs.pc);
5883 JSObject *obj = NewDenseAllocatedArray(cx, count);
5884 if (!obj)
5885 goto error;
5887 PUSH_OBJECT(*obj);
5888 CHECK_INTERRUPT_HANDLER();
5890 END_CASE(JSOP_NEWARRAY)
5892 BEGIN_CASE(JSOP_NEWOBJECT)
5894 JSObject *baseobj;
5895 LOAD_OBJECT(0, baseobj);
5897 JSObject *obj = CopyInitializerObject(cx, baseobj);
5899 if (!obj)
5900 goto error;
5902 PUSH_OBJECT(*obj);
5903 CHECK_INTERRUPT_HANDLER();
5905 END_CASE(JSOP_NEWOBJECT)
5907 BEGIN_CASE(JSOP_ENDINIT)
5909 /* FIXME remove JSOP_ENDINIT bug 588522 */
5910 JS_ASSERT(regs.sp - regs.fp->base() >= 1);
5911 JS_ASSERT(regs.sp[-1].isObject());
5913 END_CASE(JSOP_ENDINIT)
5915 BEGIN_CASE(JSOP_INITPROP)
5916 BEGIN_CASE(JSOP_INITMETHOD)
5918 /* Load the property's initial value into rval. */
5919 JS_ASSERT(regs.sp - regs.fp->base() >= 2);
5920 Value rval = regs.sp[-1];
5922 /* Load the object being initialized into lval/obj. */
5923 JSObject *obj = &regs.sp[-2].toObject();
5924 JS_ASSERT(obj->isObject());
5927 * Probe the property cache.
5929 * On a hit, if the cached shape has a non-default setter, it must be
5930 * __proto__. If shape->previous() != obj->lastProperty(), there must be a
5931 * repeated property name. The fast path does not handle these two cases.
5933 PropertyCacheEntry *entry;
5934 const Shape *shape;
5935 if (JS_PROPERTY_CACHE(cx).testForInit(rt, regs.pc, obj, &shape, &entry) &&
5936 shape->hasDefaultSetter() &&
5937 shape->previous() == obj->lastProperty())
5939 /* Fast path. Property cache hit. */
5940 uint32 slot = shape->slot;
5942 JS_ASSERT(slot == obj->slotSpan());
5943 JS_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
5944 if (slot < obj->numSlots()) {
5945 JS_ASSERT(obj->getSlot(slot).isUndefined());
5946 } else {
5947 if (!obj->allocSlot(cx, &slot))
5948 goto error;
5949 JS_ASSERT(slot == shape->slot);
5952 /* A new object, or one we just extended in a recent initprop op. */
5953 JS_ASSERT(!obj->lastProperty() ||
5954 obj->shape() == obj->lastProperty()->shape);
5955 obj->extend(cx, shape);
5958 * No method change check here because here we are adding a new
5959 * property, not updating an existing slot's value that might
5960 * contain a method of a branded shape.
5962 TRACE_1(AddProperty, obj);
5963 obj->nativeSetSlot(slot, rval);
5964 } else {
5965 PCMETER(JS_PROPERTY_CACHE(cx).inipcmisses++);
5967 /* Get the immediate property name into id. */
5968 JSAtom *atom;
5969 LOAD_ATOM(0, atom);
5970 jsid id = ATOM_TO_JSID(atom);
5972 uintN defineHow = (op == JSOP_INITMETHOD)
5973 ? JSDNP_CACHE_RESULT | JSDNP_SET_METHOD
5974 : JSDNP_CACHE_RESULT;
5975 if (!(JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom)
5976 ? js_SetPropertyHelper(cx, obj, id, defineHow, &rval, script->strictModeCode)
5977 : js_DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
5978 JSPROP_ENUMERATE, 0, 0, NULL,
5979 defineHow))) {
5980 goto error;
5984 /* Common tail for property cache hit and miss cases. */
5985 regs.sp--;
5987 END_CASE(JSOP_INITPROP);
5989 BEGIN_CASE(JSOP_INITELEM)
5991 /* Pop the element's value into rval. */
5992 JS_ASSERT(regs.sp - regs.fp->base() >= 3);
5993 const Value &rref = regs.sp[-1];
5995 /* Find the object being initialized at top of stack. */
5996 const Value &lref = regs.sp[-3];
5997 JS_ASSERT(lref.isObject());
5998 JSObject *obj = &lref.toObject();
6000 /* Fetch id now that we have obj. */
6001 jsid id;
6002 FETCH_ELEMENT_ID(obj, -2, id);
6005 * If rref is a hole, do not call JSObject::defineProperty. In this case,
6006 * obj must be an array, so if the current op is the last element
6007 * initialiser, set the array length to one greater than id.
6009 if (rref.isMagic(JS_ARRAY_HOLE)) {
6010 JS_ASSERT(obj->isArray());
6011 JS_ASSERT(JSID_IS_INT(id));
6012 JS_ASSERT(jsuint(JSID_TO_INT(id)) < JS_ARGS_LENGTH_MAX);
6013 if (js_GetOpcode(cx, script, regs.pc + JSOP_INITELEM_LENGTH) == JSOP_ENDINIT &&
6014 !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1))) {
6015 goto error;
6017 } else {
6018 if (!obj->defineProperty(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE))
6019 goto error;
6021 regs.sp -= 2;
6023 END_CASE(JSOP_INITELEM)
6025 #if JS_HAS_SHARP_VARS
6027 BEGIN_CASE(JSOP_DEFSHARP)
6029 uint32 slot = GET_UINT16(regs.pc);
6030 JS_ASSERT(slot + 1 < regs.fp->numFixed());
6031 const Value &lref = regs.fp->slots()[slot];
6032 JSObject *obj;
6033 if (lref.isObject()) {
6034 obj = &lref.toObject();
6035 } else {
6036 JS_ASSERT(lref.isUndefined());
6037 obj = NewDenseEmptyArray(cx);
6038 if (!obj)
6039 goto error;
6040 regs.fp->slots()[slot].setObject(*obj);
6042 jsint i = (jsint) GET_UINT16(regs.pc + UINT16_LEN);
6043 jsid id = INT_TO_JSID(i);
6044 const Value &rref = regs.sp[-1];
6045 if (rref.isPrimitive()) {
6046 char numBuf[12];
6047 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
6048 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6049 JSMSG_BAD_SHARP_DEF, numBuf);
6050 goto error;
6052 if (!obj->defineProperty(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE))
6053 goto error;
6055 END_CASE(JSOP_DEFSHARP)
6057 BEGIN_CASE(JSOP_USESHARP)
6059 uint32 slot = GET_UINT16(regs.pc);
6060 JS_ASSERT(slot + 1 < regs.fp->numFixed());
6061 const Value &lref = regs.fp->slots()[slot];
6062 jsint i = (jsint) GET_UINT16(regs.pc + UINT16_LEN);
6063 Value rval;
6064 if (lref.isUndefined()) {
6065 rval.setUndefined();
6066 } else {
6067 JSObject *obj = &regs.fp->slots()[slot].toObject();
6068 jsid id = INT_TO_JSID(i);
6069 if (!obj->getProperty(cx, id, &rval))
6070 goto error;
6072 if (!rval.isObjectOrNull()) {
6073 char numBuf[12];
6075 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
6076 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6077 JSMSG_BAD_SHARP_USE, numBuf);
6078 goto error;
6080 PUSH_COPY(rval);
6082 END_CASE(JSOP_USESHARP)
6084 BEGIN_CASE(JSOP_SHARPINIT)
6086 uint32 slot = GET_UINT16(regs.pc);
6087 JS_ASSERT(slot + 1 < regs.fp->numFixed());
6088 Value *vp = &regs.fp->slots()[slot];
6089 Value rval = vp[1];
6092 * We peek ahead safely here because empty initialisers get zero
6093 * JSOP_SHARPINIT ops, and non-empty ones get two: the first comes
6094 * immediately after JSOP_NEWINIT followed by one or more property
6095 * initialisers; and the second comes directly before JSOP_ENDINIT.
6097 if (regs.pc[JSOP_SHARPINIT_LENGTH] != JSOP_ENDINIT) {
6098 rval.setInt32(rval.isUndefined() ? 1 : rval.toInt32() + 1);
6099 } else {
6100 JS_ASSERT(rval.isInt32());
6101 rval.getInt32Ref() -= 1;
6102 if (rval.toInt32() == 0)
6103 vp[0].setUndefined();
6105 vp[1] = rval;
6107 END_CASE(JSOP_SHARPINIT)
6109 #endif /* JS_HAS_SHARP_VARS */
6112 BEGIN_CASE(JSOP_GOSUB)
6113 PUSH_BOOLEAN(false);
6114 jsint i = (regs.pc - script->main) + JSOP_GOSUB_LENGTH;
6115 PUSH_INT32(i);
6116 len = GET_JUMP_OFFSET(regs.pc);
6117 END_VARLEN_CASE
6121 BEGIN_CASE(JSOP_GOSUBX)
6122 PUSH_BOOLEAN(false);
6123 jsint i = (regs.pc - script->main) + JSOP_GOSUBX_LENGTH;
6124 len = GET_JUMPX_OFFSET(regs.pc);
6125 PUSH_INT32(i);
6126 END_VARLEN_CASE
6130 BEGIN_CASE(JSOP_RETSUB)
6131 /* Pop [exception or hole, retsub pc-index]. */
6132 Value rval, lval;
6133 POP_COPY_TO(rval);
6134 POP_COPY_TO(lval);
6135 JS_ASSERT(lval.isBoolean());
6136 if (lval.toBoolean()) {
6138 * Exception was pending during finally, throw it *before* we adjust
6139 * pc, because pc indexes into script->trynotes. This turns out not to
6140 * be necessary, but it seems clearer. And it points out a FIXME:
6141 * 350509, due to Igor Bukanov.
6143 cx->setPendingException(rval);
6144 goto error;
6146 JS_ASSERT(rval.isInt32());
6147 len = rval.toInt32();
6148 regs.pc = script->main;
6149 END_VARLEN_CASE
6152 BEGIN_CASE(JSOP_EXCEPTION)
6153 PUSH_COPY(cx->getPendingException());
6154 cx->clearPendingException();
6155 #if defined(JS_TRACER) && defined(JS_METHODJIT)
6156 if (interpMode == JSINTERP_PROFILE) {
6157 leaveOnSafePoint = true;
6158 LEAVE_ON_SAFE_POINT();
6160 #endif
6161 CHECK_BRANCH();
6162 END_CASE(JSOP_EXCEPTION)
6164 BEGIN_CASE(JSOP_FINALLY)
6165 CHECK_BRANCH();
6166 END_CASE(JSOP_FINALLY)
6168 BEGIN_CASE(JSOP_THROWING)
6170 JS_ASSERT(!cx->isExceptionPending());
6171 Value v;
6172 POP_COPY_TO(v);
6173 cx->setPendingException(v);
6175 END_CASE(JSOP_THROWING)
6177 BEGIN_CASE(JSOP_THROW)
6179 JS_ASSERT(!cx->isExceptionPending());
6180 CHECK_BRANCH();
6181 Value v;
6182 POP_COPY_TO(v);
6183 cx->setPendingException(v);
6184 /* let the code at error try to catch the exception. */
6185 goto error;
6187 BEGIN_CASE(JSOP_SETLOCALPOP)
6190 * The stack must have a block with at least one local slot below the
6191 * exception object.
6193 JS_ASSERT((size_t) (regs.sp - regs.fp->base()) >= 2);
6194 uint32 slot = GET_UINT16(regs.pc);
6195 JS_ASSERT(slot + 1 < script->nslots);
6196 POP_COPY_TO(regs.fp->slots()[slot]);
6198 END_CASE(JSOP_SETLOCALPOP)
6200 BEGIN_CASE(JSOP_IFPRIMTOP)
6202 * If the top of stack is of primitive type, jump to our target. Otherwise
6203 * advance to the next opcode.
6205 JS_ASSERT(regs.sp > regs.fp->base());
6206 if (regs.sp[-1].isPrimitive()) {
6207 len = GET_JUMP_OFFSET(regs.pc);
6208 BRANCH(len);
6210 END_CASE(JSOP_IFPRIMTOP)
6212 BEGIN_CASE(JSOP_PRIMTOP)
6213 JS_ASSERT(regs.sp > regs.fp->base());
6214 if (regs.sp[-1].isObject()) {
6215 jsint i = GET_INT8(regs.pc);
6216 js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO, -2, regs.sp[-2], NULL,
6217 (i == JSTYPE_VOID) ? "primitive type" : JS_TYPE_STR(i));
6218 goto error;
6220 END_CASE(JSOP_PRIMTOP)
6222 BEGIN_CASE(JSOP_OBJTOP)
6223 if (regs.sp[-1].isPrimitive()) {
6224 js_ReportValueError(cx, GET_UINT16(regs.pc), -1, regs.sp[-1], NULL);
6225 goto error;
6227 END_CASE(JSOP_OBJTOP)
6229 BEGIN_CASE(JSOP_INSTANCEOF)
6231 const Value &rref = regs.sp[-1];
6232 if (rref.isPrimitive()) {
6233 js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, -1, rref, NULL);
6234 goto error;
6236 JSObject *obj = &rref.toObject();
6237 const Value &lref = regs.sp[-2];
6238 JSBool cond = JS_FALSE;
6239 if (!HasInstance(cx, obj, &lref, &cond))
6240 goto error;
6241 regs.sp--;
6242 regs.sp[-1].setBoolean(cond);
6244 END_CASE(JSOP_INSTANCEOF)
6246 BEGIN_CASE(JSOP_DEBUGGER)
6248 JSDebuggerHandler handler = cx->debugHooks->debuggerHandler;
6249 if (handler) {
6250 Value rval;
6251 switch (handler(cx, script, regs.pc, Jsvalify(&rval), cx->debugHooks->debuggerHandlerData)) {
6252 case JSTRAP_ERROR:
6253 goto error;
6254 case JSTRAP_CONTINUE:
6255 break;
6256 case JSTRAP_RETURN:
6257 regs.fp->setReturnValue(rval);
6258 interpReturnOK = JS_TRUE;
6259 goto forced_return;
6260 case JSTRAP_THROW:
6261 cx->setPendingException(rval);
6262 goto error;
6263 default:;
6265 CHECK_INTERRUPT_HANDLER();
6268 END_CASE(JSOP_DEBUGGER)
6270 #if JS_HAS_XML_SUPPORT
6271 BEGIN_CASE(JSOP_DEFXMLNS)
6273 if (!js_SetDefaultXMLNamespace(cx, regs.sp[-1]))
6274 goto error;
6275 regs.sp--;
6277 END_CASE(JSOP_DEFXMLNS)
6279 BEGIN_CASE(JSOP_ANYNAME)
6281 jsid id;
6282 if (!js_GetAnyName(cx, &id))
6283 goto error;
6284 PUSH_COPY(IdToValue(id));
6286 END_CASE(JSOP_ANYNAME)
6288 BEGIN_CASE(JSOP_QNAMEPART)
6290 JSAtom *atom;
6291 LOAD_ATOM(0, atom);
6292 PUSH_STRING(ATOM_TO_STRING(atom));
6294 END_CASE(JSOP_QNAMEPART)
6296 BEGIN_CASE(JSOP_QNAMECONST)
6298 JSAtom *atom;
6299 LOAD_ATOM(0, atom);
6300 Value rval = StringValue(ATOM_TO_STRING(atom));
6301 Value lval = regs.sp[-1];
6302 JSObject *obj = js_ConstructXMLQNameObject(cx, lval, rval);
6303 if (!obj)
6304 goto error;
6305 regs.sp[-1].setObject(*obj);
6307 END_CASE(JSOP_QNAMECONST)
6309 BEGIN_CASE(JSOP_QNAME)
6311 Value rval = regs.sp[-1];
6312 Value lval = regs.sp[-2];
6313 JSObject *obj = js_ConstructXMLQNameObject(cx, lval, rval);
6314 if (!obj)
6315 goto error;
6316 regs.sp--;
6317 regs.sp[-1].setObject(*obj);
6319 END_CASE(JSOP_QNAME)
6321 BEGIN_CASE(JSOP_TOATTRNAME)
6323 Value rval;
6324 rval = regs.sp[-1];
6325 if (!js_ToAttributeName(cx, &rval))
6326 goto error;
6327 regs.sp[-1] = rval;
6329 END_CASE(JSOP_TOATTRNAME)
6331 BEGIN_CASE(JSOP_TOATTRVAL)
6333 Value rval;
6334 rval = regs.sp[-1];
6335 JS_ASSERT(rval.isString());
6336 JSString *str = js_EscapeAttributeValue(cx, rval.toString(), JS_FALSE);
6337 if (!str)
6338 goto error;
6339 regs.sp[-1].setString(str);
6341 END_CASE(JSOP_TOATTRVAL)
6343 BEGIN_CASE(JSOP_ADDATTRNAME)
6344 BEGIN_CASE(JSOP_ADDATTRVAL)
6346 Value rval = regs.sp[-1];
6347 Value lval = regs.sp[-2];
6348 JSString *str = lval.toString();
6349 JSString *str2 = rval.toString();
6350 str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2);
6351 if (!str)
6352 goto error;
6353 regs.sp--;
6354 regs.sp[-1].setString(str);
6356 END_CASE(JSOP_ADDATTRNAME)
6358 BEGIN_CASE(JSOP_BINDXMLNAME)
6360 Value lval;
6361 lval = regs.sp[-1];
6362 JSObject *obj;
6363 jsid id;
6364 if (!js_FindXMLProperty(cx, lval, &obj, &id))
6365 goto error;
6366 regs.sp[-1].setObjectOrNull(obj);
6367 PUSH_COPY(IdToValue(id));
6369 END_CASE(JSOP_BINDXMLNAME)
6371 BEGIN_CASE(JSOP_SETXMLNAME)
6373 JSObject *obj = &regs.sp[-3].toObject();
6374 Value rval = regs.sp[-1];
6375 jsid id;
6376 FETCH_ELEMENT_ID(obj, -2, id);
6377 if (!obj->setProperty(cx, id, &rval, script->strictModeCode))
6378 goto error;
6379 rval = regs.sp[-1];
6380 regs.sp -= 2;
6381 regs.sp[-1] = rval;
6383 END_CASE(JSOP_SETXMLNAME)
6385 BEGIN_CASE(JSOP_CALLXMLNAME)
6386 BEGIN_CASE(JSOP_XMLNAME)
6388 Value lval = regs.sp[-1];
6389 JSObject *obj;
6390 jsid id;
6391 if (!js_FindXMLProperty(cx, lval, &obj, &id))
6392 goto error;
6393 Value rval;
6394 if (!obj->getProperty(cx, id, &rval))
6395 goto error;
6396 regs.sp[-1] = rval;
6397 if (op == JSOP_CALLXMLNAME)
6398 SLOW_PUSH_THISV(cx, obj);
6400 END_CASE(JSOP_XMLNAME)
6402 BEGIN_CASE(JSOP_DESCENDANTS)
6403 BEGIN_CASE(JSOP_DELDESC)
6405 JSObject *obj;
6406 FETCH_OBJECT(cx, -2, obj);
6407 jsval rval = Jsvalify(regs.sp[-1]);
6408 if (!js_GetXMLDescendants(cx, obj, rval, &rval))
6409 goto error;
6411 if (op == JSOP_DELDESC) {
6412 regs.sp[-1] = Valueify(rval); /* set local root */
6413 if (!js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval)))
6414 goto error;
6415 rval = JSVAL_TRUE; /* always succeed */
6418 regs.sp--;
6419 regs.sp[-1] = Valueify(rval);
6421 END_CASE(JSOP_DESCENDANTS)
6424 BEGIN_CASE(JSOP_FILTER)
6426 * We push the hole value before jumping to [enditer] so we can detect the
6427 * first iteration and direct js_StepXMLListFilter to initialize filter's
6428 * state.
6430 PUSH_HOLE();
6431 len = GET_JUMP_OFFSET(regs.pc);
6432 JS_ASSERT(len > 0);
6433 END_VARLEN_CASE
6436 BEGIN_CASE(JSOP_ENDFILTER)
6438 bool cond = !regs.sp[-1].isMagic();
6439 if (cond) {
6440 /* Exit the "with" block left from the previous iteration. */
6441 js_LeaveWith(cx);
6443 if (!js_StepXMLListFilter(cx, cond))
6444 goto error;
6445 if (!regs.sp[-1].isNull()) {
6447 * Decrease sp after EnterWith returns as we use sp[-1] there to root
6448 * temporaries.
6450 JS_ASSERT(IsXML(regs.sp[-1]));
6451 if (!js_EnterWith(cx, -2, JSOP_ENDFILTER, JSOP_ENDFILTER_LENGTH))
6452 goto error;
6453 regs.sp--;
6454 len = GET_JUMP_OFFSET(regs.pc);
6455 JS_ASSERT(len < 0);
6456 BRANCH(len);
6458 regs.sp--;
6460 END_CASE(JSOP_ENDFILTER);
6462 BEGIN_CASE(JSOP_TOXML)
6464 Value rval = regs.sp[-1];
6465 JSObject *obj = js_ValueToXMLObject(cx, rval);
6466 if (!obj)
6467 goto error;
6468 regs.sp[-1].setObject(*obj);
6470 END_CASE(JSOP_TOXML)
6472 BEGIN_CASE(JSOP_TOXMLLIST)
6474 Value rval = regs.sp[-1];
6475 JSObject *obj = js_ValueToXMLListObject(cx, rval);
6476 if (!obj)
6477 goto error;
6478 regs.sp[-1].setObject(*obj);
6480 END_CASE(JSOP_TOXMLLIST)
6482 BEGIN_CASE(JSOP_XMLTAGEXPR)
6484 Value rval = regs.sp[-1];
6485 JSString *str = js_ValueToString(cx, rval);
6486 if (!str)
6487 goto error;
6488 regs.sp[-1].setString(str);
6490 END_CASE(JSOP_XMLTAGEXPR)
6492 BEGIN_CASE(JSOP_XMLELTEXPR)
6494 Value rval = regs.sp[-1];
6495 JSString *str;
6496 if (IsXML(rval)) {
6497 str = js_ValueToXMLString(cx, rval);
6498 } else {
6499 str = js_ValueToString(cx, rval);
6500 if (str)
6501 str = js_EscapeElementValue(cx, str);
6503 if (!str)
6504 goto error;
6505 regs.sp[-1].setString(str);
6507 END_CASE(JSOP_XMLELTEXPR)
6509 BEGIN_CASE(JSOP_XMLCDATA)
6511 JSAtom *atom;
6512 LOAD_ATOM(0, atom);
6513 JSString *str = ATOM_TO_STRING(atom);
6514 JSObject *obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_TEXT, NULL, str);
6515 if (!obj)
6516 goto error;
6517 PUSH_OBJECT(*obj);
6519 END_CASE(JSOP_XMLCDATA)
6521 BEGIN_CASE(JSOP_XMLCOMMENT)
6523 JSAtom *atom;
6524 LOAD_ATOM(0, atom);
6525 JSString *str = ATOM_TO_STRING(atom);
6526 JSObject *obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_COMMENT, NULL, str);
6527 if (!obj)
6528 goto error;
6529 PUSH_OBJECT(*obj);
6531 END_CASE(JSOP_XMLCOMMENT)
6533 BEGIN_CASE(JSOP_XMLPI)
6535 JSAtom *atom;
6536 LOAD_ATOM(0, atom);
6537 JSString *str = ATOM_TO_STRING(atom);
6538 Value rval = regs.sp[-1];
6539 JSString *str2 = rval.toString();
6540 JSObject *obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_PROCESSING_INSTRUCTION, str, str2);
6541 if (!obj)
6542 goto error;
6543 regs.sp[-1].setObject(*obj);
6545 END_CASE(JSOP_XMLPI)
6547 BEGIN_CASE(JSOP_GETFUNNS)
6549 Value rval;
6550 if (!js_GetFunctionNamespace(cx, &rval))
6551 goto error;
6552 PUSH_COPY(rval);
6554 END_CASE(JSOP_GETFUNNS)
6555 #endif /* JS_HAS_XML_SUPPORT */
6557 BEGIN_CASE(JSOP_ENTERBLOCK)
6559 JSObject *obj;
6560 LOAD_OBJECT(0, obj);
6561 JS_ASSERT(obj->isStaticBlock());
6562 JS_ASSERT(regs.fp->base() + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp);
6563 Value *vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj);
6564 JS_ASSERT(regs.sp < vp);
6565 JS_ASSERT(vp <= regs.fp->slots() + script->nslots);
6566 SetValueRangeToUndefined(regs.sp, vp);
6567 regs.sp = vp;
6569 #ifdef DEBUG
6571 * The young end of fp->scopeChain may omit blocks if we haven't closed
6572 * over them, but if there are any closure blocks on fp->scopeChain, they'd
6573 * better be (clones of) ancestors of the block we're entering now;
6574 * anything else we should have popped off fp->scopeChain when we left its
6575 * static scope.
6577 JSObject *obj2 = &regs.fp->scopeChain();
6578 Class *clasp;
6579 while ((clasp = obj2->getClass()) == &js_WithClass)
6580 obj2 = obj2->getParent();
6581 if (clasp == &js_BlockClass &&
6582 obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, regs.fp)) {
6583 JSObject *youngestProto = obj2->getProto();
6584 JS_ASSERT(youngestProto->isStaticBlock());
6585 JSObject *parent = obj;
6586 while ((parent = parent->getParent()) != youngestProto)
6587 JS_ASSERT(parent);
6589 #endif
6591 END_CASE(JSOP_ENTERBLOCK)
6593 BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
6594 BEGIN_CASE(JSOP_LEAVEBLOCK)
6596 JSObject *blockChain;
6597 LOAD_OBJECT(UINT16_LEN, blockChain);
6598 #ifdef DEBUG
6599 JS_ASSERT(blockChain->isStaticBlock());
6600 uintN blockDepth = OBJ_BLOCK_DEPTH(cx, blockChain);
6601 JS_ASSERT(blockDepth <= StackDepth(script));
6602 #endif
6604 * If we're about to leave the dynamic scope of a block that has been
6605 * cloned onto fp->scopeChain, clear its private data, move its locals from
6606 * the stack into the clone, and pop it off the chain.
6608 JSObject &obj = regs.fp->scopeChain();
6609 if (obj.getProto() == blockChain) {
6610 JS_ASSERT(obj.isClonedBlock());
6611 if (!js_PutBlockObject(cx, JS_TRUE))
6612 goto error;
6615 /* Move the result of the expression to the new topmost stack slot. */
6616 Value *vp = NULL; /* silence GCC warnings */
6617 if (op == JSOP_LEAVEBLOCKEXPR)
6618 vp = &regs.sp[-1];
6619 regs.sp -= GET_UINT16(regs.pc);
6620 if (op == JSOP_LEAVEBLOCKEXPR) {
6621 JS_ASSERT(regs.fp->base() + blockDepth == regs.sp - 1);
6622 regs.sp[-1] = *vp;
6623 } else {
6624 JS_ASSERT(regs.fp->base() + blockDepth == regs.sp);
6627 END_CASE(JSOP_LEAVEBLOCK)
6629 #if JS_HAS_GENERATORS
6630 BEGIN_CASE(JSOP_GENERATOR)
6632 JS_ASSERT(!cx->isExceptionPending());
6633 regs.pc += JSOP_GENERATOR_LENGTH;
6634 JSObject *obj = js_NewGenerator(cx);
6635 if (!obj)
6636 goto error;
6637 JS_ASSERT(!regs.fp->hasCallObj() && !regs.fp->hasArgsObj());
6638 regs.fp->setReturnValue(ObjectValue(*obj));
6639 interpReturnOK = true;
6640 if (entryFrame != regs.fp)
6641 goto inline_return;
6642 goto exit;
6645 BEGIN_CASE(JSOP_YIELD)
6646 JS_ASSERT(!cx->isExceptionPending());
6647 JS_ASSERT(regs.fp->isFunctionFrame() && !regs.fp->isEvalFrame());
6648 if (cx->generatorFor(regs.fp)->state == JSGEN_CLOSING) {
6649 js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD,
6650 JSDVG_SEARCH_STACK, argv[-2], NULL);
6651 goto error;
6653 regs.fp->setReturnValue(regs.sp[-1]);
6654 regs.fp->setYielding();
6655 regs.pc += JSOP_YIELD_LENGTH;
6656 interpReturnOK = JS_TRUE;
6657 goto exit;
6659 BEGIN_CASE(JSOP_ARRAYPUSH)
6661 uint32 slot = GET_UINT16(regs.pc);
6662 JS_ASSERT(script->nfixed <= slot);
6663 JS_ASSERT(slot < script->nslots);
6664 JSObject *obj = &regs.fp->slots()[slot].toObject();
6665 if (!js_ArrayCompPush(cx, obj, regs.sp[-1]))
6666 goto error;
6667 regs.sp--;
6669 END_CASE(JSOP_ARRAYPUSH)
6670 #endif /* JS_HAS_GENERATORS */
6672 #if JS_THREADED_INTERP
6673 L_JSOP_BACKPATCH:
6674 L_JSOP_BACKPATCH_POP:
6676 # if !JS_HAS_GENERATORS
6677 L_JSOP_GENERATOR:
6678 L_JSOP_YIELD:
6679 L_JSOP_ARRAYPUSH:
6680 # endif
6682 # if !JS_HAS_SHARP_VARS
6683 L_JSOP_DEFSHARP:
6684 L_JSOP_USESHARP:
6685 L_JSOP_SHARPINIT:
6686 # endif
6688 # if !JS_HAS_DESTRUCTURING
6689 L_JSOP_ENUMCONSTELEM:
6690 # endif
6692 # if !JS_HAS_XML_SUPPORT
6693 L_JSOP_CALLXMLNAME:
6694 L_JSOP_STARTXMLEXPR:
6695 L_JSOP_STARTXML:
6696 L_JSOP_DELDESC:
6697 L_JSOP_GETFUNNS:
6698 L_JSOP_XMLPI:
6699 L_JSOP_XMLCOMMENT:
6700 L_JSOP_XMLCDATA:
6701 L_JSOP_XMLELTEXPR:
6702 L_JSOP_XMLTAGEXPR:
6703 L_JSOP_TOXMLLIST:
6704 L_JSOP_TOXML:
6705 L_JSOP_ENDFILTER:
6706 L_JSOP_FILTER:
6707 L_JSOP_DESCENDANTS:
6708 L_JSOP_XMLNAME:
6709 L_JSOP_SETXMLNAME:
6710 L_JSOP_BINDXMLNAME:
6711 L_JSOP_ADDATTRVAL:
6712 L_JSOP_ADDATTRNAME:
6713 L_JSOP_TOATTRVAL:
6714 L_JSOP_TOATTRNAME:
6715 L_JSOP_QNAME:
6716 L_JSOP_QNAMECONST:
6717 L_JSOP_QNAMEPART:
6718 L_JSOP_ANYNAME:
6719 L_JSOP_DEFXMLNS:
6720 # endif
6722 #endif /* !JS_THREADED_INTERP */
6723 #if !JS_THREADED_INTERP
6724 default:
6725 #endif
6727 char numBuf[12];
6728 JS_snprintf(numBuf, sizeof numBuf, "%d", op);
6729 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6730 JSMSG_BAD_BYTECODE, numBuf);
6731 goto error;
6734 #if !JS_THREADED_INTERP
6735 } /* switch (op) */
6736 } /* for (;;) */
6737 #endif /* !JS_THREADED_INTERP */
6739 error:
6740 JS_ASSERT(cx->regs == &regs);
6741 #ifdef JS_TRACER
6742 if (regs.fp->hasImacropc() && cx->isExceptionPending()) {
6743 // Handle exceptions as if they came from the imacro-calling pc.
6744 regs.pc = regs.fp->imacropc();
6745 regs.fp->clearImacropc();
6747 #endif
6749 JS_ASSERT(size_t((regs.fp->hasImacropc() ? regs.fp->imacropc() : regs.pc) - script->code) <
6750 script->length);
6752 #ifdef JS_TRACER
6754 * This abort could be weakened to permit tracing through exceptions that
6755 * are thrown and caught within a loop, with the co-operation of the tracer.
6756 * For now just bail on any sign of trouble.
6758 if (TRACE_RECORDER(cx))
6759 AbortRecording(cx, "error or exception while recording");
6760 # ifdef JS_METHODJIT
6761 if (TRACE_PROFILER(cx))
6762 AbortProfiling(cx);
6763 # endif
6764 #endif
6766 if (!cx->isExceptionPending()) {
6767 /* This is an error, not a catchable exception, quit the frame ASAP. */
6768 interpReturnOK = JS_FALSE;
6769 } else {
6770 JSThrowHook handler;
6771 JSTryNote *tn, *tnlimit;
6772 uint32 offset;
6774 /* Restore atoms local in case we will resume. */
6775 atoms = script->atomMap.vector;
6777 /* Call debugger throw hook if set. */
6778 handler = cx->debugHooks->throwHook;
6779 if (handler) {
6780 Value rval;
6781 switch (handler(cx, script, regs.pc, Jsvalify(&rval),
6782 cx->debugHooks->throwHookData)) {
6783 case JSTRAP_ERROR:
6784 cx->clearPendingException();
6785 goto error;
6786 case JSTRAP_RETURN:
6787 cx->clearPendingException();
6788 regs.fp->setReturnValue(rval);
6789 interpReturnOK = JS_TRUE;
6790 goto forced_return;
6791 case JSTRAP_THROW:
6792 cx->setPendingException(rval);
6793 case JSTRAP_CONTINUE:
6794 default:;
6796 CHECK_INTERRUPT_HANDLER();
6800 * Look for a try block in script that can catch this exception.
6802 if (!JSScript::isValidOffset(script->trynotesOffset))
6803 goto no_catch;
6805 offset = (uint32)(regs.pc - script->main);
6806 tn = script->trynotes()->vector;
6807 tnlimit = tn + script->trynotes()->length;
6808 do {
6809 if (offset - tn->start >= tn->length)
6810 continue;
6813 * We have a note that covers the exception pc but we must check
6814 * whether the interpreter has already executed the corresponding
6815 * handler. This is possible when the executed bytecode
6816 * implements break or return from inside a for-in loop.
6818 * In this case the emitter generates additional [enditer] and
6819 * [gosub] opcodes to close all outstanding iterators and execute
6820 * the finally blocks. If such an [enditer] throws an exception,
6821 * its pc can still be inside several nested for-in loops and
6822 * try-finally statements even if we have already closed the
6823 * corresponding iterators and invoked the finally blocks.
6825 * To address this, we make [enditer] always decrease the stack
6826 * even when its implementation throws an exception. Thus already
6827 * executed [enditer] and [gosub] opcodes will have try notes
6828 * with the stack depth exceeding the current one and this
6829 * condition is what we use to filter them out.
6831 if (tn->stackDepth > regs.sp - regs.fp->base())
6832 continue;
6835 * Set pc to the first bytecode after the the try note to point
6836 * to the beginning of catch or finally or to [enditer] closing
6837 * the for-in loop.
6839 regs.pc = (script)->main + tn->start + tn->length;
6841 JSBool ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE);
6842 JS_ASSERT(regs.sp == regs.fp->base() + tn->stackDepth);
6843 if (!ok) {
6845 * Restart the handler search with updated pc and stack depth
6846 * to properly notify the debugger.
6848 goto error;
6851 switch (tn->kind) {
6852 case JSTRY_CATCH:
6853 #if JS_HAS_GENERATORS
6854 /* Catch cannot intercept the closing of a generator. */
6855 if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING)))
6856 break;
6857 #endif
6860 * Don't clear exceptions to save cx->exception from GC
6861 * until it is pushed to the stack via [exception] in the
6862 * catch block.
6864 len = 0;
6865 DO_NEXT_OP(len);
6867 case JSTRY_FINALLY:
6869 * Push (true, exception) pair for finally to indicate that
6870 * [retsub] should rethrow the exception.
6872 PUSH_BOOLEAN(true);
6873 PUSH_COPY(cx->getPendingException());
6874 cx->clearPendingException();
6875 len = 0;
6876 DO_NEXT_OP(len);
6878 case JSTRY_ITER: {
6879 /* This is similar to JSOP_ENDITER in the interpreter loop. */
6880 JS_ASSERT(js_GetOpcode(cx, regs.fp->script(), regs.pc) == JSOP_ENDITER);
6881 Value v = cx->getPendingException();
6882 cx->clearPendingException();
6883 ok = js_CloseIterator(cx, &regs.sp[-1].toObject());
6884 regs.sp -= 1;
6885 if (!ok)
6886 goto error;
6887 cx->setPendingException(v);
6890 } while (++tn != tnlimit);
6892 no_catch:
6894 * Propagate the exception or error to the caller unless the exception
6895 * is an asynchronous return from a generator.
6897 interpReturnOK = JS_FALSE;
6898 #if JS_HAS_GENERATORS
6899 if (JS_UNLIKELY(cx->isExceptionPending() &&
6900 cx->getPendingException().isMagic(JS_GENERATOR_CLOSING))) {
6901 cx->clearPendingException();
6902 interpReturnOK = JS_TRUE;
6903 regs.fp->clearReturnValue();
6905 #endif
6908 forced_return:
6910 * Unwind the scope making sure that interpReturnOK stays false even when
6911 * js_UnwindScope returns true.
6913 * When a trap handler returns JSTRAP_RETURN, we jump here with
6914 * interpReturnOK set to true bypassing any finally blocks.
6916 interpReturnOK &= js_UnwindScope(cx, 0, interpReturnOK || cx->isExceptionPending());
6917 JS_ASSERT(regs.sp == regs.fp->base());
6919 #ifdef DEBUG
6920 cx->logPrevPc = NULL;
6921 #endif
6923 if (entryFrame != regs.fp)
6924 goto inline_return;
6926 exit:
6927 interpReturnOK = ScriptEpilogue(cx, regs.fp, interpReturnOK);
6928 regs.fp->setFinishedInInterpreter();
6931 * At this point we are inevitably leaving an interpreted function or a
6932 * top-level script, and returning to one of:
6933 * (a) an "out of line" call made through js_Invoke;
6934 * (b) a js_Execute activation;
6935 * (c) a generator (SendToGenerator, jsiter.c).
6937 * We must not be in an inline frame. The check above ensures that for the
6938 * error case and for a normal return, the code jumps directly to parent's
6939 * frame pc.
6941 JS_ASSERT(entryFrame == regs.fp);
6943 #ifdef JS_TRACER
6944 JS_ASSERT_IF(interpReturnOK && interpMode == JSINTERP_RECORD, !TRACE_RECORDER(cx));
6945 if (TRACE_RECORDER(cx))
6946 AbortRecording(cx, "recording out of Interpret");
6947 # ifdef JS_METHODJIT
6948 if (TRACE_PROFILER(cx))
6949 AbortProfiling(cx);
6950 # endif
6951 #endif
6953 JS_ASSERT_IF(!regs.fp->isGeneratorFrame(), !js_IsActiveWithOrBlock(cx, &regs.fp->scopeChain(), 0));
6955 return interpReturnOK;
6957 atom_not_defined:
6959 JSAutoByteString printable;
6960 if (js_AtomToPrintableString(cx, atomNotDefined, &printable))
6961 js_ReportIsNotDefined(cx, printable.ptr());
6963 goto error;
6966 * This path is used when it's guaranteed the method can be finished
6967 * inside the JIT.
6969 #if defined(JS_METHODJIT)
6970 leave_on_safe_point:
6971 #endif
6972 return interpReturnOK;
6975 } /* namespace js */
6977 #endif /* !defined jsinvoke_cpp___ */