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