[JAEGER] Predict unbound names as being on the global object (bug 564949).
[mozilla-central.git] / js / src / methodjit / StubCalls.cpp
blob98a07ce8ff823b49354f88fd15bd290754bd77ce
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 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 SpiderMonkey JavaScript 1.9 code, released
18 * May 28, 2008.
20 * The Initial Developer of the Original Code is
21 * Brendan Eich <brendan@mozilla.org>
23 * Contributor(s):
24 * David Anderson <danderson@mozilla.com>
25 * David Mandelin <dmandelin@mozilla.com>
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 ***** */
41 #define __STDC_LIMIT_MACROS
43 #include "jscntxt.h"
44 #include "jsscope.h"
45 #include "jsobj.h"
46 #include "jslibmath.h"
47 #include "jsiter.h"
48 #include "jsnum.h"
49 #include "jsxml.h"
50 #include "jsstaticcheck.h"
51 #include "jsbool.h"
52 #include "assembler/assembler/MacroAssemblerCodeRef.h"
53 #include "jsiter.h"
54 #include "jstypes.h"
55 #include "methodjit/StubCalls.h"
56 #include "jstracer.h"
57 #include "jspropertycache.h"
58 #include "jspropertycacheinlines.h"
59 #include "jsscopeinlines.h"
60 #include "jsscriptinlines.h"
61 #include "jsstrinlines.h"
62 #include "jsobjinlines.h"
63 #include "jscntxtinlines.h"
64 #include "jsatominlines.h"
66 #include "jsautooplen.h"
68 using namespace js;
69 using namespace js::mjit;
70 using namespace JSC;
72 #define THROW() \
73 do { \
74 void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
75 f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr))); \
76 return; \
77 } while (0)
79 #define THROWV(v) \
80 do { \
81 void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
82 f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr))); \
83 return v; \
84 } while (0)
86 void JS_FASTCALL
87 mjit::stubs::BindName(VMFrame &f)
89 PropertyCacheEntry *entry;
91 /* Fast-path should have caught this. See comment in interpreter. */
92 JS_ASSERT(f.fp->scopeChainObj()->getParent());
94 JSAtom *atom;
95 JSObject *obj2;
96 JSContext *cx = f.cx;
97 JSObject *obj = f.fp->scopeChainObj();
98 JS_PROPERTY_CACHE(cx).test(cx, f.regs.pc, obj, obj2, entry, atom);
99 if (atom) {
100 jsid id = ATOM_TO_JSID(atom);
101 obj = js_FindIdentifierBase(cx, f.fp->scopeChainObj(), id);
102 if (!obj)
103 THROW();
105 f.regs.sp++;
106 f.regs.sp[-1].setNonFunObj(*obj);
109 static bool
110 InlineReturn(JSContext *cx)
112 bool ok = true;
114 JSStackFrame *fp = cx->fp;
116 JS_ASSERT(!fp->blockChain);
117 JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChainObj(), 0));
119 if (fp->script->staticLevel < JS_DISPLAY_SIZE)
120 cx->display[fp->script->staticLevel] = fp->displaySave;
122 // Marker for debug support.
123 void *hookData = fp->hookData;
124 if (JS_UNLIKELY(hookData != NULL)) {
125 JSInterpreterHook hook;
126 JSBool status;
128 hook = cx->debugHooks->callHook;
129 if (hook) {
131 * Do not pass &ok directly as exposing the address inhibits
132 * optimizations and uninitialised warnings.
134 status = ok;
135 hook(cx, fp, JS_FALSE, &status, hookData);
136 ok = (status == JS_TRUE);
137 // CHECK_INTERRUPT_HANDLER();
141 fp->putActivationObjects(cx);
143 /* :TODO: version stuff */
145 if (fp->flags & JSFRAME_CONSTRUCTING && fp->rval.isPrimitive())
146 fp->rval = fp->thisv;
148 cx->stack().popInlineFrame(cx, fp, fp->down);
149 cx->regs->sp[-1] = fp->rval;
151 return ok;
154 void * JS_FASTCALL
155 mjit::stubs::Return(VMFrame &f)
157 if (!f.inlineCallCount)
158 return f.fp->ncode;
160 JSContext *cx = f.cx;
161 JS_ASSERT(f.fp == cx->fp);
163 #ifdef DEBUG
164 bool wasInterp = f.fp->script->ncode == JS_UNJITTABLE_METHOD;
165 #endif
167 bool ok = InlineReturn(cx);
169 f.inlineCallCount--;
170 JS_ASSERT(f.regs.sp == cx->regs->sp);
171 f.fp = cx->fp;
173 JS_ASSERT_IF(f.inlineCallCount > 1 && !wasInterp,
174 f.fp->down->script->isValidJitCode(f.fp->ncode));
176 if (!ok)
177 THROWV(NULL);
179 return f.fp->ncode;
182 static jsbytecode *
183 FindExceptionHandler(JSContext *cx)
185 JSStackFrame *fp = cx->fp;
186 JSScript *script = fp->script;
188 top:
189 if (cx->throwing && script->trynotesOffset) {
190 // The PC is updated before every stub call, so we can use it here.
191 unsigned offset = cx->regs->pc - script->main;
193 JSTryNoteArray *tnarray = script->trynotes();
194 for (unsigned i = 0; i < tnarray->length; ++i) {
195 JSTryNote *tn = &tnarray->vector[i];
196 if (offset - tn->start >= tn->length)
197 continue;
198 if (tn->stackDepth > cx->regs->sp - fp->base())
199 continue;
201 jsbytecode *pc = script->main + tn->start + tn->length;
202 JSBool ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE);
203 JS_ASSERT(cx->regs->sp == fp->base() + tn->stackDepth);
205 switch (tn->kind) {
206 case JSTRY_CATCH:
207 JS_ASSERT(js_GetOpcode(cx, fp->script, pc) == JSOP_ENTERBLOCK);
209 #if JS_HAS_GENERATORS
210 /* Catch cannot intercept the closing of a generator. */
211 if (JS_UNLIKELY(cx->exception.isMagic(JS_GENERATOR_CLOSING)))
212 break;
213 #endif
216 * Don't clear cx->throwing to save cx->exception from GC
217 * until it is pushed to the stack via [exception] in the
218 * catch block.
220 return pc;
222 case JSTRY_FINALLY:
224 * Push (true, exception) pair for finally to indicate that
225 * [retsub] should rethrow the exception.
227 cx->regs->sp[0].setBoolean(true);
228 cx->regs->sp[1] = cx->exception;
229 cx->regs->sp += 2;
230 cx->throwing = JS_FALSE;
231 return pc;
233 case JSTRY_ITER:
236 * This is similar to JSOP_ENDITER in the interpreter loop,
237 * except the code now uses the stack slot normally used by
238 * JSOP_NEXTITER, namely regs.sp[-1] before the regs.sp -= 2
239 * adjustment and regs.sp[1] after, to save and restore the
240 * pending exception.
242 AutoValueRooter tvr(cx, cx->exception);
243 JS_ASSERT(js_GetOpcode(cx, fp->script, pc) == JSOP_ENDITER);
244 cx->throwing = JS_FALSE;
245 ok = !!js_CloseIterator(cx, cx->regs->sp[-1]);
246 cx->regs->sp -= 1;
247 if (!ok)
248 goto top;
249 cx->throwing = JS_TRUE;
250 cx->exception = tvr.value();
256 return NULL;
259 extern "C" void *
260 js_InternalThrow(VMFrame &f)
262 JSContext *cx = f.cx;
264 // Make sure sp is up to date.
265 JS_ASSERT(cx->regs == &f.regs);
267 jsbytecode *pc = NULL;
268 for (;;) {
269 pc = FindExceptionHandler(cx);
270 if (pc)
271 break;
273 // If |f.inlineCallCount == 0|, then we are on the 'topmost' frame (where
274 // topmost means the first frame called into through js_Interpret). In this
275 // case, we still unwind, but we shouldn't return from a JS function, because
276 // we're not in a JS function.
277 bool lastFrame = (f.inlineCallCount == 0);
278 js_UnwindScope(cx, 0, cx->throwing);
279 if (lastFrame)
280 break;
282 JS_ASSERT(f.regs.sp == cx->regs->sp);
283 f.scriptedReturn = stubs::Return(f);
286 JS_ASSERT(f.regs.sp == cx->regs->sp);
288 if (!pc) {
289 *f.oldRegs = f.regs;
290 f.cx->setCurrentRegs(f.oldRegs);
291 return NULL;
294 return cx->fp->script->pcToNative(pc);
297 #define NATIVE_SET(cx,obj,sprop,entry,vp) \
298 JS_BEGIN_MACRO \
299 if (sprop->hasDefaultSetter() && \
300 (sprop)->slot != SPROP_INVALID_SLOT && \
301 !obj->scope()->brandedOrHasMethodBarrier()) { \
302 /* Fast path for, e.g., plain Object instance properties. */ \
303 obj->setSlot(sprop->slot, *vp); \
304 } else { \
305 if (!js_NativeSet(cx, obj, sprop, false, vp)) \
306 THROW(); \
308 JS_END_MACRO
310 static inline JSObject *
311 ValueToObject(JSContext *cx, Value *vp)
313 if (vp->isObject())
314 return &vp->asObject();
315 if (!js_ValueToNonNullObject(cx, *vp, vp))
316 return NULL;
317 return &vp->asObject();
320 #define NATIVE_GET(cx,obj,pobj,sprop,getHow,vp,onerr) \
321 JS_BEGIN_MACRO \
322 if (sprop->hasDefaultGetter()) { \
323 /* Fast path for Object instance properties. */ \
324 JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT || \
325 !sprop->hasDefaultSetter()); \
326 if (((sprop)->slot != SPROP_INVALID_SLOT)) \
327 *(vp) = (pobj)->lockedGetSlot((sprop)->slot); \
328 else \
329 (vp)->setUndefined(); \
330 } else { \
331 if (!js_NativeGet(cx, obj, pobj, sprop, getHow, vp)) \
332 onerr; \
334 JS_END_MACRO
336 void JS_FASTCALL
337 mjit::stubs::SetName(VMFrame &f, JSAtom *origAtom)
339 JSContext *cx = f.cx;
341 Value &rref = f.regs.sp[-1];
342 Value &lref = f.regs.sp[-2];
343 JSObject *obj = ValueToObject(cx, &lref);
344 if (!obj)
345 THROW();
347 do {
348 PropertyCache *cache = &JS_PROPERTY_CACHE(cx);
351 * Probe the property cache, specializing for two important
352 * set-property cases. First:
354 * function f(a, b, c) {
355 * var o = {p:a, q:b, r:c};
356 * return o;
359 * or similar real-world cases, which evolve a newborn native
360 * object predicatably through some bounded number of property
361 * additions. And second:
363 * o.p = x;
365 * in a frequently executed method or loop body, where p will
366 * (possibly after the first iteration) always exist in native
367 * object o.
369 PropertyCacheEntry *entry;
370 JSObject *obj2;
371 JSAtom *atom;
372 if (cache->testForSet(cx, f.regs.pc, obj, &entry, &obj2, &atom)) {
374 * Fast property cache hit, only partially confirmed by
375 * testForSet. We know that the entry applies to regs.pc and
376 * that obj's shape matches.
378 * The entry predicts either a new property to be added
379 * directly to obj by this set, or on an existing "own"
380 * property, or on a prototype property that has a setter.
382 JS_ASSERT(entry->vword.isSprop());
383 JSScopeProperty *sprop = entry->vword.toSprop();
384 JS_ASSERT_IF(sprop->isDataDescriptor(), sprop->writable());
385 JS_ASSERT_IF(sprop->hasSlot(), entry->vcapTag() == 0);
387 JSScope *scope = obj->scope();
388 JS_ASSERT(!scope->sealed());
391 * Fastest path: check whether the cached sprop is already
392 * in scope and call NATIVE_SET and break to get out of the
393 * do-while(0). But we can call NATIVE_SET only if obj owns
394 * scope or sprop is shared.
396 bool checkForAdd;
397 if (!sprop->hasSlot()) {
398 if (entry->vcapTag() == 0 ||
399 ((obj2 = obj->getProto()) &&
400 obj2->isNative() &&
401 obj2->shape() == entry->vshape())) {
402 goto fast_set_propcache_hit;
405 /* The cache entry doesn't apply. vshape mismatch. */
406 checkForAdd = false;
407 } else if (!scope->isSharedEmpty()) {
408 if (sprop == scope->lastProperty() || scope->hasProperty(sprop)) {
409 fast_set_propcache_hit:
410 PCMETER(cache->pchits++);
411 PCMETER(cache->setpchits++);
412 NATIVE_SET(cx, obj, sprop, entry, &rref);
413 break;
415 checkForAdd = sprop->hasSlot() && sprop->parent == scope->lastProperty();
416 } else {
418 * We check that cx own obj here and will continue to
419 * own it after js_GetMutableScope returns so we can
420 * continue to skip JS_UNLOCK_OBJ calls.
422 JS_ASSERT(CX_OWNS_OBJECT_TITLE(cx, obj));
423 scope = js_GetMutableScope(cx, obj);
424 JS_ASSERT(CX_OWNS_OBJECT_TITLE(cx, obj));
425 if (!scope)
426 THROW();
427 checkForAdd = !sprop->parent;
430 uint32 slot;
431 if (checkForAdd &&
432 entry->vshape() == cx->runtime->protoHazardShape &&
433 sprop->hasDefaultSetter() &&
434 (slot = sprop->slot) == scope->freeslot) {
436 * Fast path: adding a plain old property that was once
437 * at the frontier of the property tree, whose slot is
438 * next to claim among the allocated slots in obj,
439 * where scope->table has not been created yet.
441 * We may want to remove hazard conditions above and
442 * inline compensation code here, depending on
443 * real-world workloads.
445 PCMETER(cache->pchits++);
446 PCMETER(cache->addpchits++);
449 * Beware classes such as Function that use the
450 * reserveSlots hook to allocate a number of reserved
451 * slots that may vary with obj.
453 if (slot < obj->numSlots() &&
454 !obj->getClass()->reserveSlots) {
455 ++scope->freeslot;
456 } else {
457 if (!js_AllocSlot(cx, obj, &slot))
458 THROW();
462 * If this obj's number of reserved slots differed, or
463 * if something created a hash table for scope, we must
464 * pay the price of JSScope::putProperty.
466 * (A reserveSlots hook can cause scopes of the same
467 * shape to have different freeslot values. This is
468 * what causes the slot != sprop->slot case. See
469 * js_GetMutableScope.)
471 if (slot != sprop->slot || scope->table) {
472 JSScopeProperty *sprop2 =
473 scope->putProperty(cx, sprop->id,
474 sprop->getter(), sprop->setter(),
475 slot, sprop->attributes(),
476 sprop->getFlags(), sprop->shortid);
477 if (!sprop2) {
478 js_FreeSlot(cx, obj, slot);
479 THROW();
481 sprop = sprop2;
482 } else {
483 scope->extend(cx, sprop);
487 * No method change check here because here we are
488 * adding a new property, not updating an existing
489 * slot's value that might contain a method of a
490 * branded scope.
492 obj->lockedSetSlot(slot, rref);
495 * Purge the property cache of the id we may have just
496 * shadowed in obj's scope and proto chains. We do this
497 * after unlocking obj's scope to avoid lock nesting.
499 js_PurgeScopeChain(cx, obj, sprop->id);
500 break;
502 PCMETER(cache->setpcmisses++);
503 atom = NULL;
504 } else if (!atom) {
506 * Slower property cache hit, fully confirmed by testForSet (in
507 * the slow path, via fullTest).
509 JSScopeProperty *sprop = NULL;
510 if (obj == obj2) {
511 sprop = entry->vword.toSprop();
512 JS_ASSERT(sprop->writable());
513 JS_ASSERT(!obj2->scope()->sealed());
514 NATIVE_SET(cx, obj, sprop, entry, &rref);
516 if (sprop)
517 break;
520 if (!atom)
521 atom = origAtom;
522 jsid id = ATOM_TO_JSID(atom);
523 if (entry && JS_LIKELY(obj->map->ops->setProperty == js_SetProperty)) {
524 uintN defineHow;
525 JSOp op = JSOp(*f.regs.pc);
526 if (op == JSOP_SETMETHOD)
527 defineHow = JSDNP_CACHE_RESULT | JSDNP_SET_METHOD;
528 else if (op == JSOP_SETNAME)
529 defineHow = JSDNP_CACHE_RESULT | JSDNP_UNQUALIFIED;
530 else
531 defineHow = JSDNP_CACHE_RESULT;
532 if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rref))
533 THROW();
534 } else {
535 if (!obj->setProperty(cx, id, &rref))
536 THROW();
538 } while (0);
540 f.regs.sp[-2] = f.regs.sp[-1];
543 void JS_FASTCALL
544 stubs::SetGlobalName(VMFrame &f, JSAtom *atom)
546 SetName(f, atom);
549 static void
550 ReportAtomNotDefined(JSContext *cx, JSAtom *atom)
552 const char *printable = js_AtomToPrintableString(cx, atom);
553 if (printable)
554 js_ReportIsNotDefined(cx, printable);
557 static JSObject *
558 NameOp(VMFrame &f, JSObject *obj)
560 JSContext *cx = f.cx;
562 JSScopeProperty *sprop;
563 Value rval;
565 PropertyCacheEntry *entry;
566 JSObject *obj2;
567 JSAtom *atom;
568 JS_PROPERTY_CACHE(cx).test(cx, f.regs.pc, obj, obj2, entry, atom);
569 if (!atom) {
570 if (entry->vword.isFunObj()) {
571 f.regs.sp++;
572 f.regs.sp[-1].setFunObj(entry->vword.toFunObj());
573 return obj;
576 if (entry->vword.isSlot()) {
577 uintN slot = entry->vword.toSlot();
578 JS_ASSERT(slot < obj2->scope()->freeslot);
579 f.regs.sp++;
580 f.regs.sp[-1] = obj2->lockedGetSlot(slot);
581 return obj;
584 JS_ASSERT(entry->vword.isSprop());
585 sprop = entry->vword.toSprop();
586 goto do_native_get;
589 jsid id;
590 id = ATOM_TO_JSID(atom);
591 JSProperty *prop;
592 if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
593 return NULL;
594 if (!prop) {
595 /* Kludge to allow (typeof foo == "undefined") tests. */
596 JSOp op2 = js_GetOpcode(cx, f.fp->script, f.regs.pc + JSOP_NAME_LENGTH);
597 if (op2 == JSOP_TYPEOF) {
598 f.regs.sp++;
599 f.regs.sp[-1].setUndefined();
600 return obj;
602 ReportAtomNotDefined(cx, atom);
603 return NULL;
606 /* Take the slow path if prop was not found in a native object. */
607 if (!obj->isNative() || !obj2->isNative()) {
608 obj2->dropProperty(cx, prop);
609 if (!obj->getProperty(cx, id, &rval))
610 return NULL;
611 } else {
612 sprop = (JSScopeProperty *)prop;
613 do_native_get:
614 NATIVE_GET(cx, obj, obj2, sprop, JSGET_METHOD_BARRIER, &rval, return NULL);
615 obj2->dropProperty(cx, (JSProperty *) sprop);
618 f.regs.sp++;
619 f.regs.sp[-1] = rval;
620 return obj;
623 void JS_FASTCALL
624 stubs::Name(VMFrame &f)
626 if (!NameOp(f, f.fp->scopeChainObj()))
627 THROW();
630 void JS_FASTCALL
631 stubs::GetGlobalName(VMFrame &f)
633 JSObject *globalObj = f.fp->scopeChainObj()->getGlobal();
634 if (!NameOp(f, globalObj))
635 THROW();
638 static inline bool
639 IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
641 if (iterobj->getClass() == &js_IteratorClass.base) {
642 NativeIterator *ni = (NativeIterator *) iterobj->getPrivate();
643 JS_ASSERT(ni->props_cursor < ni->props_end);
644 *rval = ID_TO_VALUE(*ni->props_cursor);
645 if (rval->isString() || (ni->flags & JSITER_FOREACH)) {
646 ni->props_cursor++;
647 return true;
649 /* Take the slow path if we have to stringify a numeric property name. */
651 return js_IteratorNext(cx, iterobj, rval);
654 void JS_FASTCALL
655 stubs::ForName(VMFrame &f, JSAtom *atom)
657 JSContext *cx = f.cx;
658 JSFrameRegs &regs = f.regs;
660 JS_ASSERT(regs.sp - 1 >= f.fp->base());
661 jsid id = ATOM_TO_JSID(atom);
662 JSObject *obj, *obj2;
663 JSProperty *prop;
664 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
665 THROW();
666 if (prop)
667 obj2->dropProperty(cx, prop);
669 AutoValueRooter tvr(cx);
670 JS_ASSERT(regs.sp[-1].isObject());
671 if (!IteratorNext(cx, &regs.sp[-1].asObject(), tvr.addr()))
672 THROW();
673 if (!obj->setProperty(cx, id, tvr.addr()))
674 THROW();
678 void JS_FASTCALL
679 stubs::GetElem(VMFrame &f)
681 JSContext *cx = f.cx;
682 JSFrameRegs &regs = f.regs;
684 Value lval = regs.sp[-2];
685 Value rval = regs.sp[-1];
686 const Value *copyFrom;
688 JSObject *obj;
689 jsid id;
690 int i;
692 if (lval.isString() && rval.isInt32()) {
693 Value retval;
694 JSString *str = lval.asString();
695 i = rval.asInt32();
697 if ((size_t)i >= str->length())
698 THROW();
700 str = JSString::getUnitString(cx, str, (size_t)i);
701 if (!str)
702 THROW();
703 f.regs.sp[-2].setString(str);
704 return;
707 obj = ValueToObject(cx, &lval);
708 if (!obj)
709 THROW();
711 if (rval.isInt32()) {
712 if (obj->isDenseArray()) {
713 jsuint idx = jsuint(rval.asInt32());
715 if (idx < obj->getArrayLength() &&
716 idx < obj->getDenseArrayCapacity()) {
717 copyFrom = obj->addressOfDenseArrayElement(idx);
718 if (!copyFrom->isMagic())
719 goto end_getelem;
720 /* Otherwise, fall to getProperty(). */
722 } else if (obj->isArguments()
723 #if 0 /* def JS_TRACER */
724 && !GetArgsPrivateNative(obj)
725 #endif
727 uint32 arg = uint32(rval.asInt32());
729 if (arg < obj->getArgsLength()) {
730 JSStackFrame *afp = (JSStackFrame *) obj->getPrivate();
731 if (afp) {
732 copyFrom = &afp->argv[arg];
733 goto end_getelem;
736 copyFrom = obj->addressOfArgsElement(arg);
737 if (!copyFrom->isMagic())
738 goto end_getelem;
739 /* Otherwise, fall to getProperty(). */
742 id = INT_TO_JSID(rval.asInt32());
744 } else {
745 if (!js_InternNonIntElementId(cx, obj, rval, &id))
746 THROW();
749 if (!obj->getProperty(cx, id, &rval))
750 THROW();
751 copyFrom = &rval;
753 end_getelem:
754 f.regs.sp[-2] = *copyFrom;
757 static inline bool
758 FetchElementId(VMFrame &f, JSObject *obj, const Value &idval, jsid &id, Value *vp)
760 int32_t i_;
761 if (ValueFitsInInt32(idval, &i_)) {
762 id = INT_TO_JSID(i_);
763 return true;
765 return !!js_InternNonIntElementId(f.cx, obj, idval, &id, vp);
768 void JS_FASTCALL
769 stubs::CallElem(VMFrame &f)
771 JSContext *cx = f.cx;
772 JSFrameRegs &regs = f.regs;
774 /* Fetch the left part and resolve it to a non-null object. */
775 JSObject *obj = ValueToObject(cx, &regs.sp[-2]);
776 if (!obj)
777 THROW();
779 /* Fetch index and convert it to id suitable for use with obj. */
780 jsid id;
781 if (!FetchElementId(f, obj, regs.sp[-1], id, &regs.sp[-2]))
782 THROW();
784 /* Get or set the element. */
785 if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &regs.sp[-2]))
786 THROW();
788 #if JS_HAS_NO_SUCH_METHOD
789 if (JS_UNLIKELY(regs.sp[-2].isUndefined())) {
790 regs.sp[-2] = regs.sp[-1];
791 regs.sp[-1].setObject(*obj);
792 if (!js_OnUnknownMethod(cx, regs.sp - 2))
793 THROW();
794 } else
795 #endif
797 regs.sp[-1].setObject(*obj);
801 void JS_FASTCALL
802 stubs::SetElem(VMFrame &f)
804 JSContext *cx = f.cx;
805 JSFrameRegs &regs = f.regs;
807 Value &objval = regs.sp[-3];
808 Value &idval = regs.sp[-2];
809 Value retval = regs.sp[-1];
811 JSObject *obj;
812 jsid id;
814 obj = ValueToObject(cx, &objval);
815 if (!obj)
816 THROW();
818 if (!FetchElementId(f, obj, idval, id, &regs.sp[-2]))
819 THROW();
821 if (obj->isDenseArray() && JSID_IS_INT(id)) {
822 jsuint length = obj->getDenseArrayCapacity();
823 jsint i = JSID_TO_INT(id);
825 if ((jsuint)i < length) {
826 if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
827 if (js_PrototypeHasIndexedProperties(cx, obj))
828 goto mid_setelem;
829 if ((jsuint)i >= obj->getArrayLength())
830 obj->setDenseArrayLength(i + 1);
831 obj->incDenseArrayCountBy(1);
833 obj->setDenseArrayElement(i, regs.sp[-1]);
834 goto end_setelem;
838 mid_setelem:
839 if (!obj->setProperty(cx, id, &regs.sp[-1]))
840 THROW();
842 end_setelem:
843 /* :FIXME: Moving the assigned object into the lowest stack slot
844 * is a temporary hack. What we actually want is an implementation
845 * of popAfterSet() that allows popping more than one value;
846 * this logic can then be handled in Compiler.cpp. */
847 regs.sp[-3] = retval;
850 void JS_FASTCALL
851 stubs::CallName(VMFrame &f)
853 JSObject *obj = NameOp(f, f.fp->scopeChainObj());
854 if (!obj)
855 THROW();
856 f.regs.sp++;
857 f.regs.sp[-1].setNonFunObj(*obj);
860 void JS_FASTCALL
861 stubs::BitOr(VMFrame &f)
863 int32_t i, j;
865 if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i) ||
866 !ValueToECMAInt32(f.cx, f.regs.sp[-1], &j)) {
867 THROW();
869 i = i | j;
870 f.regs.sp[-2].setInt32(i);
873 void JS_FASTCALL
874 stubs::BitXor(VMFrame &f)
876 int32_t i, j;
878 if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i) ||
879 !ValueToECMAInt32(f.cx, f.regs.sp[-1], &j)) {
880 THROW();
882 i = i ^ j;
883 f.regs.sp[-2].setInt32(i);
886 void JS_FASTCALL
887 stubs::BitAnd(VMFrame &f)
889 int32_t i, j;
891 if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i) ||
892 !ValueToECMAInt32(f.cx, f.regs.sp[-1], &j)) {
893 THROW();
895 i = i & j;
896 f.regs.sp[-2].setInt32(i);
899 void JS_FASTCALL
900 stubs::BitNot(VMFrame &f)
902 int32_t i;
904 if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &i))
905 THROW();
906 i = ~i;
907 f.regs.sp[-1].setInt32(i);
910 void JS_FASTCALL
911 stubs::Lsh(VMFrame &f)
913 int32_t i, j;
914 if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i))
915 THROW();
916 if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &j))
917 THROW();
918 i = i << (j & 31);
919 f.regs.sp[-2].setInt32(i);
922 void JS_FASTCALL
923 stubs::Rsh(VMFrame &f)
925 int32_t i, j;
926 if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i))
927 THROW();
928 if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &j))
929 THROW();
930 i = i >> (j & 31);
931 f.regs.sp[-2].setInt32(i);
934 void JS_FASTCALL
935 stubs::Ursh(VMFrame &f)
937 uint32_t u;
938 if (!ValueToECMAUint32(f.cx, f.regs.sp[-2], &u))
939 THROW();
940 int32_t j;
941 if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &j))
942 THROW();
944 u >>= (j & 31);
946 f.regs.sp[-2].setNumber(uint32(u));
949 template <int32 N>
950 static inline bool
951 PostInc(VMFrame &f, Value *vp)
953 double d;
954 if (!ValueToNumber(f.cx, *vp, &d))
955 return false;
956 f.regs.sp++;
957 f.regs.sp[-1].setDouble(d);
958 d += N;
959 vp->setDouble(d);
960 return true;
963 template <int32 N>
964 static inline bool
965 PreInc(VMFrame &f, Value *vp)
967 double d;
968 if (!ValueToNumber(f.cx, *vp, &d))
969 return false;
970 d += N;
971 vp->setDouble(d);
972 f.regs.sp++;
973 f.regs.sp[-1].setDouble(d);
974 return true;
977 void JS_FASTCALL
978 stubs::VpInc(VMFrame &f, Value *vp)
980 if (!PostInc<1>(f, vp))
981 THROW();
984 void JS_FASTCALL
985 stubs::VpDec(VMFrame &f, Value *vp)
987 if (!PostInc<-1>(f, vp))
988 THROW();
991 void JS_FASTCALL
992 stubs::DecVp(VMFrame &f, Value *vp)
994 if (!PreInc<-1>(f, vp))
995 THROW();
998 void JS_FASTCALL
999 stubs::IncVp(VMFrame &f, Value *vp)
1001 if (!PreInc<1>(f, vp))
1002 THROW();
1005 static inline bool
1006 DoInvoke(VMFrame &f, uint32 argc, Value *vp)
1008 JSContext *cx = f.cx;
1010 if (vp->isFunObj()) {
1011 JSObject *obj = &vp->asFunObj();
1012 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj);
1014 JS_ASSERT(!FUN_INTERPRETED(fun));
1015 if (fun->flags & JSFUN_FAST_NATIVE) {
1016 JS_ASSERT(fun->u.n.extra == 0);
1017 JS_ASSERT(vp[1].isObjectOrNull() || PrimitiveValue::test(fun, vp[1]));
1018 JSBool ok = ((FastNative)fun->u.n.native)(cx, argc, vp);
1019 f.regs.sp = vp + 1;
1020 return ok ? true : false;
1024 JSBool ok = Invoke(cx, InvokeArgsGuard(vp, argc), 0);
1025 f.regs.sp = vp + 1;
1026 return ok ? true : false;
1029 static inline bool
1030 InlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc)
1032 JSContext *cx = f.cx;
1033 JSStackFrame *fp = f.fp;
1034 Value *vp = f.regs.sp - (argc + 2);
1035 JSObject *funobj = &vp->asFunObj();
1036 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
1038 JS_ASSERT(FUN_INTERPRETED(fun));
1040 JSScript *newscript = fun->u.i.script;
1042 if (f.inlineCallCount >= JS_MAX_INLINE_CALL_COUNT) {
1043 js_ReportOverRecursed(cx);
1044 return false;
1047 /* Allocate the frame. */
1048 StackSpace &stack = cx->stack();
1049 uintN nslots = newscript->nslots;
1050 uintN funargs = fun->nargs;
1051 Value *argv = vp + 2;
1052 JSStackFrame *newfp;
1053 if (argc < funargs) {
1054 uintN missing = funargs - argc;
1055 newfp = stack.getInlineFrame(cx, f.regs.sp, missing, nslots);
1056 if (!newfp)
1057 return false;
1058 for (Value *v = argv + argc, *end = v + missing; v != end; ++v)
1059 v->setUndefined();
1060 } else {
1061 newfp = stack.getInlineFrame(cx, f.regs.sp, 0, nslots);
1062 if (!newfp)
1063 return false;
1066 /* Initialize the frame. */
1067 newfp->ncode = NULL;
1068 newfp->callobj = NULL;
1069 newfp->argsval.setNull();
1070 newfp->script = newscript;
1071 newfp->fun = fun;
1072 newfp->argc = argc;
1073 newfp->argv = vp + 2;
1074 newfp->rval.setUndefined();
1075 newfp->annotation = NULL;
1076 newfp->scopeChain.setNonFunObj(*funobj->getParent());
1077 newfp->flags = flags;
1078 newfp->blockChain = NULL;
1079 JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags));
1080 newfp->thisv = vp[1];
1081 newfp->imacpc = NULL;
1083 /* Push void to initialize local variables. */
1084 Value *newslots = newfp->slots();
1085 Value *newsp = newslots + fun->u.i.nvars;
1086 for (Value *v = newslots; v != newsp; ++v)
1087 v->setUndefined();
1089 /* Scope with a call object parented by callee's parent. */
1090 if (fun->isHeavyweight() && !js_GetCallObject(cx, newfp))
1091 return false;
1093 /* :TODO: Switch version if currentVersion wasn't overridden. */
1094 newfp->callerVersion = (JSVersion)cx->version;
1096 // Marker for debug support.
1097 if (JSInterpreterHook hook = cx->debugHooks->callHook) {
1098 newfp->hookData = hook(cx, fp, JS_TRUE, 0,
1099 cx->debugHooks->callHookData);
1100 // CHECK_INTERRUPT_HANDLER();
1101 } else {
1102 newfp->hookData = NULL;
1105 f.inlineCallCount++;
1106 f.fp = newfp;
1107 stack.pushInlineFrame(cx, fp, cx->regs->pc, newfp);
1109 if (newscript->staticLevel < JS_DISPLAY_SIZE) {
1110 JSStackFrame **disp = &cx->display[newscript->staticLevel];
1111 newfp->displaySave = *disp;
1112 *disp = newfp;
1115 f.regs.pc = newscript->code;
1116 f.regs.sp = newsp;
1118 if (cx->options & JSOPTION_METHODJIT) {
1119 if (!newscript->ncode) {
1120 if (mjit::TryCompile(cx, newscript, fun, newfp->scopeChainObj()) == Compile_Error)
1121 return false;
1123 JS_ASSERT(newscript->ncode);
1124 if (newscript->ncode != JS_UNJITTABLE_METHOD) {
1125 fp->ncode = f.scriptedReturn;
1126 *pret = newscript->ncode;
1127 return true;
1131 bool ok = !!Interpret(cx); //, newfp, f.inlineCallCount);
1132 stubs::Return(f);
1134 *pret = NULL;
1135 return ok;
1138 void * JS_FASTCALL
1139 stubs::Call(VMFrame &f, uint32 argc)
1141 Value *vp = f.regs.sp - (argc + 2);
1143 if (vp->isFunObj()) {
1144 JSObject *obj = &vp->asFunObj();
1145 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj);
1146 if (FUN_INTERPRETED(fun)) {
1147 if (fun->u.i.script->isEmpty()) {
1148 vp->setUndefined();
1149 f.regs.sp = vp + 1;
1150 return NULL;
1153 void *ret;
1154 if (!InlineCall(f, 0, &ret, argc))
1155 THROWV(NULL);
1157 f.cx->regs->pc = f.fp->script->code;
1159 #if 0 /* def JS_TRACER */
1160 if (ret && f.cx->jitEnabled && IsTraceableRecursion(f.cx)) {
1161 /* Top of script should always have traceId 0. */
1162 f.u.tracer.traceId = 0;
1163 f.u.tracer.offs = 0;
1165 /* cx.regs.sp is only set in InlineCall() if non-jittable. */
1166 JS_ASSERT(f.cx->regs == &f.regs);
1169 * NB: Normally, the function address is returned, and the
1170 * caller's JIT'd code will set f.scriptedReturn and jump.
1171 * Invoking the tracer breaks this in two ways:
1172 * 1) f.scriptedReturn is not yet set, so when pushing new
1173 * inline frames, the call stack would get corrupted.
1174 * 2) If the tracer does not push new frames, but runs some
1175 * code, the JIT'd code to set f.scriptedReturn will not
1176 * be run.
1178 * So, a simple hack: set f.scriptedReturn now.
1180 f.scriptedReturn = GetReturnAddress(f, f.fp);
1182 void *newRet = InvokeTracer(f, Record_Recursion);
1185 * The tracer could have dropped us off anywhere. Hijack the
1186 * stub return address to JaegerFromTracer, which will restore
1187 * state correctly.
1189 if (newRet) {
1190 void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerFromTracer);
1191 f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr)));
1192 return newRet;
1195 #endif
1197 return ret;
1201 if (!DoInvoke(f, argc, vp))
1202 THROWV(NULL);
1204 return NULL;
1207 void * JS_FASTCALL
1208 stubs::New(VMFrame &f, uint32 argc)
1210 JSContext *cx = f.cx;
1211 Value *vp = f.regs.sp - (argc + 2);
1213 if (vp[0].isFunObj()) {
1214 JSObject *funobj = &vp[0].asFunObj();
1215 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
1216 if (fun->isInterpreted()) {
1217 jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
1218 if (!funobj->getProperty(cx, id, &vp[1]))
1219 THROWV(NULL);
1221 JSObject *proto = vp[1].isObject() ? &vp[1].asObject() : NULL;
1222 JSObject *obj2 = NewObject(cx, &js_ObjectClass, proto, funobj->getParent());
1223 if (!obj2)
1224 THROWV(NULL);
1226 if (fun->u.i.script->isEmpty()) {
1227 vp[0].setNonFunObj(*obj2);
1228 f.regs.sp = vp + 1;
1229 return NULL;
1232 vp[1].setNonFunObj(*obj2);
1233 void *pret;
1234 if (!InlineCall(f, JSFRAME_CONSTRUCTING, &pret, argc))
1235 THROWV(NULL);
1236 return pret;
1240 if (!InvokeConstructor(cx, InvokeArgsGuard(vp, argc), JS_FALSE))
1241 THROWV(NULL);
1243 f.regs.sp = vp + 1;
1244 return NULL;
1247 void JS_FASTCALL
1248 stubs::DefFun(VMFrame &f, uint32 index)
1250 bool doSet;
1251 JSObject *pobj, *obj2;
1252 JSProperty *prop;
1253 uint32 old;
1255 JSContext *cx = f.cx;
1256 JSStackFrame *fp = f.fp;
1257 JSScript *script = fp->script;
1260 * A top-level function defined in Global or Eval code (see ECMA-262
1261 * Ed. 3), or else a SpiderMonkey extension: a named function statement in
1262 * a compound statement (not at the top statement level of global code, or
1263 * at the top level of a function body).
1265 JSFunction *fun = script->getFunction(index);
1266 JSObject *obj = FUN_OBJECT(fun);
1268 if (FUN_NULL_CLOSURE(fun)) {
1270 * Even a null closure needs a parent for principals finding.
1271 * FIXME: bug 476950, although debugger users may also demand some kind
1272 * of scope link for debugger-assisted eval-in-frame.
1274 obj2 = fp->scopeChainObj();
1275 } else {
1276 JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
1279 * Inline js_GetScopeChain a bit to optimize for the case of a
1280 * top-level function.
1282 if (!fp->blockChain) {
1283 obj2 = fp->scopeChainObj();
1284 } else {
1285 obj2 = js_GetScopeChain(cx, fp);
1286 if (!obj2)
1287 THROW();
1292 * If static link is not current scope, clone fun's object to link to the
1293 * current scope via parent. We do this to enable sharing of compiled
1294 * functions among multiple equivalent scopes, amortizing the cost of
1295 * compilation over a number of executions. Examples include XUL scripts
1296 * and event handlers shared among Firefox or other Mozilla app chrome
1297 * windows, and user-defined JS functions precompiled and then shared among
1298 * requests in server-side JS.
1300 if (obj->getParent() != obj2) {
1301 obj = CloneFunctionObject(cx, fun, obj2);
1302 if (!obj)
1303 THROW();
1307 * Protect obj from any GC hiding below JSObject::setProperty or
1308 * JSObject::defineProperty. All paths from here must flow through the
1309 * fp->scopeChain code below the parent->defineProperty call.
1311 MUST_FLOW_THROUGH("restore_scope");
1312 fp->setScopeChainObj(obj);
1314 Value rval;
1315 rval.setFunObj(*obj);
1318 * ECMA requires functions defined when entering Eval code to be
1319 * impermanent.
1321 uintN attrs;
1322 attrs = (fp->flags & JSFRAME_EVAL)
1323 ? JSPROP_ENUMERATE
1324 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
1327 * Load function flags that are also property attributes. Getters and
1328 * setters do not need a slot, their value is stored elsewhere in the
1329 * property itself, not in obj slots.
1331 PropertyOp getter, setter;
1332 uintN flags;
1334 getter = setter = PropertyStub;
1335 flags = JSFUN_GSFLAG2ATTR(fun->flags);
1336 if (flags) {
1337 /* Function cannot be both getter a setter. */
1338 JS_ASSERT(flags == JSPROP_GETTER || flags == JSPROP_SETTER);
1339 attrs |= flags | JSPROP_SHARED;
1340 rval.setUndefined();
1341 if (flags == JSPROP_GETTER)
1342 getter = CastAsPropertyOp(obj);
1343 else
1344 setter = CastAsPropertyOp(obj);
1347 jsid id;
1348 JSBool ok;
1349 JSObject *parent;
1352 * We define the function as a property of the variable object and not the
1353 * current scope chain even for the case of function expression statements
1354 * and functions defined by eval inside let or with blocks.
1356 parent = fp->varobj(cx);
1357 JS_ASSERT(parent);
1360 * Check for a const property of the same name -- or any kind of property
1361 * if executing with the strict option. We check here at runtime as well
1362 * as at compile-time, to handle eval as well as multiple HTML script tags.
1364 id = ATOM_TO_JSID(fun->atom);
1365 prop = NULL;
1366 ok = CheckRedeclaration(cx, parent, id, attrs, &pobj, &prop);
1367 if (!ok)
1368 goto restore_scope;
1371 * We deviate from 10.1.2 in ECMA 262 v3 and under eval use for function
1372 * declarations JSObject::setProperty, not JSObject::defineProperty, to
1373 * preserve the JSOP_PERMANENT attribute of existing properties and make
1374 * sure that such properties cannot be deleted.
1376 * We also use JSObject::setProperty for the existing properties of Call
1377 * objects with matching attributes to preserve the native getters and
1378 * setters that store the value of the property in the interpreter frame,
1379 * see bug 467495.
1381 doSet = (attrs == JSPROP_ENUMERATE);
1382 JS_ASSERT_IF(doSet, fp->flags & JSFRAME_EVAL);
1383 if (prop) {
1384 if (parent == pobj &&
1385 parent->getClass() == &js_CallClass &&
1386 (old = ((JSScopeProperty *) prop)->attributes(),
1387 !(old & (JSPROP_GETTER|JSPROP_SETTER)) &&
1388 (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs)) {
1390 * js_CheckRedeclaration must reject attempts to add a getter or
1391 * setter to an existing property without a getter or setter.
1393 JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT)));
1394 JS_ASSERT(!(old & JSPROP_READONLY));
1395 doSet = JS_TRUE;
1397 pobj->dropProperty(cx, prop);
1399 ok = doSet
1400 ? parent->setProperty(cx, id, &rval)
1401 : parent->defineProperty(cx, id, rval, getter, setter, attrs);
1403 restore_scope:
1404 /* Restore fp->scopeChain now that obj is defined in fp->callobj. */
1405 fp->setScopeChainObj(obj2);
1406 if (!ok)
1407 THROW();
1410 #define DEFAULT_VALUE(cx, n, hint, v) \
1411 JS_BEGIN_MACRO \
1412 JS_ASSERT(v.isObject()); \
1413 JS_ASSERT(v == regs.sp[n]); \
1414 if (!v.asObject().defaultValue(cx, hint, &regs.sp[n])) \
1415 THROWV(JS_FALSE); \
1416 v = regs.sp[n]; \
1417 JS_END_MACRO
1419 #define RELATIONAL(OP) \
1420 JS_BEGIN_MACRO \
1421 JSContext *cx = f.cx; \
1422 JSFrameRegs &regs = f.regs; \
1423 Value rval = regs.sp[-1]; \
1424 Value lval = regs.sp[-2]; \
1425 bool cond; \
1426 if (lval.isObject()) \
1427 DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \
1428 if (rval.isObject()) \
1429 DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \
1430 if (BothString(lval, rval)) { \
1431 JSString *l = lval.asString(), *r = rval.asString(); \
1432 cond = js_CompareStrings(l, r) OP 0; \
1433 } else { \
1434 double l, r; \
1435 if (!ValueToNumber(cx, lval, &l) || \
1436 !ValueToNumber(cx, rval, &r)) { \
1437 THROWV(JS_FALSE); \
1439 cond = JSDOUBLE_COMPARE(l, OP, r, false); \
1441 regs.sp[-2].setBoolean(cond); \
1442 return cond; \
1443 JS_END_MACRO
1445 JSBool JS_FASTCALL
1446 stubs::LessThan(VMFrame &f)
1448 RELATIONAL(<);
1451 JSBool JS_FASTCALL
1452 stubs::LessEqual(VMFrame &f)
1454 RELATIONAL(<=);
1457 JSBool JS_FASTCALL
1458 stubs::GreaterThan(VMFrame &f)
1460 RELATIONAL(>);
1463 JSBool JS_FASTCALL
1464 stubs::GreaterEqual(VMFrame &f)
1466 RELATIONAL(>=);
1469 JSBool JS_FASTCALL
1470 stubs::ValueToBoolean(VMFrame &f)
1472 return js_ValueToBoolean(f.regs.sp[-1]);
1475 void JS_FASTCALL
1476 stubs::Not(VMFrame &f)
1478 JSBool b = !js_ValueToBoolean(f.regs.sp[-1]);
1479 f.regs.sp[-1].setBoolean(b);
1483 * Inline copy of jsops.cpp:EQUALITY_OP().
1484 * @param op true if for JSOP_EQ; false for JSOP_NE.
1485 * @param ifnan return value upon NaN comparison.
1487 static inline bool
1488 InlineEqualityOp(VMFrame &f, bool op, bool ifnan)
1490 Class *clasp;
1491 JSContext *cx = f.cx;
1492 JSFrameRegs &regs = f.regs;
1494 Value rval = regs.sp[-1];
1495 Value lval = regs.sp[-2];
1497 JSBool jscond;
1498 bool cond;
1500 #if JS_HAS_XML_SUPPORT
1501 /* Inline copy of jsops.cpp:XML_EQUALITY_OP() */
1502 if ((lval.isNonFunObj() && lval.asObject().isXML()) ||
1503 (rval.isNonFunObj() && rval.asObject().isXML())) {
1504 if (!js_TestXMLEquality(cx, lval, rval, &jscond))
1505 THROWV(false);
1506 cond = (jscond == JS_TRUE) == op;
1507 } else
1508 #endif /* JS_HAS_XML_SUPPORT */
1510 if (SamePrimitiveTypeOrBothObjects(lval, rval)) {
1511 if (lval.isString()) {
1512 JSString *l = lval.asString();
1513 JSString *r = rval.asString();
1514 cond = js_EqualStrings(l, r) == op;
1515 } else if (lval.isDouble()) {
1516 double l = lval.asDouble();
1517 double r = rval.asDouble();
1518 if (op) {
1519 cond = JSDOUBLE_COMPARE(l, ==, r, ifnan);
1520 } else {
1521 cond = JSDOUBLE_COMPARE(l, !=, r, ifnan);
1523 } else {
1524 /* jsops.cpp:EXTENDED_EQUALITY_OP() */
1525 JSObject *lobj;
1526 if (lval.isObject() &&
1527 (lobj = &lval.asObject()) &&
1528 ((clasp = lobj->getClass())->flags & JSCLASS_IS_EXTENDED) &&
1529 ((ExtendedClass *)clasp)->equality) {
1530 if (!((ExtendedClass *)clasp)->equality(cx, lobj, lval, &jscond))
1531 THROWV(false);
1532 cond = (jscond == JS_TRUE) == op;
1533 } else {
1534 cond = (lval.asRawUint32() == rval.asRawUint32()) == op;
1537 } else {
1538 if (lval.isNullOrUndefined()) {
1539 cond = rval.isNullOrUndefined() == op;
1540 } else if (rval.isNullOrUndefined()) {
1541 cond = !op;
1542 } else {
1543 if (lval.isObject()) {
1544 if (!lval.asObject().defaultValue(cx, JSTYPE_VOID, &regs.sp[-2]))
1545 THROWV(false);
1546 lval = regs.sp[-2];
1549 if (rval.isObject()) {
1550 if (!rval.asObject().defaultValue(cx, JSTYPE_VOID, &regs.sp[-1]))
1551 THROWV(false);
1552 rval = regs.sp[-1];
1555 if (BothString(lval, rval)) {
1556 JSString *l = lval.asString();
1557 JSString *r = rval.asString();
1558 cond = js_EqualStrings(l, r) == op;
1559 } else {
1560 double l, r;
1561 if (!ValueToNumber(cx, lval, &l) ||
1562 !ValueToNumber(cx, rval, &r)) {
1563 THROWV(false);
1566 if (op)
1567 cond = JSDOUBLE_COMPARE(l, ==, r, ifnan);
1568 else
1569 cond = JSDOUBLE_COMPARE(l, !=, r, ifnan);
1574 regs.sp[-2].setBoolean(cond);
1575 return cond;
1578 JSBool JS_FASTCALL
1579 stubs::Equal(VMFrame &f)
1581 return InlineEqualityOp(f, true, false);
1584 JSBool JS_FASTCALL
1585 stubs::NotEqual(VMFrame &f)
1587 return InlineEqualityOp(f, false, true);
1590 static inline bool
1591 DefaultValue(VMFrame &f, JSType hint, Value &v, int n)
1593 JS_ASSERT(v.isObject());
1594 if (!v.asObject().defaultValue(f.cx, hint, &f.regs.sp[n]))
1595 return false;
1596 v = f.regs.sp[n];
1597 return true;
1600 void JS_FASTCALL
1601 stubs::Add(VMFrame &f)
1603 JSContext *cx = f.cx;
1604 JSFrameRegs &regs = f.regs;
1605 Value rval = regs.sp[-1];
1606 Value lval = regs.sp[-2];
1608 if (BothInt32(lval, rval)) {
1609 int32_t l = lval.asInt32(), r = rval.asInt32();
1610 int32_t sum = l + r;
1611 regs.sp--;
1612 if (JS_UNLIKELY(bool((l ^ sum) & (r ^ sum) & 0x80000000)))
1613 regs.sp[-1].setDouble(double(l) + double(r));
1614 else
1615 regs.sp[-1].setInt32(sum);
1616 } else
1617 #if JS_HAS_XML_SUPPORT
1618 if (lval.isNonFunObj() && lval.asObject().isXML() &&
1619 rval.isNonFunObj() && rval.asObject().isXML()) {
1620 if (!js_ConcatenateXML(cx, &lval.asObject(), &rval.asObject(), &rval))
1621 THROW();
1622 regs.sp--;
1623 regs.sp[-1] = rval;
1624 } else
1625 #endif
1627 if (lval.isObject() && !DefaultValue(f, JSTYPE_VOID, lval, -2))
1628 THROW();
1629 if (rval.isObject() && !DefaultValue(f, JSTYPE_VOID, rval, -1))
1630 THROW();
1631 bool lIsString, rIsString;
1632 if ((lIsString = lval.isString()) | (rIsString = rval.isString())) {
1633 JSString *lstr, *rstr;
1634 if (lIsString) {
1635 lstr = lval.asString();
1636 } else {
1637 lstr = js_ValueToString(cx, lval);
1638 if (!lstr)
1639 THROW();
1640 regs.sp[-2].setString(lstr);
1642 if (rIsString) {
1643 rstr = rval.asString();
1644 } else {
1645 rstr = js_ValueToString(cx, rval);
1646 if (!rstr)
1647 THROW();
1648 regs.sp[-1].setString(rstr);
1650 JSString *str = js_ConcatStrings(cx, lstr, rstr);
1651 if (!str)
1652 THROW();
1653 regs.sp--;
1654 regs.sp[-1].setString(str);
1655 } else {
1656 double l, r;
1657 if (!ValueToNumber(cx, lval, &l) || !ValueToNumber(cx, rval, &r))
1658 THROW();
1659 l += r;
1660 regs.sp--;
1661 regs.sp[-1].setNumber(l);
1667 void JS_FASTCALL
1668 stubs::Sub(VMFrame &f)
1670 JSContext *cx = f.cx;
1671 JSFrameRegs &regs = f.regs;
1672 double d1, d2;
1673 if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
1674 !ValueToNumber(cx, regs.sp[-1], &d2)) {
1675 THROW();
1677 double d = d1 - d2;
1678 regs.sp[-2].setNumber(d);
1681 void JS_FASTCALL
1682 stubs::Mul(VMFrame &f)
1684 JSContext *cx = f.cx;
1685 JSFrameRegs &regs = f.regs;
1686 double d1, d2;
1687 if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
1688 !ValueToNumber(cx, regs.sp[-1], &d2)) {
1689 THROW();
1691 double d = d1 * d2;
1692 regs.sp[-2].setNumber(d);
1695 void JS_FASTCALL
1696 stubs::Div(VMFrame &f)
1698 JSContext *cx = f.cx;
1699 JSRuntime *rt = cx->runtime;
1700 JSFrameRegs &regs = f.regs;
1702 double d1, d2;
1703 if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
1704 !ValueToNumber(cx, regs.sp[-1], &d2)) {
1705 THROW();
1707 if (d2 == 0) {
1708 const Value *vp;
1709 #ifdef XP_WIN
1710 /* XXX MSVC miscompiles such that (NaN == 0) */
1711 if (JSDOUBLE_IS_NaN(d2))
1712 vp = &rt->NaNValue;
1713 else
1714 #endif
1715 if (d1 == 0 || JSDOUBLE_IS_NaN(d1))
1716 vp = &rt->NaNValue;
1717 else if (JSDOUBLE_IS_NEG(d1) != JSDOUBLE_IS_NEG(d2))
1718 vp = &rt->negativeInfinityValue;
1719 else
1720 vp = &rt->positiveInfinityValue;
1721 regs.sp[-2] = *vp;
1722 } else {
1723 d1 /= d2;
1724 regs.sp[-2].setNumber(d1);
1728 void JS_FASTCALL
1729 stubs::Mod(VMFrame &f)
1731 JSContext *cx = f.cx;
1732 JSFrameRegs &regs = f.regs;
1734 Value &lref = regs.sp[-2];
1735 Value &rref = regs.sp[-1];
1736 int32_t l, r;
1737 if (lref.isInt32() && rref.isInt32() &&
1738 (l = lref.asInt32()) >= 0 && (r = rref.asInt32()) > 0) {
1739 int32_t mod = l % r;
1740 regs.sp[-2].setInt32(mod);
1741 } else {
1742 double d1, d2;
1743 if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
1744 !ValueToNumber(cx, regs.sp[-1], &d2)) {
1745 THROW();
1747 if (d2 == 0) {
1748 regs.sp[-2].setDouble(js_NaN);
1749 } else {
1750 d1 = js_fmod(d1, d2);
1751 regs.sp[-2].setDouble(d1);
1756 JSObject *JS_FASTCALL
1757 stubs::NewArray(VMFrame &f, uint32 len)
1759 JSObject *obj = js_NewArrayObject(f.cx, len, f.regs.sp - len, JS_TRUE);
1760 if (!obj)
1761 THROWV(NULL);
1762 return obj;
1765 void JS_FASTCALL
1766 stubs::Interrupt(VMFrame &f)
1768 if (!js_HandleExecutionInterrupt(f.cx)) {
1769 THROW();
1773 void JS_FASTCALL
1774 stubs::This(VMFrame &f)
1776 if (!f.fp->getThisObject(f.cx))
1777 THROW();
1778 f.regs.sp[0] = f.fp->thisv;
1781 void JS_FASTCALL
1782 stubs::Neg(VMFrame &f)
1784 double d;
1785 if (!ValueToNumber(f.cx, f.regs.sp[-1], &d))
1786 THROW();
1787 d = -d;
1788 f.regs.sp[-1].setNumber(d);
1791 void JS_FASTCALL
1792 stubs::ObjToStr(VMFrame &f)
1794 const Value &ref = f.regs.sp[-1];
1795 if (ref.isObject()) {
1796 JSString *str = js_ValueToString(f.cx, ref);
1797 if (!str)
1798 THROW();
1799 f.regs.sp[-1].setString(str);
1803 JSObject * JS_FASTCALL
1804 stubs::NewInitArray(VMFrame &f)
1806 JSObject *obj = js_NewArrayObject(f.cx, 0, NULL);
1807 if (!obj)
1808 THROWV(NULL);
1809 return obj;
1812 JSObject * JS_FASTCALL
1813 stubs::NewInitObject(VMFrame &f, uint32 empty)
1815 JSContext *cx = f.cx;
1817 JSObject *obj = NewObject(cx, &js_ObjectClass, NULL, NULL);
1818 if (!obj)
1819 THROWV(NULL);
1821 if (!empty) {
1822 JS_LOCK_OBJ(cx, obj);
1823 JSScope *scope = js_GetMutableScope(cx, obj);
1824 if (!scope) {
1825 JS_UNLOCK_OBJ(cx, obj);
1826 THROWV(NULL);
1830 * We cannot assume that js_GetMutableScope above creates a scope
1831 * owned by cx and skip JS_UNLOCK_SCOPE. A new object debugger
1832 * hook may add properties to the newly created object, suspend
1833 * the current request and share the object with other threads.
1835 JS_UNLOCK_SCOPE(cx, scope);
1838 return obj;
1841 void JS_FASTCALL
1842 stubs::EndInit(VMFrame &f)
1844 JS_ASSERT(f.regs.sp - f.fp->base() >= 1);
1845 const Value &lref = f.regs.sp[-1];
1846 JS_ASSERT(lref.isObject());
1847 f.cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] = &lref.asObject();
1850 void JS_FASTCALL
1851 stubs::InitElem(VMFrame &f, uint32 last)
1853 JSContext *cx = f.cx;
1854 JSFrameRegs &regs = f.regs;
1856 /* Pop the element's value into rval. */
1857 JS_ASSERT(regs.sp - f.fp->base() >= 3);
1858 const Value &rref = regs.sp[-1];
1860 /* Find the object being initialized at top of stack. */
1861 const Value &lref = regs.sp[-3];
1862 JS_ASSERT(lref.isObject());
1863 JSObject *obj = &lref.asObject();
1865 /* Fetch id now that we have obj. */
1866 jsid id;
1867 const Value &idval = regs.sp[-2];
1868 if (!FetchElementId(f, obj, idval, id, &regs.sp[-2]))
1869 THROW();
1872 * Check for property redeclaration strict warning (we may be in an object
1873 * initialiser, not an array initialiser).
1875 if (!CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER, NULL, NULL))
1876 THROW();
1879 * If rref is a hole, do not call JSObject::defineProperty. In this case,
1880 * obj must be an array, so if the current op is the last element
1881 * initialiser, set the array length to one greater than id.
1883 if (rref.isMagic(JS_ARRAY_HOLE)) {
1884 JS_ASSERT(obj->isArray());
1885 JS_ASSERT(JSID_IS_INT(id));
1886 JS_ASSERT(jsuint(JSID_TO_INT(id)) < JS_ARGS_LENGTH_MAX);
1887 if (last && !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1)))
1888 THROW();
1889 } else {
1890 if (!obj->defineProperty(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE))
1891 THROW();
1895 void JS_FASTCALL
1896 stubs::GetUpvar(VMFrame &f, uint32 cookie)
1898 /* :FIXME: We can do better, this stub isn't needed. */
1899 uint32 staticLevel = f.fp->script->staticLevel;
1900 f.regs.sp[0] = js_GetUpvar(f.cx, staticLevel, cookie);
1903 JSObject * JS_FASTCALL
1904 stubs::DefLocalFun(VMFrame &f, JSFunction *fun)
1907 * Define a local function (i.e., one nested at the top level of another
1908 * function), parented by the current scope chain, stored in a local
1909 * variable slot that the compiler allocated. This is an optimization over
1910 * JSOP_DEFFUN that avoids requiring a call object for the outer function's
1911 * activation.
1913 JS_ASSERT(fun->isInterpreted());
1914 JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
1915 JSObject *obj = FUN_OBJECT(fun);
1917 if (FUN_NULL_CLOSURE(fun)) {
1918 obj = CloneFunctionObject(f.cx, fun, f.fp->scopeChainObj());
1919 if (!obj)
1920 THROWV(NULL);
1921 } else {
1922 JSObject *parent = js_GetScopeChain(f.cx, f.fp);
1923 if (!parent)
1924 THROWV(NULL);
1926 if (obj->getParent() != parent) {
1927 obj = CloneFunctionObject(f.cx, fun, parent);
1928 if (!obj)
1929 THROWV(NULL);
1933 return obj;
1936 JSObject * JS_FASTCALL
1937 stubs::RegExp(VMFrame &f, JSObject *regex)
1940 * Push a regexp object cloned from the regexp literal object mapped by the
1941 * bytecode at pc. ES5 finally fixed this bad old ES3 design flaw which was
1942 * flouted by many browser-based implementations.
1944 * We avoid the js_GetScopeChain call here and pass fp->scopeChain as
1945 * js_GetClassPrototype uses the latter only to locate the global.
1947 JSObject *proto;
1948 if (!js_GetClassPrototype(f.cx, f.fp->scopeChainObj(), JSProto_RegExp, &proto))
1949 THROWV(NULL);
1950 JS_ASSERT(proto);
1951 JSObject *obj = js_CloneRegExpObject(f.cx, regex, proto);
1952 if (!obj)
1953 THROWV(NULL);
1954 return obj;
1957 JSObject * JS_FASTCALL
1958 stubs::Lambda(VMFrame &f, JSFunction *fun)
1960 JSObject *obj = FUN_OBJECT(fun);
1962 JSObject *parent;
1963 if (FUN_NULL_CLOSURE(fun)) {
1964 parent = f.fp->scopeChainObj();
1965 } else {
1966 parent = js_GetScopeChain(f.cx, f.fp);
1967 if (!parent)
1968 THROWV(NULL);
1971 obj = CloneFunctionObject(f.cx, fun, parent);
1972 if (!obj)
1973 THROWV(NULL);
1975 return obj;
1978 /* Test whether v is an int in the range [-2^31 + 1, 2^31 - 2] */
1979 static JS_ALWAYS_INLINE bool
1980 CanIncDecWithoutOverflow(int32_t i)
1982 return (i > JSVAL_INT_MIN) && (i < JSVAL_INT_MAX);
1985 template <int32 N, bool POST>
1986 static inline bool
1987 ObjIncOp(VMFrame &f, JSObject *obj, jsid id)
1989 JSContext *cx = f.cx;
1990 JSStackFrame *fp = f.fp;
1992 f.regs.sp[0].setNull();
1993 f.regs.sp++;
1994 if (!obj->getProperty(cx, id, &f.regs.sp[-1]))
1995 return false;
1997 Value &ref = f.regs.sp[-1];
1998 int32_t tmp;
1999 if (JS_LIKELY(ref.isInt32() && CanIncDecWithoutOverflow(tmp = ref.asInt32()))) {
2000 if (POST)
2001 ref.asInt32Ref() = tmp + N;
2002 else
2003 ref.asInt32Ref() = tmp += N;
2004 fp->flags |= JSFRAME_ASSIGNING;
2005 JSBool ok = obj->setProperty(cx, id, &ref);
2006 fp->flags &= ~JSFRAME_ASSIGNING;
2007 if (!ok)
2008 return false;
2011 * We must set regs.sp[-1] to tmp for both post and pre increments
2012 * as the setter overwrites regs.sp[-1].
2014 ref.setInt32(tmp);
2015 } else {
2016 Value v;
2017 double d;
2018 if (!ValueToNumber(cx, ref, &d))
2019 return false;
2020 if (POST) {
2021 ref.setDouble(d);
2022 d += N;
2023 } else {
2024 d += N;
2025 ref.setDouble(d);
2027 v.setDouble(d);
2028 fp->flags |= JSFRAME_ASSIGNING;
2029 JSBool ok = obj->setProperty(cx, id, &v);
2030 fp->flags &= ~JSFRAME_ASSIGNING;
2031 if (!ok)
2032 return false;
2035 return true;
2038 template <int32 N, bool POST>
2039 static inline bool
2040 NameIncDec(VMFrame &f, JSObject *obj, JSAtom *origAtom)
2042 JSContext *cx = f.cx;
2044 JSAtom *atom;
2045 JSObject *obj2;
2046 JSProperty *prop;
2047 PropertyCacheEntry *entry;
2048 JS_PROPERTY_CACHE(cx).test(cx, f.regs.pc, obj, obj2, entry, atom);
2049 if (!atom) {
2050 if (obj == obj2 && entry->vword.isSlot()) {
2051 uint32 slot = entry->vword.toSlot();
2052 JS_ASSERT(slot < obj->scope()->freeslot);
2053 Value &rref = obj->getSlotRef(slot);
2054 int32_t tmp;
2055 if (JS_LIKELY(rref.isInt32() && CanIncDecWithoutOverflow(tmp = rref.asInt32()))) {
2056 int32_t inc = tmp + N;
2057 if (!POST)
2058 tmp = inc;
2059 rref.asInt32Ref() = inc;
2060 f.regs.sp[0].setInt32(tmp);
2061 return true;
2064 atom = origAtom;
2067 jsid id = ATOM_TO_JSID(atom);
2068 if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
2069 return false;
2070 if (!prop) {
2071 ReportAtomNotDefined(cx, atom);
2072 return false;
2074 obj2->dropProperty(cx, prop);
2075 return ObjIncOp<N, POST>(f, obj, id);
2078 void JS_FASTCALL
2079 stubs::PropInc(VMFrame &f, JSAtom *atom)
2081 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
2082 if (!obj)
2083 THROW();
2084 if (!ObjIncOp<1, true>(f, obj, ATOM_TO_JSID(atom)))
2085 THROW();
2086 f.regs.sp[-2] = f.regs.sp[-1];
2089 void JS_FASTCALL
2090 stubs::PropDec(VMFrame &f, JSAtom *atom)
2092 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
2093 if (!obj)
2094 THROW();
2095 if (!ObjIncOp<-1, true>(f, obj, ATOM_TO_JSID(atom)))
2096 THROW();
2097 f.regs.sp[-2] = f.regs.sp[-1];
2100 void JS_FASTCALL
2101 stubs::IncProp(VMFrame &f, JSAtom *atom)
2103 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
2104 if (!obj)
2105 THROW();
2106 if (!ObjIncOp<1, false>(f, obj, ATOM_TO_JSID(atom)))
2107 THROW();
2108 f.regs.sp[-2] = f.regs.sp[-1];
2111 void JS_FASTCALL
2112 stubs::DecProp(VMFrame &f, JSAtom *atom)
2114 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
2115 if (!obj)
2116 THROW();
2117 if (!ObjIncOp<-1, false>(f, obj, ATOM_TO_JSID(atom)))
2118 THROW();
2119 f.regs.sp[-2] = f.regs.sp[-1];
2122 void JS_FASTCALL
2123 stubs::ElemInc(VMFrame &f)
2125 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
2126 if (!obj)
2127 THROW();
2128 jsid id;
2129 if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
2130 THROW();
2131 if (!ObjIncOp<1, true>(f, obj, id))
2132 THROW();
2133 f.regs.sp[-3] = f.regs.sp[-1];
2136 void JS_FASTCALL
2137 stubs::ElemDec(VMFrame &f)
2139 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
2140 if (!obj)
2141 THROW();
2142 jsid id;
2143 if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
2144 THROW();
2145 if (!ObjIncOp<-1, true>(f, obj, id))
2146 THROW();
2147 f.regs.sp[-3] = f.regs.sp[-1];
2150 void JS_FASTCALL
2151 stubs::IncElem(VMFrame &f)
2153 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
2154 if (!obj)
2155 THROW();
2156 jsid id;
2157 if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
2158 THROW();
2159 if (!ObjIncOp<1, false>(f, obj, id))
2160 THROW();
2161 f.regs.sp[-3] = f.regs.sp[-1];
2164 void JS_FASTCALL
2165 stubs::DecElem(VMFrame &f)
2167 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
2168 if (!obj)
2169 THROW();
2170 jsid id;
2171 if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
2172 THROW();
2173 if (!ObjIncOp<-1, false>(f, obj, id))
2174 THROW();
2175 f.regs.sp[-3] = f.regs.sp[-1];
2178 void JS_FASTCALL
2179 stubs::NameInc(VMFrame &f, JSAtom *atom)
2181 JSObject *obj = f.fp->scopeChainObj();
2182 if (!NameIncDec<1, true>(f, obj, atom))
2183 THROW();
2186 void JS_FASTCALL
2187 stubs::NameDec(VMFrame &f, JSAtom *atom)
2189 JSObject *obj = f.fp->scopeChainObj();
2190 if (!NameIncDec<-1, true>(f, obj, atom))
2191 THROW();
2194 void JS_FASTCALL
2195 stubs::IncName(VMFrame &f, JSAtom *atom)
2197 JSObject *obj = f.fp->scopeChainObj();
2198 if (!NameIncDec<1, false>(f, obj, atom))
2199 THROW();
2202 void JS_FASTCALL
2203 stubs::DecName(VMFrame &f, JSAtom *atom)
2205 JSObject *obj = f.fp->scopeChainObj();
2206 if (!NameIncDec<-1, false>(f, obj, atom))
2207 THROW();
2210 void JS_FASTCALL
2211 stubs::GlobalNameInc(VMFrame &f, JSAtom *atom)
2213 JSObject *obj = f.fp->scopeChainObj()->getGlobal();
2214 if (!NameIncDec<1, true>(f, obj, atom))
2215 THROW();
2218 void JS_FASTCALL
2219 stubs::GlobalNameDec(VMFrame &f, JSAtom *atom)
2221 JSObject *obj = f.fp->scopeChainObj()->getGlobal();
2222 if (!NameIncDec<-1, true>(f, obj, atom))
2223 THROW();
2226 void JS_FASTCALL
2227 stubs::IncGlobalName(VMFrame &f, JSAtom *atom)
2229 JSObject *obj = f.fp->scopeChainObj()->getGlobal();
2230 if (!NameIncDec<1, false>(f, obj, atom))
2231 THROW();
2234 void JS_FASTCALL
2235 stubs::DecGlobalName(VMFrame &f, JSAtom *atom)
2237 JSObject *obj = f.fp->scopeChainObj()->getGlobal();
2238 if (!NameIncDec<-1, false>(f, obj, atom))
2239 THROW();
2242 static bool JS_FASTCALL
2243 InlineGetProp(VMFrame &f)
2245 JSContext *cx = f.cx;
2246 JSFrameRegs &regs = f.regs;
2248 Value *vp = &f.regs.sp[-1];
2249 JSObject *obj = ValueToObject(f.cx, vp);
2250 if (!obj)
2251 return false;
2253 Value rval;
2254 do {
2256 * We do not impose the method read barrier if in an imacro,
2257 * assuming any property gets it does (e.g., for 'toString'
2258 * from JSOP_NEW) will not be leaked to the calling script.
2260 JSObject *aobj = js_GetProtoIfDenseArray(obj);
2262 PropertyCacheEntry *entry;
2263 JSObject *obj2;
2264 JSAtom *atom;
2265 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
2266 if (!atom) {
2267 if (entry->vword.isFunObj()) {
2268 rval.setFunObj(entry->vword.toFunObj());
2269 } else if (entry->vword.isSlot()) {
2270 uint32 slot = entry->vword.toSlot();
2271 JS_ASSERT(slot < obj2->scope()->freeslot);
2272 rval = obj2->lockedGetSlot(slot);
2273 } else {
2274 JS_ASSERT(entry->vword.isSprop());
2275 JSScopeProperty *sprop = entry->vword.toSprop();
2276 NATIVE_GET(cx, obj, obj2, sprop,
2277 f.fp->imacpc ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER,
2278 &rval, return false);
2280 break;
2283 jsid id = ATOM_TO_JSID(atom);
2284 if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)
2285 ? !js_GetPropertyHelper(cx, obj, id,
2286 f.fp->imacpc
2287 ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
2288 : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER,
2289 &rval)
2290 : !obj->getProperty(cx, id, &rval)) {
2291 return false;
2293 } while(0);
2295 regs.sp[-1] = rval;
2296 return true;
2299 void JS_FASTCALL
2300 stubs::GetProp(VMFrame &f)
2302 if (!InlineGetProp(f))
2303 THROW();
2306 void JS_FASTCALL
2307 stubs::CallProp(VMFrame &f, JSAtom *origAtom)
2309 JSContext *cx = f.cx;
2310 JSFrameRegs &regs = f.regs;
2312 Value lval;
2313 lval = regs.sp[-1];
2315 Value objv;
2316 if (lval.isObject()) {
2317 objv = lval;
2318 } else {
2319 JSProtoKey protoKey;
2320 if (lval.isString()) {
2321 protoKey = JSProto_String;
2322 } else if (lval.isNumber()) {
2323 protoKey = JSProto_Number;
2324 } else if (lval.isBoolean()) {
2325 protoKey = JSProto_Boolean;
2326 } else {
2327 JS_ASSERT(lval.isNull() || lval.isUndefined());
2328 js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
2329 THROW();
2331 JSObject *pobj;
2332 if (!js_GetClassPrototype(cx, NULL, protoKey, &pobj))
2333 THROW();
2334 objv.setNonFunObj(*pobj);
2337 JSObject *aobj = js_GetProtoIfDenseArray(&objv.asObject());
2338 Value rval;
2340 PropertyCacheEntry *entry;
2341 JSObject *obj2;
2342 JSAtom *atom;
2343 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
2344 if (!atom) {
2345 if (entry->vword.isFunObj()) {
2346 rval.setFunObj(entry->vword.toFunObj());
2347 } else if (entry->vword.isSlot()) {
2348 uint32 slot = entry->vword.toSlot();
2349 JS_ASSERT(slot < obj2->scope()->freeslot);
2350 rval = obj2->lockedGetSlot(slot);
2351 } else {
2352 JS_ASSERT(entry->vword.isSprop());
2353 JSScopeProperty *sprop = entry->vword.toSprop();
2354 NATIVE_GET(cx, &objv.asObject(), obj2, sprop, JSGET_NO_METHOD_BARRIER, &rval,
2355 THROW());
2357 regs.sp++;
2358 regs.sp[-2] = rval;
2359 regs.sp[-1] = lval;
2360 goto end_callprop;
2364 * Cache miss: use the immediate atom that was loaded for us under
2365 * PropertyCache::test.
2367 jsid id;
2368 id = ATOM_TO_JSID(origAtom);
2370 regs.sp++;
2371 regs.sp[-1].setNull();
2372 if (lval.isObject()) {
2373 if (!js_GetMethod(cx, &objv.asObject(), id,
2374 JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)
2375 ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
2376 : JSGET_NO_METHOD_BARRIER,
2377 &rval)) {
2378 THROW();
2380 regs.sp[-1] = objv;
2381 regs.sp[-2] = rval;
2382 } else {
2383 JS_ASSERT(objv.asObject().map->ops->getProperty == js_GetProperty);
2384 if (!js_GetPropertyHelper(cx, &objv.asObject(), id,
2385 JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER,
2386 &rval)) {
2387 THROW();
2389 regs.sp[-1] = lval;
2390 regs.sp[-2] = rval;
2393 end_callprop:
2394 /* Wrap primitive lval in object clothing if necessary. */
2395 if (lval.isPrimitive()) {
2396 /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */
2397 if (!rval.isFunObj() ||
2398 !PrimitiveValue::test(GET_FUNCTION_PRIVATE(cx, &rval.asFunObj()),
2399 lval)) {
2400 if (!js_PrimitiveToObject(cx, &regs.sp[-1]))
2401 THROW();
2404 #if JS_HAS_NO_SUCH_METHOD
2405 if (JS_UNLIKELY(rval.isUndefined())) {
2406 regs.sp[-2].setString(ATOM_TO_STRING(origAtom));
2407 if (!js_OnUnknownMethod(cx, regs.sp - 2))
2408 THROW();
2410 #endif
2413 void JS_FASTCALL
2414 stubs::Length(VMFrame &f)
2416 JSFrameRegs &regs = f.regs;
2417 Value *vp = &regs.sp[-1];
2419 if (vp->isString()) {
2420 vp->setInt32(vp->asString()->length());
2421 return;
2422 } else if (vp->isObject()) {
2423 JSObject *obj = &vp->asObject();
2424 if (obj->isArray()) {
2425 jsuint length = obj->getArrayLength();
2426 regs.sp[-1].setDouble(length);
2427 return;
2428 } else if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
2429 uint32 length = obj->getArgsLength();
2430 JS_ASSERT(length < INT32_MAX);
2431 regs.sp[-1].setInt32(int32_t(length));
2432 return;
2436 if (!InlineGetProp(f))
2437 THROW();
2440 void JS_FASTCALL
2441 stubs::Iter(VMFrame &f, uint32 flags)
2443 if (!js_ValueToIterator(f.cx, flags, &f.regs.sp[-1]))
2444 THROW();
2445 JS_ASSERT(!f.regs.sp[-1].isPrimitive());
2448 static void
2449 InitPropOrMethod(VMFrame &f, JSAtom *atom, JSOp op)
2451 JSContext *cx = f.cx;
2452 JSRuntime *rt = cx->runtime;
2453 JSFrameRegs &regs = f.regs;
2455 /* Load the property's initial value into rval. */
2456 JS_ASSERT(regs.sp - f.fp->base() >= 2);
2457 Value rval;
2458 rval = regs.sp[-1];
2460 /* Load the object being initialized into lval/obj. */
2461 JSObject *obj = &regs.sp[-2].asObject();
2462 JS_ASSERT(obj->isNative());
2463 JS_ASSERT(!obj->getClass()->reserveSlots);
2465 JSScope *scope = obj->scope();
2468 * Probe the property cache.
2470 * We can not assume that the object created by JSOP_NEWINIT is still
2471 * single-threaded as the debugger can access it from other threads.
2472 * So check first.
2474 * On a hit, if the cached sprop has a non-default setter, it must be
2475 * __proto__. If sprop->parent != scope->lastProperty(), there is a
2476 * repeated property name. The fast path does not handle these two cases.
2478 PropertyCacheEntry *entry;
2479 JSScopeProperty *sprop;
2480 if (CX_OWNS_OBJECT_TITLE(cx, obj) &&
2481 JS_PROPERTY_CACHE(cx).testForInit(rt, regs.pc, obj, scope, &sprop, &entry) &&
2482 sprop->hasDefaultSetter() &&
2483 sprop->parent == scope->lastProperty())
2485 /* Fast path. Property cache hit. */
2486 uint32 slot = sprop->slot;
2487 JS_ASSERT(slot == scope->freeslot);
2488 if (slot < obj->numSlots()) {
2489 ++scope->freeslot;
2490 } else {
2491 if (!js_AllocSlot(cx, obj, &slot))
2492 THROW();
2493 JS_ASSERT(slot == sprop->slot);
2496 JS_ASSERT(!scope->lastProperty() ||
2497 scope->shape == scope->lastProperty()->shape);
2498 if (scope->table) {
2499 JSScopeProperty *sprop2 =
2500 scope->addProperty(cx, sprop->id, sprop->getter(), sprop->setter(), slot,
2501 sprop->attributes(), sprop->getFlags(), sprop->shortid);
2502 if (!sprop2) {
2503 js_FreeSlot(cx, obj, slot);
2504 THROW();
2506 JS_ASSERT(sprop2 == sprop);
2507 } else {
2508 JS_ASSERT(!scope->isSharedEmpty());
2509 scope->extend(cx, sprop);
2513 * No method change check here because here we are adding a new
2514 * property, not updating an existing slot's value that might
2515 * contain a method of a branded scope.
2517 obj->lockedSetSlot(slot, rval);
2518 } else {
2519 PCMETER(JS_PROPERTY_CACHE(cx).inipcmisses++);
2521 /* Get the immediate property name into id. */
2522 jsid id = ATOM_TO_JSID(atom);
2524 /* Set the property named by obj[id] to rval. */
2525 if (!CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER, NULL, NULL))
2526 THROW();
2528 uintN defineHow = (op == JSOP_INITMETHOD)
2529 ? JSDNP_CACHE_RESULT | JSDNP_SET_METHOD
2530 : JSDNP_CACHE_RESULT;
2531 if (!(JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom)
2532 ? js_SetPropertyHelper(cx, obj, id, defineHow, &rval)
2533 : js_DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
2534 JSPROP_ENUMERATE, 0, 0, NULL,
2535 defineHow))) {
2536 THROW();
2541 void JS_FASTCALL
2542 stubs::InitProp(VMFrame &f, JSAtom *atom)
2544 InitPropOrMethod(f, atom, JSOP_INITPROP);
2547 void JS_FASTCALL
2548 stubs::InitMethod(VMFrame &f, JSAtom *atom)
2550 InitPropOrMethod(f, atom, JSOP_INITMETHOD);
2553 void JS_FASTCALL
2554 stubs::IterNext(VMFrame &f)
2556 JS_ASSERT(f.regs.sp - 1 >= f.fp->base());
2557 JS_ASSERT(f.regs.sp[-1].isObject());
2559 JSObject *iterobj = &f.regs.sp[-1].asObject();
2560 f.regs.sp[0].setNull();
2561 f.regs.sp++;
2562 if (!js_IteratorNext(f.cx, iterobj, &f.regs.sp[-1]))
2563 THROW();
2566 JSBool JS_FASTCALL
2567 stubs::IterMore(VMFrame &f)
2569 JS_ASSERT(f.regs.sp - 1 >= f.fp->base());
2570 JS_ASSERT(f.regs.sp[-1].isObject());
2572 Value v;
2573 JSObject *iterobj = &f.regs.sp[-1].asObject();
2574 if (!js_IteratorMore(f.cx, iterobj, &v))
2575 THROWV(JS_FALSE);
2577 return v.asBoolean();
2580 void JS_FASTCALL
2581 stubs::EndIter(VMFrame &f)
2583 JS_ASSERT(f.regs.sp - 1 >= f.fp->base());
2584 if (!js_CloseIterator(f.cx, f.regs.sp[-1]))
2585 THROW();
2588 JSString * JS_FASTCALL
2589 stubs::TypeOf(VMFrame &f)
2591 const Value &ref = f.regs.sp[-1];
2592 JSType type = JS_TypeOfValue(f.cx, Jsvalify(ref));
2593 JSAtom *atom = f.cx->runtime->atomState.typeAtoms[type];
2594 return ATOM_TO_STRING(atom);
2597 JSBool JS_FASTCALL
2598 stubs::StrictEq(VMFrame &f)
2600 const Value &rhs = f.regs.sp[-1];
2601 const Value &lhs = f.regs.sp[-2];
2602 return StrictlyEqual(f.cx, lhs, rhs) == true;
2605 JSBool JS_FASTCALL
2606 stubs::StrictNe(VMFrame &f)
2608 const Value &rhs = f.regs.sp[-1];
2609 const Value &lhs = f.regs.sp[-2];
2610 return StrictlyEqual(f.cx, lhs, rhs) != true;
2613 JSString * JS_FASTCALL
2614 stubs::ConcatN(VMFrame &f, uint32 argc)
2616 JSCharBuffer buf(f.cx);
2617 for (Value *vp = f.regs.sp - argc; vp < f.regs.sp; vp++) {
2618 JS_ASSERT(vp->isPrimitive());
2619 if (!js_ValueToCharBuffer(f.cx, *vp, buf))
2620 THROWV(NULL);
2622 JSString *str = js_NewStringFromCharBuffer(f.cx, buf);
2623 if (!str)
2624 THROWV(NULL);
2625 return str;
2628 void JS_FASTCALL
2629 stubs::Throw(VMFrame &f)
2631 JSContext *cx = f.cx;
2633 JS_ASSERT(!cx->throwing);
2634 cx->throwing = JS_TRUE;
2635 cx->exception = f.regs.sp[-1];
2636 THROW();
2639 JSObject * JS_FASTCALL
2640 stubs::FlatLambda(VMFrame &f, JSFunction *fun)
2642 JSObject *obj = js_NewFlatClosure(f.cx, fun);
2643 if (!obj)
2644 THROWV(NULL);
2645 return obj;
2648 void JS_FASTCALL
2649 stubs::Arguments(VMFrame &f)
2651 f.regs.sp++;
2652 if (!js_GetArgsValue(f.cx, f.fp, &f.regs.sp[-1]))
2653 THROW();
2656 JSBool JS_FASTCALL
2657 stubs::InstanceOf(VMFrame &f)
2659 JSContext *cx = f.cx;
2660 JSFrameRegs &regs = f.regs;
2662 const Value &rref = regs.sp[-1];
2663 JSObject *obj;
2664 if (rref.isPrimitive() ||
2665 !(obj = &rref.asObject())->map->ops->hasInstance) {
2666 js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
2667 -1, rref, NULL);
2668 THROWV(JS_FALSE);
2670 const Value &lref = regs.sp[-2];
2671 JSBool cond = JS_FALSE;
2672 if (!obj->map->ops->hasInstance(cx, obj, lref, &cond))
2673 THROWV(JS_FALSE);
2674 return cond;
2678 void JS_FASTCALL
2679 stubs::ArgCnt(VMFrame &f)
2681 JSContext *cx = f.cx;
2682 JSRuntime *rt = cx->runtime;
2683 JSStackFrame *fp = f.fp;
2685 jsid id = ATOM_TO_JSID(rt->atomState.lengthAtom);
2686 f.regs.sp++;
2687 if (!js_GetArgsProperty(cx, fp, id, &f.regs.sp[-1]))
2688 THROW();
2691 void JS_FASTCALL
2692 stubs::EnterBlock(VMFrame &f, JSObject *obj)
2694 JSFrameRegs &regs = f.regs;
2695 JSStackFrame *fp = f.fp;
2697 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj));
2698 JS_ASSERT(fp->base() + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp);
2699 Value *vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj);
2700 JS_ASSERT(regs.sp < vp);
2701 JS_ASSERT(vp <= fp->slots() + fp->script->nslots);
2702 SetValueRangeToUndefined(regs.sp, vp);
2703 regs.sp = vp;
2705 #ifdef DEBUG
2706 JSContext *cx = f.cx;
2707 JS_ASSERT(fp->blockChain == obj->getParent());
2710 * The young end of fp->scopeChain may omit blocks if we haven't closed
2711 * over them, but if there are any closure blocks on fp->scopeChain, they'd
2712 * better be (clones of) ancestors of the block we're entering now;
2713 * anything else we should have popped off fp->scopeChain when we left its
2714 * static scope.
2716 JSObject *obj2 = fp->scopeChainObj();
2717 Class *clasp;
2718 while ((clasp = obj2->getClass()) == &js_WithClass)
2719 obj2 = obj2->getParent();
2720 if (clasp == &js_BlockClass &&
2721 obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, fp)) {
2722 JSObject *youngestProto = obj2->getProto();
2723 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(youngestProto));
2724 JSObject *parent = obj;
2725 while ((parent = parent->getParent()) != youngestProto)
2726 JS_ASSERT(parent);
2728 #endif
2730 fp->blockChain = obj;
2733 void JS_FASTCALL
2734 stubs::LeaveBlock(VMFrame &f)
2736 JSContext *cx = f.cx;
2737 JSStackFrame *fp = f.fp;
2739 #ifdef DEBUG
2740 JS_ASSERT(fp->blockChain->getClass() == &js_BlockClass);
2741 uintN blockDepth = OBJ_BLOCK_DEPTH(cx, fp->blockChain);
2743 JS_ASSERT(blockDepth <= StackDepth(fp->script));
2744 #endif
2746 * If we're about to leave the dynamic scope of a block that has been
2747 * cloned onto fp->scopeChain, clear its private data, move its locals from
2748 * the stack into the clone, and pop it off the chain.
2750 JSObject *obj = fp->scopeChainObj();
2751 if (obj->getProto() == fp->blockChain) {
2752 JS_ASSERT(obj->getClass() == &js_BlockClass);
2753 if (!js_PutBlockObject(cx, JS_TRUE))
2754 THROW();
2757 /* Pop the block chain, too. */
2758 fp->blockChain = fp->blockChain->getParent();
2761 void * JS_FASTCALL
2762 stubs::LookupSwitch(VMFrame &f, jsbytecode *pc)
2764 jsbytecode *jpc = pc;
2765 JSScript *script = f.fp->script;
2767 /* This is correct because the compiler adjusts the stack beforehand. */
2768 Value lval = f.regs.sp[-1];
2770 if (!lval.isPrimitive()) {
2771 ptrdiff_t offs = (pc + GET_JUMP_OFFSET(pc)) - script->code;
2772 JS_ASSERT(script->nmap[offs]);
2773 return script->nmap[offs];
2776 JS_ASSERT(pc[0] == JSOP_LOOKUPSWITCH);
2778 pc += JUMP_OFFSET_LEN;
2779 uint32 npairs = GET_UINT16(pc);
2780 pc += UINT16_LEN;
2782 JS_ASSERT(npairs);
2784 if (lval.isString()) {
2785 JSString *str = lval.asString();
2786 for (uint32 i = 1; i <= npairs; i++) {
2787 Value rval = script->getConst(GET_INDEX(pc));
2788 pc += INDEX_LEN;
2789 if (rval.isString()) {
2790 JSString *rhs = rval.asString();
2791 if (rhs == str || js_EqualStrings(str, rhs)) {
2792 ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code;
2793 JS_ASSERT(script->nmap[offs]);
2794 return script->nmap[offs];
2797 pc += JUMP_OFFSET_LEN;
2799 } else if (lval.isNumber()) {
2800 double d = lval.asNumber();
2801 for (uint32 i = 1; i <= npairs; i++) {
2802 Value rval = script->getConst(GET_INDEX(pc));
2803 pc += INDEX_LEN;
2804 if (rval.isNumber() && d == rval.asNumber()) {
2805 ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code;
2806 JS_ASSERT(script->nmap[offs]);
2807 return script->nmap[offs];
2809 pc += JUMP_OFFSET_LEN;
2811 } else {
2812 for (uint32 i = 1; i <= npairs; i++) {
2813 Value rval = script->getConst(GET_INDEX(pc));
2814 pc += INDEX_LEN;
2815 if (lval == rval) {
2816 ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code;
2817 JS_ASSERT(script->nmap[offs]);
2818 return script->nmap[offs];
2820 pc += JUMP_OFFSET_LEN;
2824 ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(jpc)) - script->code;
2825 JS_ASSERT(script->nmap[offs]);
2826 return script->nmap[offs];
2829 void * JS_FASTCALL
2830 stubs::TableSwitch(VMFrame &f, jsbytecode *origPc)
2832 jsbytecode * const originalPC = origPc;
2833 jsbytecode *pc = originalPC;
2834 JSScript *script = f.fp->script;
2835 uint32 jumpOffset = GET_JUMP_OFFSET(pc);
2836 pc += JUMP_OFFSET_LEN;
2838 /* Note: compiler adjusts the stack beforehand. */
2839 Value rval = f.regs.sp[-1];
2841 jsint tableIdx;
2842 if (rval.isInt32()) {
2843 tableIdx = rval.asInt32();
2844 } else if (rval.isDouble()) {
2845 double d = rval.asDouble();
2846 if (d == 0) {
2847 /* Treat -0 (double) as 0. */
2848 tableIdx = 0;
2849 } else if (!JSDOUBLE_IS_INT32(d, (int32_t&)tableIdx)) {
2850 goto finally;
2852 } else {
2853 goto finally;
2857 uint32 low = GET_JUMP_OFFSET(pc);
2858 pc += JUMP_OFFSET_LEN;
2859 uint32 high = GET_JUMP_OFFSET(pc);
2860 pc += JUMP_OFFSET_LEN;
2862 tableIdx -= low;
2863 if ((jsuint) tableIdx < (jsuint)(high - low + 1)) {
2864 pc += JUMP_OFFSET_LEN * tableIdx;
2865 uint32 candidateOffset = GET_JUMP_OFFSET(pc);
2866 if (candidateOffset)
2867 jumpOffset = candidateOffset;
2871 finally:
2872 /* Provide the native address. */
2873 ptrdiff_t offset = (originalPC + jumpOffset) - script->code;
2874 JS_ASSERT(script->nmap[offset]);
2875 return script->nmap[offset];