Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / methodjit / StubCalls.cpp
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=4 sw=4 et tw=99:
3  *
4  * ***** BEGIN LICENSE BLOCK *****
5  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18  * May 28, 2008.
19  *
20  * The Initial Developer of the Original Code is
21  *   Brendan Eich <brendan@mozilla.org>
22  *
23  * Contributor(s):
24  *   David Anderson <danderson@mozilla.com>
25  *   David Mandelin <dmandelin@mozilla.com>
26  *
27  * Alternatively, the contents of this file may be used under the terms of
28  * either of the GNU General Public License Version 2 or later (the "GPL"),
29  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30  * in which case the provisions of the GPL or the LGPL are applicable instead
31  * of those above. If you wish to allow use of your version of this file only
32  * under the terms of either the GPL or the LGPL, and not to allow others to
33  * use your version of this file under the terms of the MPL, indicate your
34  * decision by deleting the provisions above and replace them with the notice
35  * and other provisions required by the GPL or the LGPL. If you do not delete
36  * the provisions above, a recipient may use your version of this file under
37  * the terms of any one of the MPL, the GPL or the LGPL.
38  *
39  * ***** END LICENSE BLOCK ***** */
40
41 #include "jscntxt.h"
42 #include "jsscope.h"
43 #include "jsobj.h"
44 #include "jslibmath.h"
45 #include "jsiter.h"
46 #include "jsnum.h"
47 #include "jsxml.h"
48 #include "jsstaticcheck.h"
49 #include "jsbool.h"
50 #include "assembler/assembler/MacroAssemblerCodeRef.h"
51 #include "jsiter.h"
52 #include "jstypes.h"
53 #include "methodjit/Compiler.h"
54 #include "methodjit/StubCalls.h"
55 #include "jstracer.h"
56
57 #include "jsinterpinlines.h"
58 #include "jspropertycache.h"
59 #include "jspropertycacheinlines.h"
60 #include "jsscopeinlines.h"
61 #include "jsscriptinlines.h"
62 #include "jsstrinlines.h"
63 #include "jsobjinlines.h"
64 #include "jscntxtinlines.h"
65 #include "jsatominlines.h"
66 #include "StubCalls-inl.h"
67 #include "jsfuninlines.h"
68 #include "jstypedarray.h"
69
70 #ifdef XP_WIN
71 # include "jswin.h"
72 #endif
73
74 #include "jsautooplen.h"
75
76 using namespace js;
77 using namespace js::mjit;
78 using namespace JSC;
79
80 void JS_FASTCALL
81 stubs::BindName(VMFrame &f)
82 {
83     PropertyCacheEntry *entry;
84
85     /* Fast-path should have caught this. See comment in interpreter. */
86     JS_ASSERT(f.fp()->scopeChain().getParent());
87
88     JSAtom *atom;
89     JSObject *obj2;
90     JSContext *cx = f.cx;
91     JSObject *obj = &f.fp()->scopeChain();
92     JS_PROPERTY_CACHE(cx).test(cx, f.regs.pc, obj, obj2, entry, atom);
93     if (atom) {
94         jsid id = ATOM_TO_JSID(atom);
95         obj = js_FindIdentifierBase(cx, &f.fp()->scopeChain(), id);
96         if (!obj)
97             THROW();
98     }
99     f.regs.sp++;
100     f.regs.sp[-1].setObject(*obj);
101 }
102
103 void JS_FASTCALL
104 stubs::BindNameNoCache(VMFrame &f, JSAtom *atom)
105 {
106     JSObject *obj = js_FindIdentifierBase(f.cx, &f.fp()->scopeChain(), ATOM_TO_JSID(atom));
107     if (!obj)
108         THROW();
109     f.regs.sp[0].setObject(*obj);
110 }
111
112 JSObject * JS_FASTCALL
113 stubs::BindGlobalName(VMFrame &f)
114 {
115     return f.fp()->scopeChain().getGlobal();
116 }
117
118 template<JSBool strict>
119 void JS_FASTCALL
120 stubs::SetName(VMFrame &f, JSAtom *origAtom)
121 {
122     JSContext *cx = f.cx;
123
124     Value rval = f.regs.sp[-1];
125     Value &lref = f.regs.sp[-2];
126     JSObject *obj = ValueToObject(cx, &lref);
127     if (!obj)
128         THROW();
129
130     do {
131         PropertyCache *cache = &JS_PROPERTY_CACHE(cx);
132
133         /*
134          * Probe the property cache, specializing for two important
135          * set-property cases. First:
136          *
137          *   function f(a, b, c) {
138          *     var o = {p:a, q:b, r:c};
139          *     return o;
140          *   }
141          *
142          * or similar real-world cases, which evolve a newborn native
143          * object predicatably through some bounded number of property
144          * additions. And second:
145          *
146          *   o.p = x;
147          *
148          * in a frequently executed method or loop body, where p will
149          * (possibly after the first iteration) always exist in native
150          * object o.
151          */
152         PropertyCacheEntry *entry;
153         JSObject *obj2;
154         JSAtom *atom;
155         if (cache->testForSet(cx, f.regs.pc, obj, &entry, &obj2, &atom)) {
156             /*
157              * Property cache hit, only partially confirmed by testForSet. We
158              * know that the entry applies to regs.pc and that obj's shape
159              * matches.
160              *
161              * The entry predicts either a new property to be added directly to
162              * obj by this set, or on an existing "own" property, or on a
163              * prototype property that has a setter.
164              */
165             const Shape *shape = entry->vword.toShape();
166             JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable());
167             JS_ASSERT_IF(shape->hasSlot(), entry->vcapTag() == 0);
168
169             /*
170              * Fastest path: check whether obj already has the cached shape and
171              * call NATIVE_SET and break to get out of the do-while(0). But we
172              * can call NATIVE_SET only for a direct or proto-setter hit.
173              */
174             if (!entry->adding()) {
175                 if (entry->vcapTag() == 0 ||
176                     ((obj2 = obj->getProto()) && obj2->shape() == entry->vshape()))
177                 {
178 #ifdef DEBUG
179                     if (entry->directHit()) {
180                         JS_ASSERT(obj->nativeContains(*shape));
181                     } else {
182                         JS_ASSERT(obj2->nativeContains(*shape));
183                         JS_ASSERT(entry->vcapTag() == 1);
184                         JS_ASSERT(entry->kshape != entry->vshape());
185                         JS_ASSERT(!shape->hasSlot());
186                     }
187 #endif
188
189                     PCMETER(cache->pchits++);
190                     PCMETER(cache->setpchits++);
191                     NATIVE_SET(cx, obj, shape, entry, strict, &rval);
192                     break;
193                 }
194             } else {
195                 JS_ASSERT(obj->isExtensible());
196
197                 if (obj->nativeEmpty()) {
198                     if (!obj->ensureClassReservedSlotsForEmptyObject(cx))
199                         THROW();
200                 }
201
202                 uint32 slot;
203                 if (shape->previous() == obj->lastProperty() &&
204                     entry->vshape() == cx->runtime->protoHazardShape &&
205                     shape->hasDefaultSetter()) {
206                     slot = shape->slot;
207                     JS_ASSERT(slot == obj->slotSpan());
208
209                     /*
210                      * Fast path: adding a plain old property that was once at
211                      * the frontier of the property tree, whose slot is next to
212                      * claim among the already-allocated slots in obj, where
213                      * shape->table has not been created yet.
214                      */
215                     PCMETER(cache->pchits++);
216                     PCMETER(cache->addpchits++);
217
218                     if (slot < obj->numSlots()) {
219                         JS_ASSERT(obj->getSlot(slot).isUndefined());
220                     } else {
221                         if (!obj->allocSlot(cx, &slot))
222                             THROW();
223                         JS_ASSERT(slot == shape->slot);
224                     }
225
226                     /* Simply extend obj's property tree path with shape! */
227                     obj->extend(cx, shape);
228
229                     /*
230                      * No method change check here because here we are adding a
231                      * new property, not updating an existing slot's value that
232                      * might contain a method of a branded shape.
233                      */
234                     obj->setSlot(slot, rval);
235
236                     /*
237                      * Purge the property cache of the id we may have just
238                      * shadowed in obj's scope and proto chains.
239                      */
240                     js_PurgeScopeChain(cx, obj, shape->id);
241                     break;
242                 }
243             }
244             PCMETER(cache->setpcmisses++);
245
246             atom = origAtom;
247         } else {
248             JS_ASSERT(atom);
249         }
250
251         jsid id = ATOM_TO_JSID(atom);
252         if (entry && JS_LIKELY(!obj->getOps()->setProperty)) {
253             uintN defineHow;
254             JSOp op = JSOp(*f.regs.pc);
255             if (op == JSOP_SETMETHOD)
256                 defineHow = JSDNP_CACHE_RESULT | JSDNP_SET_METHOD;
257             else if (op == JSOP_SETNAME)
258                 defineHow = JSDNP_CACHE_RESULT | JSDNP_UNQUALIFIED;
259             else
260                 defineHow = JSDNP_CACHE_RESULT;
261             if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rval, strict))
262                 THROW();
263         } else {
264             if (!obj->setProperty(cx, id, &rval, strict))
265                 THROW();
266         }
267     } while (0);
268
269     f.regs.sp[-2] = f.regs.sp[-1];
270 }
271
272 template void JS_FASTCALL stubs::SetName<true>(VMFrame &f, JSAtom *origAtom);
273 template void JS_FASTCALL stubs::SetName<false>(VMFrame &f, JSAtom *origAtom);
274
275 template<JSBool strict>
276 void JS_FASTCALL
277 stubs::SetPropNoCache(VMFrame &f, JSAtom *atom)
278 {
279     JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
280     if (!obj)
281         THROW();
282     Value rval = f.regs.sp[-1];
283     if (!obj->setProperty(f.cx, ATOM_TO_JSID(atom), &f.regs.sp[-1], strict))
284         THROW();
285     f.regs.sp[-2] = rval;
286 }
287
288 template void JS_FASTCALL stubs::SetPropNoCache<true>(VMFrame &f, JSAtom *origAtom);
289 template void JS_FASTCALL stubs::SetPropNoCache<false>(VMFrame &f, JSAtom *origAtom);
290
291 template<JSBool strict>
292 void JS_FASTCALL
293 stubs::SetGlobalNameNoCache(VMFrame &f, JSAtom *atom)
294 {
295     JSContext *cx = f.cx;
296
297     Value rval = f.regs.sp[-1];
298     Value &lref = f.regs.sp[-2];
299     JSObject *obj = ValueToObject(cx, &lref);
300     if (!obj)
301         THROW();
302     jsid id = ATOM_TO_JSID(atom);
303     if (!obj->setProperty(cx, id, &rval, strict))
304         THROW();
305
306     f.regs.sp[-2] = f.regs.sp[-1];
307 }
308
309 template void JS_FASTCALL stubs::SetGlobalNameNoCache<true>(VMFrame &f, JSAtom *atom);
310 template void JS_FASTCALL stubs::SetGlobalNameNoCache<false>(VMFrame &f, JSAtom *atom);
311
312 template<JSBool strict>
313 void JS_FASTCALL
314 stubs::SetGlobalName(VMFrame &f, JSAtom *atom)
315 {
316     SetName<strict>(f, atom);
317 }
318
319 template void JS_FASTCALL stubs::SetGlobalName<true>(VMFrame &f, JSAtom *atom);
320 template void JS_FASTCALL stubs::SetGlobalName<false>(VMFrame &f, JSAtom *atom);
321
322 static inline void
323 PushImplicitThis(VMFrame &f, JSObject *obj, Value &rval)
324 {
325     Value thisv;
326
327     if (!ComputeImplicitThis(f.cx, obj, rval, &thisv))
328         return;
329     *f.regs.sp++ = thisv;
330 }
331
332 static JSObject *
333 NameOp(VMFrame &f, JSObject *obj, bool callname = false)
334 {
335     JSContext *cx = f.cx;
336
337     const Shape *shape;
338     Value rval;
339
340     PropertyCacheEntry *entry;
341     JSObject *obj2;
342     JSAtom *atom;
343     JS_PROPERTY_CACHE(cx).test(cx, f.regs.pc, obj, obj2, entry, atom);
344     if (!atom) {
345         if (entry->vword.isFunObj()) {
346             rval.setObject(entry->vword.toFunObj());
347         } else if (entry->vword.isSlot()) {
348             uintN slot = entry->vword.toSlot();
349             rval = obj2->nativeGetSlot(slot);
350         } else {
351             JS_ASSERT(entry->vword.isShape());
352             shape = entry->vword.toShape();
353             NATIVE_GET(cx, obj, obj2, shape, JSGET_METHOD_BARRIER, &rval, return NULL);
354         }
355
356         JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj));
357     } else {
358         jsid id;
359         id = ATOM_TO_JSID(atom);
360         JSProperty *prop;
361         if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
362             return NULL;
363         if (!prop) {
364             /* Kludge to allow (typeof foo == "undefined") tests. */
365             JSOp op2 = js_GetOpcode(cx, f.fp()->script(), f.regs.pc + JSOP_NAME_LENGTH);
366             if (op2 == JSOP_TYPEOF) {
367                 f.regs.sp++;
368                 f.regs.sp[-1].setUndefined();
369                 return obj;
370             }
371             ReportAtomNotDefined(cx, atom);
372             return NULL;
373         }
374
375         /* Take the slow path if prop was not found in a native object. */
376         if (!obj->isNative() || !obj2->isNative()) {
377             if (!obj->getProperty(cx, id, &rval))
378                 return NULL;
379         } else {
380             shape = (Shape *)prop;
381             JSObject *normalized = obj;
382             if (normalized->getClass() == &js_WithClass && !shape->hasDefaultGetter())
383                 normalized = js_UnwrapWithObject(cx, normalized);
384             NATIVE_GET(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, &rval, return NULL);
385         }
386     }
387
388     *f.regs.sp++ = rval;
389
390     if (callname)
391         PushImplicitThis(f, obj, rval);
392
393     return obj;
394 }
395
396 void JS_FASTCALL
397 stubs::Name(VMFrame &f)
398 {
399     if (!NameOp(f, &f.fp()->scopeChain()))
400         THROW();
401 }
402
403 void JS_FASTCALL
404 stubs::GetGlobalName(VMFrame &f)
405 {
406     JSObject *globalObj = f.fp()->scopeChain().getGlobal();
407     if (!NameOp(f, globalObj))
408          THROW();
409 }
410
411 void JS_FASTCALL
412 stubs::GetElem(VMFrame &f)
413 {
414     JSContext *cx = f.cx;
415     JSFrameRegs &regs = f.regs;
416
417     Value &lref = regs.sp[-2];
418     Value &rref = regs.sp[-1];
419     if (lref.isString() && rref.isInt32()) {
420         JSString *str = lref.toString();
421         int32_t i = rref.toInt32();
422         if ((size_t)i < str->length()) {
423             str = JSString::getUnitString(cx, str, (size_t)i);
424             if (!str)
425                 THROW();
426             f.regs.sp[-2].setString(str);
427             return;
428         }
429     }
430
431     JSObject *obj = ValueToObject(cx, &lref);
432     if (!obj)
433         THROW();
434
435     const Value *copyFrom;
436     Value rval;
437     jsid id;
438     if (rref.isInt32()) {
439         int32_t i = rref.toInt32();
440         if (obj->isDenseArray()) {
441             jsuint idx = jsuint(i);
442
443             if (idx < obj->getArrayLength() &&
444                 idx < obj->getDenseArrayCapacity()) {
445                 copyFrom = obj->addressOfDenseArrayElement(idx);
446                 if (!copyFrom->isMagic())
447                     goto end_getelem;
448             }
449         } else if (obj->isArguments()) {
450             uint32 arg = uint32(i);
451
452             if (arg < obj->getArgsInitialLength()) {
453                 copyFrom = obj->addressOfArgsElement(arg);
454                 if (!copyFrom->isMagic()) {
455                     if (JSStackFrame *afp = (JSStackFrame *) obj->getPrivate())
456                         copyFrom = &afp->canonicalActualArg(arg);
457                     goto end_getelem;
458                 }
459             }
460         }
461         if (JS_LIKELY(INT_FITS_IN_JSID(i)))
462             id = INT_TO_JSID(i);
463         else
464             goto intern_big_int;
465
466     } else {
467         int32_t i;
468         if (ValueFitsInInt32(rref, &i) && INT_FITS_IN_JSID(i)) {
469             id = INT_TO_JSID(i);
470         } else {
471           intern_big_int:
472             if (!js_InternNonIntElementId(cx, obj, rref, &id))
473                 THROW();
474         }
475     }
476
477     if (!obj->getProperty(cx, id, &rval))
478         THROW();
479     copyFrom = &rval;
480
481   end_getelem:
482     f.regs.sp[-2] = *copyFrom;
483 }
484
485 static inline bool
486 FetchElementId(VMFrame &f, JSObject *obj, const Value &idval, jsid &id, Value *vp)
487 {
488     int32_t i_;
489     if (ValueFitsInInt32(idval, &i_) && INT_FITS_IN_JSID(i_)) {
490         id = INT_TO_JSID(i_);
491         return true;
492     }
493     return !!js_InternNonIntElementId(f.cx, obj, idval, &id, vp);
494 }
495
496 void JS_FASTCALL
497 stubs::CallElem(VMFrame &f)
498 {
499     JSContext *cx = f.cx;
500     JSFrameRegs &regs = f.regs;
501
502     /* Find the object on which to look for |this|'s properties. */
503     Value thisv = regs.sp[-2];
504     JSObject *thisObj = ValuePropertyBearer(cx, thisv, -2);
505     if (!thisObj)
506         THROW();
507
508     /* Fetch index and convert it to id suitable for use with thisObj. */
509     jsid id;
510     if (!FetchElementId(f, thisObj, regs.sp[-1], id, &regs.sp[-2]))
511         THROW();
512
513     /* Get or set the element. */
514     if (!js_GetMethod(cx, thisObj, id, JSGET_NO_METHOD_BARRIER, &regs.sp[-2]))
515         THROW();
516
517 #if JS_HAS_NO_SUCH_METHOD
518     if (JS_UNLIKELY(regs.sp[-2].isUndefined()) && thisv.isObject()) {
519         regs.sp[-2] = regs.sp[-1];
520         regs.sp[-1].setObject(*thisObj);
521         if (!js_OnUnknownMethod(cx, regs.sp - 2))
522             THROW();
523     } else
524 #endif
525     {
526         regs.sp[-1] = thisv;
527     }
528 }
529
530 template<JSBool strict>
531 void JS_FASTCALL
532 stubs::SetElem(VMFrame &f)
533 {
534     JSContext *cx = f.cx;
535     JSFrameRegs &regs = f.regs;
536
537     Value &objval = regs.sp[-3];
538     Value &idval  = regs.sp[-2];
539     Value rval    = regs.sp[-1];
540
541     JSObject *obj;
542     jsid id;
543
544     obj = ValueToObject(cx, &objval);
545     if (!obj)
546         THROW();
547
548     if (!FetchElementId(f, obj, idval, id, &regs.sp[-2]))
549         THROW();
550
551     do {
552         if (obj->isDenseArray() && JSID_IS_INT(id)) {
553             jsuint length = obj->getDenseArrayCapacity();
554             jsint i = JSID_TO_INT(id);
555             if ((jsuint)i < length) {
556                 if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
557                     if (js_PrototypeHasIndexedProperties(cx, obj))
558                         break;
559                     if ((jsuint)i >= obj->getArrayLength())
560                         obj->setArrayLength(i + 1);
561                 }
562                 obj->setDenseArrayElement(i, rval);
563                 goto end_setelem;
564             }
565         }
566     } while (0);
567     if (!obj->setProperty(cx, id, &rval, strict))
568         THROW();
569   end_setelem:
570     /* :FIXME: Moving the assigned object into the lowest stack slot
571      * is a temporary hack. What we actually want is an implementation
572      * of popAfterSet() that allows popping more than one value;
573      * this logic can then be handled in Compiler.cpp. */
574     regs.sp[-3] = regs.sp[-1];
575 }
576
577 template void JS_FASTCALL stubs::SetElem<true>(VMFrame &f);
578 template void JS_FASTCALL stubs::SetElem<false>(VMFrame &f);
579
580 void JS_FASTCALL
581 stubs::CallName(VMFrame &f)
582 {
583     JSObject *obj = NameOp(f, &f.fp()->scopeChain(), true);
584     if (!obj)
585         THROW();
586 }
587
588 /*
589  * Push the implicit this value, with the assumption that the callee
590  * (which is on top of the stack) was read as a property from the
591  * global object.
592  */
593 void JS_FASTCALL
594 stubs::PushImplicitThisForGlobal(VMFrame &f)
595 {
596     return PushImplicitThis(f, f.fp()->scopeChain().getGlobal(), f.regs.sp[-1]);
597 }
598
599 void JS_FASTCALL
600 stubs::BitOr(VMFrame &f)
601 {
602     int32_t i, j;
603
604     if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i) ||
605         !ValueToECMAInt32(f.cx, f.regs.sp[-1], &j)) {
606         THROW();
607     }
608     i = i | j;
609     f.regs.sp[-2].setInt32(i);
610 }
611
612 void JS_FASTCALL
613 stubs::BitXor(VMFrame &f)
614 {
615     int32_t i, j;
616
617     if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i) ||
618         !ValueToECMAInt32(f.cx, f.regs.sp[-1], &j)) {
619         THROW();
620     }
621     i = i ^ j;
622     f.regs.sp[-2].setInt32(i);
623 }
624
625 void JS_FASTCALL
626 stubs::BitAnd(VMFrame &f)
627 {
628     int32_t i, j;
629
630     if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i) ||
631         !ValueToECMAInt32(f.cx, f.regs.sp[-1], &j)) {
632         THROW();
633     }
634     i = i & j;
635     f.regs.sp[-2].setInt32(i);
636 }
637
638 void JS_FASTCALL
639 stubs::BitNot(VMFrame &f)
640 {
641     int32_t i;
642
643     if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &i))
644         THROW();
645     i = ~i;
646     f.regs.sp[-1].setInt32(i);
647 }
648
649 void JS_FASTCALL
650 stubs::Lsh(VMFrame &f)
651 {
652     int32_t i, j;
653     if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i))
654         THROW();
655     if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &j))
656         THROW();
657     i = i << (j & 31);
658     f.regs.sp[-2].setInt32(i);
659 }
660
661 void JS_FASTCALL
662 stubs::Rsh(VMFrame &f)
663 {
664     int32_t i, j;
665     if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i))
666         THROW();
667     if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &j))
668         THROW();
669     i = i >> (j & 31);
670     f.regs.sp[-2].setInt32(i);
671 }
672
673 void JS_FASTCALL
674 stubs::Ursh(VMFrame &f)
675 {
676     uint32_t u;
677     if (!ValueToECMAUint32(f.cx, f.regs.sp[-2], &u))
678         THROW();
679     int32_t j;
680     if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &j))
681         THROW();
682
683     u >>= (j & 31);
684
685         f.regs.sp[-2].setNumber(uint32(u));
686 }
687
688 template<JSBool strict>
689 void JS_FASTCALL
690 stubs::DefFun(VMFrame &f, JSFunction *fun)
691 {
692     JSObject *obj2;
693
694     JSContext *cx = f.cx;
695     JSStackFrame *fp = f.fp();
696
697     /*
698      * A top-level function defined in Global or Eval code (see ECMA-262
699      * Ed. 3), or else a SpiderMonkey extension: a named function statement in
700      * a compound statement (not at the top statement level of global code, or
701      * at the top level of a function body).
702      */
703     JSObject *obj = FUN_OBJECT(fun);
704
705     if (FUN_NULL_CLOSURE(fun)) {
706         /*
707          * Even a null closure needs a parent for principals finding.
708          * FIXME: bug 476950, although debugger users may also demand some kind
709          * of scope link for debugger-assisted eval-in-frame.
710          */
711         obj2 = &fp->scopeChain();
712     } else {
713         JS_ASSERT(!fun->isFlatClosure());
714
715         obj2 = GetScopeChainFast(cx, fp, JSOP_DEFFUN, JSOP_DEFFUN_LENGTH);
716         if (!obj2)
717             THROW();
718     }
719
720     /*
721      * If static link is not current scope, clone fun's object to link to the
722      * current scope via parent. We do this to enable sharing of compiled
723      * functions among multiple equivalent scopes, amortizing the cost of
724      * compilation over a number of executions.  Examples include XUL scripts
725      * and event handlers shared among Firefox or other Mozilla app chrome
726      * windows, and user-defined JS functions precompiled and then shared among
727      * requests in server-side JS.
728      */
729     if (obj->getParent() != obj2) {
730         obj = CloneFunctionObject(cx, fun, obj2);
731         if (!obj)
732             THROW();
733     }
734
735     /*
736      * ECMA requires functions defined when entering Eval code to be
737      * impermanent.
738      */
739     uintN attrs = fp->isEvalFrame()
740                   ? JSPROP_ENUMERATE
741                   : JSPROP_ENUMERATE | JSPROP_PERMANENT;
742
743     /*
744      * We define the function as a property of the variable object and not the
745      * current scope chain even for the case of function expression statements
746      * and functions defined by eval inside let or with blocks.
747      */
748     JSObject *parent = &fp->varobj(cx);
749
750     /* ES5 10.5 (NB: with subsequent errata). */
751     jsid id = ATOM_TO_JSID(fun->atom);
752     JSProperty *prop = NULL;
753     JSObject *pobj;
754     if (!parent->lookupProperty(cx, id, &pobj, &prop))
755         THROW();
756
757     Value rval = ObjectValue(*obj);
758
759     do {
760         /* Steps 5d, 5f. */
761         if (!prop || pobj != parent) {
762             if (!parent->defineProperty(cx, id, rval, PropertyStub, StrictPropertyStub, attrs))
763                 THROW();
764             break;
765         }
766
767         /* Step 5e. */
768         JS_ASSERT(parent->isNative());
769         Shape *shape = reinterpret_cast<Shape *>(prop);
770         if (parent->isGlobal()) {
771             if (shape->configurable()) {
772                 if (!parent->defineProperty(cx, id, rval, PropertyStub, StrictPropertyStub, attrs))
773                     THROW();
774                 break;
775             }
776
777             if (shape->isAccessorDescriptor() || !shape->writable() || !shape->enumerable()) {
778                 JSAutoByteString bytes;
779                 if (const char *name = js_ValueToPrintable(cx, IdToValue(id), &bytes)) {
780                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
781                                          JSMSG_CANT_REDEFINE_PROP, name);
782                 }
783                 THROW();
784             }
785         }
786
787         /*
788          * Non-global properties, and global properties which we aren't simply
789          * redefining, must be set.  First, this preserves their attributes.
790          * Second, this will produce warnings and/or errors as necessary if the
791          * specified Call object property is not writable (const).
792          */
793
794         /* Step 5f. */
795         if (!parent->setProperty(cx, id, &rval, strict))
796             THROW();
797     } while (false);
798 }
799
800 template void JS_FASTCALL stubs::DefFun<true>(VMFrame &f, JSFunction *fun);
801 template void JS_FASTCALL stubs::DefFun<false>(VMFrame &f, JSFunction *fun);
802
803 #define DEFAULT_VALUE(cx, n, hint, v)                                         \
804     JS_BEGIN_MACRO                                                            \
805         JS_ASSERT(v.isObject());                                              \
806         JS_ASSERT(v == regs.sp[n]);                                           \
807         if (!DefaultValue(cx, &v.toObject(), hint, &regs.sp[n]))              \
808             THROWV(JS_FALSE);                                                 \
809         v = regs.sp[n];                                                       \
810     JS_END_MACRO
811
812 #define RELATIONAL(OP)                                                        \
813     JS_BEGIN_MACRO                                                            \
814         JSContext *cx = f.cx;                                                 \
815         JSFrameRegs &regs = f.regs;                                           \
816         Value rval = regs.sp[-1];                                             \
817         Value lval = regs.sp[-2];                                             \
818         bool cond;                                                            \
819         if (lval.isObject())                                                  \
820             DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval);                       \
821         if (rval.isObject())                                                  \
822             DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval);                       \
823         if (lval.isString() && rval.isString()) {                             \
824             JSString *l = lval.toString(), *r = rval.toString();              \
825             JSBool cmp;                                                       \
826             if (!CompareStrings(cx, l, r, &cmp))                              \
827                 THROWV(JS_FALSE);                                             \
828             cond = cmp OP 0;                                                  \
829         } else {                                                              \
830             double l, r;                                                      \
831             if (!ValueToNumber(cx, lval, &l) ||                               \
832                 !ValueToNumber(cx, rval, &r)) {                               \
833                 THROWV(JS_FALSE);                                             \
834             }                                                                 \
835             cond = JSDOUBLE_COMPARE(l, OP, r, false);                         \
836         }                                                                     \
837         regs.sp[-2].setBoolean(cond);                                         \
838         return cond;                                                          \
839     JS_END_MACRO
840
841 JSBool JS_FASTCALL
842 stubs::LessThan(VMFrame &f)
843 {
844     RELATIONAL(<);
845 }
846
847 JSBool JS_FASTCALL
848 stubs::LessEqual(VMFrame &f)
849 {
850     RELATIONAL(<=);
851 }
852
853 JSBool JS_FASTCALL
854 stubs::GreaterThan(VMFrame &f)
855 {
856     RELATIONAL(>);
857 }
858
859 JSBool JS_FASTCALL
860 stubs::GreaterEqual(VMFrame &f)
861 {
862     RELATIONAL(>=);
863 }
864
865 JSBool JS_FASTCALL
866 stubs::ValueToBoolean(VMFrame &f)
867 {
868     return js_ValueToBoolean(f.regs.sp[-1]);
869 }
870
871 void JS_FASTCALL
872 stubs::Not(VMFrame &f)
873 {
874     JSBool b = !js_ValueToBoolean(f.regs.sp[-1]);
875     f.regs.sp[-1].setBoolean(b);
876 }
877
878 template <JSBool EQ, bool IFNAN>
879 static inline bool
880 StubEqualityOp(VMFrame &f)
881 {
882     JSContext *cx = f.cx;
883     JSFrameRegs &regs = f.regs;
884
885     Value rval = regs.sp[-1];
886     Value lval = regs.sp[-2];
887
888     JSBool cond;
889
890     /* The string==string case is easily the hottest;  try it first. */
891     if (lval.isString() && rval.isString()) {
892         JSString *l = lval.toString();
893         JSString *r = rval.toString();
894         JSBool equal;
895         if (!EqualStrings(cx, l, r, &equal))
896             return false;
897         cond = equal == EQ;
898     } else
899 #if JS_HAS_XML_SUPPORT
900     if ((lval.isObject() && lval.toObject().isXML()) ||
901         (rval.isObject() && rval.toObject().isXML())) {
902         if (!js_TestXMLEquality(cx, lval, rval, &cond))
903             return false;
904         cond = cond == EQ;
905     } else
906 #endif
907
908     if (SameType(lval, rval)) {
909         JS_ASSERT(!lval.isString());    /* this case is handled above */
910         if (lval.isDouble()) {
911             double l = lval.toDouble();
912             double r = rval.toDouble();
913             if (EQ)
914                 cond = JSDOUBLE_COMPARE(l, ==, r, IFNAN);
915             else
916                 cond = JSDOUBLE_COMPARE(l, !=, r, IFNAN);
917         } else if (lval.isObject()) {
918             JSObject *l = &lval.toObject(), *r = &rval.toObject();
919             l->assertSpecialEqualitySynced();
920             if (EqualityOp eq = l->getClass()->ext.equality) {
921                 if (!eq(cx, l, &rval, &cond))
922                     return false;
923                 cond = cond == EQ;
924             } else {
925                 cond = (l == r) == EQ;
926             }
927         } else if (lval.isNullOrUndefined()) {
928             cond = EQ;
929         } else {
930             cond = (lval.payloadAsRawUint32() == rval.payloadAsRawUint32()) == EQ;
931         }
932     } else {
933         if (lval.isNullOrUndefined()) {
934             cond = rval.isNullOrUndefined() == EQ;
935         } else if (rval.isNullOrUndefined()) {
936             cond = !EQ;
937         } else {
938             if (lval.isObject()) {
939                 if (!DefaultValue(cx, &lval.toObject(), JSTYPE_VOID, &regs.sp[-2]))
940                     return false;
941                 lval = regs.sp[-2];
942             }
943
944             if (rval.isObject()) {
945                 if (!DefaultValue(cx, &rval.toObject(), JSTYPE_VOID, &regs.sp[-1]))
946                     return false;
947                 rval = regs.sp[-1];
948             }
949
950             /*
951              * The string==string case is repeated because DefaultValue() can
952              * convert lval/rval to strings.
953              */
954             if (lval.isString() && rval.isString()) {
955                 JSString *l = lval.toString();
956                 JSString *r = rval.toString();
957                 JSBool equal;
958                 if (!EqualStrings(cx, l, r, &equal))
959                     return false;
960                 cond = equal == EQ;
961             } else {
962                 double l, r;
963                 if (!ValueToNumber(cx, lval, &l) ||
964                     !ValueToNumber(cx, rval, &r)) {
965                     return false;
966                 }
967
968                 if (EQ)
969                     cond = JSDOUBLE_COMPARE(l, ==, r, false);
970                 else
971                     cond = JSDOUBLE_COMPARE(l, !=, r, true);
972             }
973         }
974     }
975
976     regs.sp[-2].setBoolean(cond);
977     return true;
978 }
979
980 JSBool JS_FASTCALL
981 stubs::Equal(VMFrame &f)
982 {
983     if (!StubEqualityOp<JS_TRUE, false>(f))
984         THROWV(JS_FALSE);
985     return f.regs.sp[-2].toBoolean();
986 }
987
988 JSBool JS_FASTCALL
989 stubs::NotEqual(VMFrame &f)
990 {
991     if (!StubEqualityOp<JS_FALSE, true>(f))
992         THROWV(JS_FALSE);
993     return f.regs.sp[-2].toBoolean();
994 }
995
996 static inline bool
997 DefaultValue(VMFrame &f, JSType hint, Value &v, int n)
998 {
999     JS_ASSERT(v.isObject());
1000     if (!DefaultValue(f.cx, &v.toObject(), hint, &f.regs.sp[n]))
1001         return false;
1002     v = f.regs.sp[n];
1003     return true;
1004 }
1005
1006 void JS_FASTCALL
1007 stubs::Add(VMFrame &f)
1008 {
1009     JSContext *cx = f.cx;
1010     JSFrameRegs &regs = f.regs;
1011     Value rval = regs.sp[-1];
1012     Value lval = regs.sp[-2];
1013
1014     /* The string + string case is easily the hottest;  try it first. */
1015     bool lIsString = lval.isString();
1016     bool rIsString = rval.isString();
1017     JSString *lstr, *rstr;
1018     if (lIsString && rIsString) {
1019         lstr = lval.toString();
1020         rstr = rval.toString();
1021         goto string_concat;
1022
1023     } else
1024 #if JS_HAS_XML_SUPPORT
1025     if (lval.isObject() && lval.toObject().isXML() &&
1026         rval.isObject() && rval.toObject().isXML()) {
1027         if (!js_ConcatenateXML(cx, &lval.toObject(), &rval.toObject(), &rval))
1028             THROW();
1029         regs.sp--;
1030         regs.sp[-1] = rval;
1031     } else
1032 #endif
1033     {
1034         /* These can convert lval/rval to strings. */
1035         if (lval.isObject() && !DefaultValue(f, JSTYPE_VOID, lval, -2))
1036             THROW();
1037         if (rval.isObject() && !DefaultValue(f, JSTYPE_VOID, rval, -1))
1038             THROW();
1039         if ((lIsString = lval.isString()) || (rIsString = rval.isString())) {
1040             if (lIsString) {
1041                 lstr = lval.toString();
1042             } else {
1043                 lstr = js_ValueToString(cx, lval);
1044                 if (!lstr)
1045                     THROW();
1046                 regs.sp[-2].setString(lstr);
1047             }
1048             if (rIsString) {
1049                 rstr = rval.toString();
1050             } else {
1051                 rstr = js_ValueToString(cx, rval);
1052                 if (!rstr)
1053                     THROW();
1054                 regs.sp[-1].setString(rstr);
1055             }
1056             goto string_concat;
1057
1058         } else {
1059             double l, r;
1060             if (!ValueToNumber(cx, lval, &l) || !ValueToNumber(cx, rval, &r))
1061                 THROW();
1062             l += r;
1063             regs.sp--;
1064             regs.sp[-1].setNumber(l);
1065         }
1066     }
1067     return;
1068
1069   string_concat:
1070     JSString *str = js_ConcatStrings(cx, lstr, rstr);
1071     if (!str)
1072         THROW();
1073     regs.sp--;
1074     regs.sp[-1].setString(str);
1075 }
1076
1077
1078 void JS_FASTCALL
1079 stubs::Sub(VMFrame &f)
1080 {
1081     JSContext *cx = f.cx;
1082     JSFrameRegs &regs = f.regs;
1083     double d1, d2;
1084     if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
1085         !ValueToNumber(cx, regs.sp[-1], &d2)) {
1086         THROW();
1087     }
1088     double d = d1 - d2;
1089     regs.sp[-2].setNumber(d);
1090 }
1091
1092 void JS_FASTCALL
1093 stubs::Mul(VMFrame &f)
1094 {
1095     JSContext *cx = f.cx;
1096     JSFrameRegs &regs = f.regs;
1097     double d1, d2;
1098     if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
1099         !ValueToNumber(cx, regs.sp[-1], &d2)) {
1100         THROW();
1101     }
1102     double d = d1 * d2;
1103     regs.sp[-2].setNumber(d);
1104 }
1105
1106 void JS_FASTCALL
1107 stubs::Div(VMFrame &f)
1108 {
1109     JSContext *cx = f.cx;
1110     JSRuntime *rt = cx->runtime;
1111     JSFrameRegs &regs = f.regs;
1112
1113     double d1, d2;
1114     if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
1115         !ValueToNumber(cx, regs.sp[-1], &d2)) {
1116         THROW();
1117     }
1118     if (d2 == 0) {
1119         const Value *vp;
1120 #ifdef XP_WIN
1121         /* XXX MSVC miscompiles such that (NaN == 0) */
1122         if (JSDOUBLE_IS_NaN(d2))
1123             vp = &rt->NaNValue;
1124         else
1125 #endif
1126         if (d1 == 0 || JSDOUBLE_IS_NaN(d1))
1127             vp = &rt->NaNValue;
1128         else if (JSDOUBLE_IS_NEG(d1) != JSDOUBLE_IS_NEG(d2))
1129             vp = &rt->negativeInfinityValue;
1130         else
1131             vp = &rt->positiveInfinityValue;
1132         regs.sp[-2] = *vp;
1133     } else {
1134         d1 /= d2;
1135         regs.sp[-2].setNumber(d1);
1136     }
1137 }
1138
1139 void JS_FASTCALL
1140 stubs::Mod(VMFrame &f)
1141 {
1142     JSContext *cx = f.cx;
1143     JSFrameRegs &regs = f.regs;
1144
1145     Value &lref = regs.sp[-2];
1146     Value &rref = regs.sp[-1];
1147     int32_t l, r;
1148     if (lref.isInt32() && rref.isInt32() &&
1149         (l = lref.toInt32()) >= 0 && (r = rref.toInt32()) > 0) {
1150         int32_t mod = l % r;
1151         regs.sp[-2].setInt32(mod);
1152     } else {
1153         double d1, d2;
1154         if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
1155             !ValueToNumber(cx, regs.sp[-1], &d2)) {
1156             THROW();
1157         }
1158         if (d2 == 0) {
1159             regs.sp[-2].setDouble(js_NaN);
1160         } else {
1161             d1 = js_fmod(d1, d2);
1162             regs.sp[-2].setDouble(d1);
1163         }
1164     }
1165 }
1166
1167 void JS_FASTCALL
1168 stubs::Debugger(VMFrame &f, jsbytecode *pc)
1169 {
1170     JSDebuggerHandler handler = f.cx->debugHooks->debuggerHandler;
1171     if (handler) {
1172         Value rval;
1173         switch (handler(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval),
1174                         f.cx->debugHooks->debuggerHandlerData)) {
1175           case JSTRAP_THROW:
1176             f.cx->setPendingException(rval);
1177             THROW();
1178
1179           case JSTRAP_RETURN:
1180             f.cx->clearPendingException();
1181             f.cx->fp()->setReturnValue(rval);
1182 #if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64)
1183             *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *,
1184                                          f.cx->jaegerCompartment()->forceReturnFastTrampoline());
1185 #else
1186             *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *,
1187                                          f.cx->jaegerCompartment()->forceReturnTrampoline());
1188 #endif
1189             break;
1190
1191           case JSTRAP_ERROR:
1192             f.cx->clearPendingException();
1193             THROW();
1194
1195           default:
1196             break;
1197         }
1198     }
1199 }
1200
1201 void JS_FASTCALL
1202 stubs::Interrupt(VMFrame &f, jsbytecode *pc)
1203 {
1204     if (!js_HandleExecutionInterrupt(f.cx))
1205         THROW();
1206 }
1207
1208 void JS_FASTCALL
1209 stubs::Trap(VMFrame &f, uint32 trapTypes)
1210 {
1211     Value rval;
1212     jsbytecode *pc = f.cx->regs->pc;
1213
1214     /*
1215      * Trap may be called for a single-step interrupt trap and/or a
1216      * regular trap. Try the single-step first, and if it lets control
1217      * flow through or does not exist, do the regular trap.
1218      */
1219     JSTrapStatus result = JSTRAP_CONTINUE;
1220     if (trapTypes & JSTRAP_SINGLESTEP) {
1221         /*
1222          * single step mode may be paused without recompiling by
1223          * setting the interruptHook to NULL.
1224          */
1225         JSInterruptHook hook = f.cx->debugHooks->interruptHook;
1226         if (hook)
1227             result = hook(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval),
1228                           f.cx->debugHooks->interruptHookData);
1229     }
1230
1231     if (result == JSTRAP_CONTINUE && (trapTypes & JSTRAP_TRAP))
1232         result = JS_HandleTrap(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval));
1233
1234     switch (result) {
1235       case JSTRAP_THROW:
1236         f.cx->setPendingException(rval);
1237         THROW();
1238
1239       case JSTRAP_RETURN:
1240         f.cx->clearPendingException();
1241         f.cx->fp()->setReturnValue(rval);
1242 #if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64)
1243         *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *,
1244                                      f.cx->jaegerCompartment()->forceReturnFastTrampoline());
1245 #else
1246         *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *,
1247                                      f.cx->jaegerCompartment()->forceReturnTrampoline());
1248 #endif
1249         break;
1250
1251       case JSTRAP_ERROR:
1252         f.cx->clearPendingException();
1253         THROW();
1254
1255       default:
1256         break;
1257     }
1258 }
1259
1260 void JS_FASTCALL
1261 stubs::This(VMFrame &f)
1262 {
1263     if (!f.fp()->computeThis(f.cx))
1264         THROW();
1265     f.regs.sp[-1] = f.fp()->thisValue();
1266 }
1267
1268 void JS_FASTCALL
1269 stubs::Neg(VMFrame &f)
1270 {
1271     double d;
1272     if (!ValueToNumber(f.cx, f.regs.sp[-1], &d))
1273         THROW();
1274     d = -d;
1275     f.regs.sp[-1].setNumber(d);
1276 }
1277
1278 JSObject * JS_FASTCALL
1279 stubs::NewInitArray(VMFrame &f, uint32 count)
1280 {
1281     JSObject *obj = NewDenseAllocatedArray(f.cx, count);
1282     if (!obj)
1283         THROWV(NULL);
1284
1285     return obj;
1286 }
1287
1288 JSObject * JS_FASTCALL
1289 stubs::NewInitObject(VMFrame &f, JSObject *baseobj)
1290 {
1291     JSContext *cx = f.cx;
1292
1293     if (!baseobj) {
1294         gc::FinalizeKind kind = GuessObjectGCKind(0, false);
1295         JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
1296         if (!obj)
1297             THROWV(NULL);
1298         return obj;
1299     }
1300
1301     JSObject *obj = CopyInitializerObject(cx, baseobj);
1302
1303     if (!obj)
1304         THROWV(NULL);
1305     return obj;
1306 }
1307
1308 void JS_FASTCALL
1309 stubs::InitElem(VMFrame &f, uint32 last)
1310 {
1311     JSContext *cx = f.cx;
1312     JSFrameRegs &regs = f.regs;
1313
1314     /* Pop the element's value into rval. */
1315     JS_ASSERT(regs.sp - f.fp()->base() >= 3);
1316     const Value &rref = regs.sp[-1];
1317
1318     /* Find the object being initialized at top of stack. */
1319     const Value &lref = regs.sp[-3];
1320     JS_ASSERT(lref.isObject());
1321     JSObject *obj = &lref.toObject();
1322
1323     /* Fetch id now that we have obj. */
1324     jsid id;
1325     const Value &idval = regs.sp[-2];
1326     if (!FetchElementId(f, obj, idval, id, &regs.sp[-2]))
1327         THROW();
1328
1329     /*
1330      * If rref is a hole, do not call JSObject::defineProperty. In this case,
1331      * obj must be an array, so if the current op is the last element
1332      * initialiser, set the array length to one greater than id.
1333      */
1334     if (rref.isMagic(JS_ARRAY_HOLE)) {
1335         JS_ASSERT(obj->isArray());
1336         JS_ASSERT(JSID_IS_INT(id));
1337         JS_ASSERT(jsuint(JSID_TO_INT(id)) < JS_ARGS_LENGTH_MAX);
1338         if (last && !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1)))
1339             THROW();
1340     } else {
1341         if (!obj->defineProperty(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE))
1342             THROW();
1343     }
1344 }
1345
1346 void JS_FASTCALL
1347 stubs::GetUpvar(VMFrame &f, uint32 ck)
1348 {
1349     /* :FIXME: We can do better, this stub isn't needed. */
1350     uint32 staticLevel = f.fp()->script()->staticLevel;
1351     UpvarCookie cookie;
1352     cookie.fromInteger(ck);
1353     f.regs.sp[0] = GetUpvar(f.cx, staticLevel, cookie);
1354 }
1355
1356 JSObject * JS_FASTCALL
1357 stubs::DefLocalFun(VMFrame &f, JSFunction *fun)
1358 {
1359     /*
1360      * Define a local function (i.e., one nested at the top level of another
1361      * function), parented by the current scope chain, stored in a local
1362      * variable slot that the compiler allocated.  This is an optimization over
1363      * JSOP_DEFFUN that avoids requiring a call object for the outer function's
1364      * activation.
1365      */
1366     JS_ASSERT(fun->isInterpreted());
1367     JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
1368     JSObject *obj = FUN_OBJECT(fun);
1369
1370     if (FUN_NULL_CLOSURE(fun)) {
1371         obj = CloneFunctionObject(f.cx, fun, &f.fp()->scopeChain());
1372         if (!obj)
1373             THROWV(NULL);
1374     } else {
1375         JSObject *parent = GetScopeChainFast(f.cx, f.fp(), JSOP_DEFLOCALFUN,
1376                                              JSOP_DEFLOCALFUN_LENGTH);
1377         if (!parent)
1378             THROWV(NULL);
1379
1380         if (obj->getParent() != parent) {
1381             obj = CloneFunctionObject(f.cx, fun, parent);
1382             if (!obj)
1383                 THROWV(NULL);
1384         }
1385     }
1386
1387     return obj;
1388 }
1389
1390 JSObject * JS_FASTCALL
1391 stubs::DefLocalFun_FC(VMFrame &f, JSFunction *fun)
1392 {
1393     JSObject *obj = js_NewFlatClosure(f.cx, fun, JSOP_DEFLOCALFUN_FC, JSOP_DEFLOCALFUN_FC_LENGTH);
1394     if (!obj)
1395         THROWV(NULL);
1396     return obj;
1397 }
1398
1399 JSObject * JS_FASTCALL
1400 stubs::RegExp(VMFrame &f, JSObject *regex)
1401 {
1402     /*
1403      * Push a regexp object cloned from the regexp literal object mapped by the
1404      * bytecode at pc. ES5 finally fixed this bad old ES3 design flaw which was
1405      * flouted by many browser-based implementations.
1406      *
1407      * We avoid the GetScopeChain call here and pass fp->scopeChain() as
1408      * js_GetClassPrototype uses the latter only to locate the global.
1409      */
1410     JSObject *proto;
1411     if (!js_GetClassPrototype(f.cx, &f.fp()->scopeChain(), JSProto_RegExp, &proto))
1412         THROWV(NULL);
1413     JS_ASSERT(proto);
1414     JSObject *obj = js_CloneRegExpObject(f.cx, regex, proto);
1415     if (!obj)
1416         THROWV(NULL);
1417     return obj;
1418 }
1419
1420 JSObject * JS_FASTCALL
1421 stubs::LambdaForInit(VMFrame &f, JSFunction *fun)
1422 {
1423     JSObject *obj = FUN_OBJECT(fun);
1424     if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) {
1425         fun->setMethodAtom(f.fp()->script()->getAtom(GET_SLOTNO(f.regs.pc)));
1426         return obj;
1427     }
1428     return Lambda(f, fun);
1429 }
1430
1431 JSObject * JS_FASTCALL
1432 stubs::LambdaForSet(VMFrame &f, JSFunction *fun)
1433 {
1434     JSObject *obj = FUN_OBJECT(fun);
1435     if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) {
1436         const Value &lref = f.regs.sp[-1];
1437         if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) {
1438             fun->setMethodAtom(f.fp()->script()->getAtom(GET_SLOTNO(f.regs.pc)));
1439             return obj;
1440         }
1441     }
1442     return Lambda(f, fun);
1443 }
1444
1445 JSObject * JS_FASTCALL
1446 stubs::LambdaJoinableForCall(VMFrame &f, JSFunction *fun)
1447 {
1448     JSObject *obj = FUN_OBJECT(fun);
1449     if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) {
1450         /*
1451          * Array.prototype.sort and String.prototype.replace are
1452          * optimized as if they are special form. We know that they
1453          * won't leak the joined function object in obj, therefore
1454          * we don't need to clone that compiler- created function
1455          * object for identity/mutation reasons.
1456          */
1457         int iargc = GET_ARGC(f.regs.pc);
1458
1459         /*
1460          * Note that we have not yet pushed obj as the final argument,
1461          * so regs.sp[1 - (iargc + 2)], and not regs.sp[-(iargc + 2)],
1462          * is the callee for this JSOP_CALL.
1463          */
1464         const Value &cref = f.regs.sp[1 - (iargc + 2)];
1465         JSObject *callee;
1466
1467         if (IsFunctionObject(cref, &callee)) {
1468             JSFunction *calleeFun = callee->getFunctionPrivate();
1469             Native native = calleeFun->maybeNative();
1470
1471             if (native) {
1472                 if (iargc == 1 && native == array_sort)
1473                     return obj;
1474                 if (iargc == 2 && native == str_replace)
1475                     return obj;
1476             }
1477         }
1478     }
1479     return Lambda(f, fun);
1480 }
1481
1482 JSObject * JS_FASTCALL
1483 stubs::LambdaJoinableForNull(VMFrame &f, JSFunction *fun)
1484 {
1485     JSObject *obj = FUN_OBJECT(fun);
1486     if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) {
1487         jsbytecode *pc2 = f.regs.pc + JSOP_NULL_LENGTH;
1488         JSOp op2 = JSOp(*pc2);
1489
1490         if (op2 == JSOP_CALL && GET_ARGC(pc2) == 0)
1491             return obj;
1492     }
1493     return Lambda(f, fun);
1494 }
1495
1496 JSObject * JS_FASTCALL
1497 stubs::Lambda(VMFrame &f, JSFunction *fun)
1498 {
1499     JSObject *obj = FUN_OBJECT(fun);
1500
1501     JSObject *parent;
1502     if (FUN_NULL_CLOSURE(fun)) {
1503         parent = &f.fp()->scopeChain();
1504     } else {
1505         parent = GetScopeChainFast(f.cx, f.fp(), JSOP_LAMBDA, JSOP_LAMBDA_LENGTH);
1506         if (!parent)
1507             THROWV(NULL);
1508     }
1509
1510     obj = CloneFunctionObject(f.cx, fun, parent);
1511     if (!obj)
1512         THROWV(NULL);
1513
1514     return obj;
1515 }
1516
1517 /* Test whether v is an int in the range [-2^31 + 1, 2^31 - 2] */
1518 static JS_ALWAYS_INLINE bool
1519 CanIncDecWithoutOverflow(int32_t i)
1520 {
1521     return (i > JSVAL_INT_MIN) && (i < JSVAL_INT_MAX);
1522 }
1523
1524 template <int32 N, bool POST, JSBool strict>
1525 static inline bool
1526 ObjIncOp(VMFrame &f, JSObject *obj, jsid id)
1527 {
1528     JSContext *cx = f.cx;
1529     JSStackFrame *fp = f.fp();
1530
1531     f.regs.sp[0].setNull();
1532     f.regs.sp++;
1533     if (!obj->getProperty(cx, id, &f.regs.sp[-1]))
1534         return false;
1535
1536     Value &ref = f.regs.sp[-1];
1537     int32_t tmp;
1538     if (JS_LIKELY(ref.isInt32() && CanIncDecWithoutOverflow(tmp = ref.toInt32()))) {
1539         if (POST)
1540             ref.getInt32Ref() = tmp + N;
1541         else
1542             ref.getInt32Ref() = tmp += N;
1543         fp->setAssigning();
1544         JSBool ok = obj->setProperty(cx, id, &ref, strict);
1545         fp->clearAssigning();
1546         if (!ok)
1547             return false;
1548
1549         /*
1550          * We must set regs.sp[-1] to tmp for both post and pre increments
1551          * as the setter overwrites regs.sp[-1].
1552          */
1553         ref.setInt32(tmp);
1554     } else {
1555         Value v;
1556         double d;
1557         if (!ValueToNumber(cx, ref, &d))
1558             return false;
1559         if (POST) {
1560             ref.setDouble(d);
1561             d += N;
1562         } else {
1563             d += N;
1564             ref.setDouble(d);
1565         }
1566         v.setDouble(d);
1567         fp->setAssigning();
1568         JSBool ok = obj->setProperty(cx, id, &v, strict);
1569         fp->clearAssigning();
1570         if (!ok)
1571             return false;
1572     }
1573
1574     return true;
1575 }
1576
1577 template <int32 N, bool POST, JSBool strict>
1578 static inline bool
1579 NameIncDec(VMFrame &f, JSObject *obj, JSAtom *origAtom)
1580 {
1581     JSContext *cx = f.cx;
1582
1583     JSAtom *atom;
1584     JSObject *obj2;
1585     JSProperty *prop;
1586     PropertyCacheEntry *entry;
1587     JS_PROPERTY_CACHE(cx).test(cx, f.regs.pc, obj, obj2, entry, atom);
1588     if (!atom) {
1589         if (obj == obj2 && entry->vword.isSlot()) {
1590             uint32 slot = entry->vword.toSlot();
1591             Value &rref = obj->nativeGetSlotRef(slot);
1592             int32_t tmp;
1593             if (JS_LIKELY(rref.isInt32() && CanIncDecWithoutOverflow(tmp = rref.toInt32()))) {
1594                 int32_t inc = tmp + N;
1595                 if (!POST)
1596                     tmp = inc;
1597                 rref.getInt32Ref() = inc;
1598                 f.regs.sp[0].setInt32(tmp);
1599                 return true;
1600             }
1601         }
1602         atom = origAtom;
1603     }
1604
1605     jsid id = ATOM_TO_JSID(atom);
1606     if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
1607         return false;
1608     if (!prop) {
1609         ReportAtomNotDefined(cx, atom);
1610         return false;
1611     }
1612     return ObjIncOp<N, POST, strict>(f, obj, id);
1613 }
1614
1615 template<JSBool strict>
1616 void JS_FASTCALL
1617 stubs::PropInc(VMFrame &f, JSAtom *atom)
1618 {
1619     JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
1620     if (!obj)
1621         THROW();
1622     if (!ObjIncOp<1, true, strict>(f, obj, ATOM_TO_JSID(atom)))
1623         THROW();
1624     f.regs.sp[-2] = f.regs.sp[-1];
1625 }
1626
1627 template void JS_FASTCALL stubs::PropInc<true>(VMFrame &f, JSAtom *atom);
1628 template void JS_FASTCALL stubs::PropInc<false>(VMFrame &f, JSAtom *atom);
1629
1630 template<JSBool strict>
1631 void JS_FASTCALL
1632 stubs::PropDec(VMFrame &f, JSAtom *atom)
1633 {
1634     JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
1635     if (!obj)
1636         THROW();
1637     if (!ObjIncOp<-1, true, strict>(f, obj, ATOM_TO_JSID(atom)))
1638         THROW();
1639     f.regs.sp[-2] = f.regs.sp[-1];
1640 }
1641
1642 template void JS_FASTCALL stubs::PropDec<true>(VMFrame &f, JSAtom *atom);
1643 template void JS_FASTCALL stubs::PropDec<false>(VMFrame &f, JSAtom *atom);
1644
1645 template<JSBool strict>
1646 void JS_FASTCALL
1647 stubs::IncProp(VMFrame &f, JSAtom *atom)
1648 {
1649     JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
1650     if (!obj)
1651         THROW();
1652     if (!ObjIncOp<1, false, strict>(f, obj, ATOM_TO_JSID(atom)))
1653         THROW();
1654     f.regs.sp[-2] = f.regs.sp[-1];
1655 }
1656
1657 template void JS_FASTCALL stubs::IncProp<true>(VMFrame &f, JSAtom *atom);
1658 template void JS_FASTCALL stubs::IncProp<false>(VMFrame &f, JSAtom *atom);
1659
1660 template<JSBool strict>
1661 void JS_FASTCALL
1662 stubs::DecProp(VMFrame &f, JSAtom *atom)
1663 {
1664     JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
1665     if (!obj)
1666         THROW();
1667     if (!ObjIncOp<-1, false, strict>(f, obj, ATOM_TO_JSID(atom)))
1668         THROW();
1669     f.regs.sp[-2] = f.regs.sp[-1];
1670 }
1671
1672 template void JS_FASTCALL stubs::DecProp<true>(VMFrame &f, JSAtom *atom);
1673 template void JS_FASTCALL stubs::DecProp<false>(VMFrame &f, JSAtom *atom);
1674
1675 template<JSBool strict>
1676 void JS_FASTCALL
1677 stubs::ElemInc(VMFrame &f)
1678 {
1679     JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
1680     if (!obj)
1681         THROW();
1682     jsid id;
1683     if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
1684         THROW();
1685     if (!ObjIncOp<1, true, strict>(f, obj, id))
1686         THROW();
1687     f.regs.sp[-3] = f.regs.sp[-1];
1688 }
1689
1690 template void JS_FASTCALL stubs::ElemInc<true>(VMFrame &f);
1691 template void JS_FASTCALL stubs::ElemInc<false>(VMFrame &f);
1692
1693 template<JSBool strict>
1694 void JS_FASTCALL
1695 stubs::ElemDec(VMFrame &f)
1696 {
1697     JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
1698     if (!obj)
1699         THROW();
1700     jsid id;
1701     if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
1702         THROW();
1703     if (!ObjIncOp<-1, true, strict>(f, obj, id))
1704         THROW();
1705     f.regs.sp[-3] = f.regs.sp[-1];
1706 }
1707
1708 template void JS_FASTCALL stubs::ElemDec<true>(VMFrame &f);
1709 template void JS_FASTCALL stubs::ElemDec<false>(VMFrame &f);
1710
1711 template<JSBool strict>
1712 void JS_FASTCALL
1713 stubs::IncElem(VMFrame &f)
1714 {
1715     JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
1716     if (!obj)
1717         THROW();
1718     jsid id;
1719     if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
1720         THROW();
1721     if (!ObjIncOp<1, false, strict>(f, obj, id))
1722         THROW();
1723     f.regs.sp[-3] = f.regs.sp[-1];
1724 }
1725
1726 template void JS_FASTCALL stubs::IncElem<true>(VMFrame &f);
1727 template void JS_FASTCALL stubs::IncElem<false>(VMFrame &f);
1728
1729 template<JSBool strict>
1730 void JS_FASTCALL
1731 stubs::DecElem(VMFrame &f)
1732 {
1733     JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
1734     if (!obj)
1735         THROW();
1736     jsid id;
1737     if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
1738         THROW();
1739     if (!ObjIncOp<-1, false, strict>(f, obj, id))
1740         THROW();
1741     f.regs.sp[-3] = f.regs.sp[-1];
1742 }
1743
1744 template void JS_FASTCALL stubs::DecElem<true>(VMFrame &f);
1745 template void JS_FASTCALL stubs::DecElem<false>(VMFrame &f);
1746
1747 template<JSBool strict>
1748 void JS_FASTCALL
1749 stubs::NameInc(VMFrame &f, JSAtom *atom)
1750 {
1751     JSObject *obj = &f.fp()->scopeChain();
1752     if (!NameIncDec<1, true, strict>(f, obj, atom))
1753         THROW();
1754 }
1755
1756 template void JS_FASTCALL stubs::NameInc<true>(VMFrame &f, JSAtom *atom);
1757 template void JS_FASTCALL stubs::NameInc<false>(VMFrame &f, JSAtom *atom);
1758
1759 template<JSBool strict>
1760 void JS_FASTCALL
1761 stubs::NameDec(VMFrame &f, JSAtom *atom)
1762 {
1763     JSObject *obj = &f.fp()->scopeChain();
1764     if (!NameIncDec<-1, true, strict>(f, obj, atom))
1765         THROW();
1766 }
1767
1768 template void JS_FASTCALL stubs::NameDec<true>(VMFrame &f, JSAtom *atom);
1769 template void JS_FASTCALL stubs::NameDec<false>(VMFrame &f, JSAtom *atom);
1770
1771 template<JSBool strict>
1772 void JS_FASTCALL
1773 stubs::IncName(VMFrame &f, JSAtom *atom)
1774 {
1775     JSObject *obj = &f.fp()->scopeChain();
1776     if (!NameIncDec<1, false, strict>(f, obj, atom))
1777         THROW();
1778 }
1779
1780 template void JS_FASTCALL stubs::IncName<true>(VMFrame &f, JSAtom *atom);
1781 template void JS_FASTCALL stubs::IncName<false>(VMFrame &f, JSAtom *atom);
1782
1783 template<JSBool strict>
1784 void JS_FASTCALL
1785 stubs::DecName(VMFrame &f, JSAtom *atom)
1786 {
1787     JSObject *obj = &f.fp()->scopeChain();
1788     if (!NameIncDec<-1, false, strict>(f, obj, atom))
1789         THROW();
1790 }
1791
1792 template void JS_FASTCALL stubs::DecName<true>(VMFrame &f, JSAtom *atom);
1793 template void JS_FASTCALL stubs::DecName<false>(VMFrame &f, JSAtom *atom);
1794
1795 template<JSBool strict>
1796 void JS_FASTCALL
1797 stubs::GlobalNameInc(VMFrame &f, JSAtom *atom)
1798 {
1799     JSObject *obj = f.fp()->scopeChain().getGlobal();
1800     if (!NameIncDec<1, true, strict>(f, obj, atom))
1801         THROW();
1802 }
1803
1804 template void JS_FASTCALL stubs::GlobalNameInc<true>(VMFrame &f, JSAtom *atom);
1805 template void JS_FASTCALL stubs::GlobalNameInc<false>(VMFrame &f, JSAtom *atom);
1806
1807 template<JSBool strict>
1808 void JS_FASTCALL
1809 stubs::GlobalNameDec(VMFrame &f, JSAtom *atom)
1810 {
1811     JSObject *obj = f.fp()->scopeChain().getGlobal();
1812     if (!NameIncDec<-1, true, strict>(f, obj, atom))
1813         THROW();
1814 }
1815
1816 template void JS_FASTCALL stubs::GlobalNameDec<true>(VMFrame &f, JSAtom *atom);
1817 template void JS_FASTCALL stubs::GlobalNameDec<false>(VMFrame &f, JSAtom *atom);
1818
1819 template<JSBool strict>
1820 void JS_FASTCALL
1821 stubs::IncGlobalName(VMFrame &f, JSAtom *atom)
1822 {
1823     JSObject *obj = f.fp()->scopeChain().getGlobal();
1824     if (!NameIncDec<1, false, strict>(f, obj, atom))
1825         THROW();
1826 }
1827
1828 template void JS_FASTCALL stubs::IncGlobalName<true>(VMFrame &f, JSAtom *atom);
1829 template void JS_FASTCALL stubs::IncGlobalName<false>(VMFrame &f, JSAtom *atom);
1830
1831 template<JSBool strict>
1832 void JS_FASTCALL
1833 stubs::DecGlobalName(VMFrame &f, JSAtom *atom)
1834 {
1835     JSObject *obj = f.fp()->scopeChain().getGlobal();
1836     if (!NameIncDec<-1, false, strict>(f, obj, atom))
1837         THROW();
1838 }
1839
1840 template void JS_FASTCALL stubs::DecGlobalName<true>(VMFrame &f, JSAtom *atom);
1841 template void JS_FASTCALL stubs::DecGlobalName<false>(VMFrame &f, JSAtom *atom);
1842
1843 static bool JS_FASTCALL
1844 InlineGetProp(VMFrame &f)
1845 {
1846     JSContext *cx = f.cx;
1847     JSFrameRegs &regs = f.regs;
1848
1849     Value *vp = &f.regs.sp[-1];
1850     JSObject *obj = ValueToObject(f.cx, vp);
1851     if (!obj)
1852         return false;
1853
1854     Value rval;
1855     do {
1856         /*
1857          * We do not impose the method read barrier if in an imacro,
1858          * assuming any property gets it does (e.g., for 'toString'
1859          * from JSOP_NEW) will not be leaked to the calling script.
1860          */
1861         JSObject *aobj = js_GetProtoIfDenseArray(obj);
1862
1863         PropertyCacheEntry *entry;
1864         JSObject *obj2;
1865         JSAtom *atom;
1866         JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
1867         if (!atom) {
1868             if (entry->vword.isFunObj()) {
1869                 rval.setObject(entry->vword.toFunObj());
1870             } else if (entry->vword.isSlot()) {
1871                 uint32 slot = entry->vword.toSlot();
1872                 rval = obj2->nativeGetSlot(slot);
1873             } else {
1874                 JS_ASSERT(entry->vword.isShape());
1875                 const Shape *shape = entry->vword.toShape();
1876                 NATIVE_GET(cx, obj, obj2, shape,
1877                         f.fp()->hasImacropc() ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER,
1878                         &rval, return false);
1879             }
1880             break;
1881         }
1882
1883         jsid id = ATOM_TO_JSID(atom);
1884         if (JS_LIKELY(!aobj->getOps()->getProperty)
1885                 ? !js_GetPropertyHelper(cx, obj, id,
1886                     f.fp()->hasImacropc()
1887                     ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
1888                     : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER,
1889                     &rval)
1890                 : !obj->getProperty(cx, id, &rval)) {
1891             return false;
1892         }
1893     } while(0);
1894
1895     regs.sp[-1] = rval;
1896     return true;
1897 }
1898
1899 void JS_FASTCALL
1900 stubs::GetProp(VMFrame &f)
1901 {
1902     if (!InlineGetProp(f))
1903         THROW();
1904 }
1905
1906 void JS_FASTCALL
1907 stubs::GetPropNoCache(VMFrame &f, JSAtom *atom)
1908 {
1909     JSContext *cx = f.cx;
1910
1911     Value *vp = &f.regs.sp[-1];
1912     JSObject *obj = ValueToObject(cx, vp);
1913     if (!obj)
1914         THROW();
1915
1916     if (!obj->getProperty(cx, ATOM_TO_JSID(atom), vp))
1917         THROW();
1918 }
1919
1920 void JS_FASTCALL
1921 stubs::CallProp(VMFrame &f, JSAtom *origAtom)
1922 {
1923     JSContext *cx = f.cx;
1924     JSFrameRegs &regs = f.regs;
1925
1926     Value lval;
1927     lval = regs.sp[-1];
1928
1929     Value objv;
1930     if (lval.isObject()) {
1931         objv = lval;
1932     } else {
1933         JSProtoKey protoKey;
1934         if (lval.isString()) {
1935             protoKey = JSProto_String;
1936         } else if (lval.isNumber()) {
1937             protoKey = JSProto_Number;
1938         } else if (lval.isBoolean()) {
1939             protoKey = JSProto_Boolean;
1940         } else {
1941             JS_ASSERT(lval.isNull() || lval.isUndefined());
1942             js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
1943             THROW();
1944         }
1945         JSObject *pobj;
1946         if (!js_GetClassPrototype(cx, NULL, protoKey, &pobj))
1947             THROW();
1948         objv.setObject(*pobj);
1949     }
1950
1951     JSObject *aobj = js_GetProtoIfDenseArray(&objv.toObject());
1952     Value rval;
1953
1954     PropertyCacheEntry *entry;
1955     JSObject *obj2;
1956     JSAtom *atom;
1957     JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
1958     if (!atom) {
1959         if (entry->vword.isFunObj()) {
1960             rval.setObject(entry->vword.toFunObj());
1961         } else if (entry->vword.isSlot()) {
1962             uint32 slot = entry->vword.toSlot();
1963             rval = obj2->nativeGetSlot(slot);
1964         } else {
1965             JS_ASSERT(entry->vword.isShape());
1966             const Shape *shape = entry->vword.toShape();
1967             NATIVE_GET(cx, &objv.toObject(), obj2, shape, JSGET_NO_METHOD_BARRIER, &rval,
1968                        THROW());
1969         }
1970         regs.sp++;
1971         regs.sp[-2] = rval;
1972         regs.sp[-1] = lval;
1973     } else {
1974         /*
1975          * Cache miss: use the immediate atom that was loaded for us under
1976          * PropertyCache::test.
1977          */
1978         jsid id;
1979         id = ATOM_TO_JSID(origAtom);
1980
1981         regs.sp++;
1982         regs.sp[-1].setNull();
1983         if (lval.isObject()) {
1984             if (!js_GetMethod(cx, &objv.toObject(), id,
1985                               JS_LIKELY(!aobj->getOps()->getProperty)
1986                               ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
1987                               : JSGET_NO_METHOD_BARRIER,
1988                               &rval)) {
1989                 THROW();
1990             }
1991             regs.sp[-1] = objv;
1992             regs.sp[-2] = rval;
1993         } else {
1994             JS_ASSERT(!objv.toObject().getOps()->getProperty);
1995             if (!js_GetPropertyHelper(cx, &objv.toObject(), id,
1996                                       JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER,
1997                                       &rval)) {
1998                 THROW();
1999             }
2000             regs.sp[-1] = lval;
2001             regs.sp[-2] = rval;
2002         }
2003     }
2004 #if JS_HAS_NO_SUCH_METHOD
2005     if (JS_UNLIKELY(rval.isUndefined()) && regs.sp[-1].isObject()) {
2006         regs.sp[-2].setString(ATOM_TO_STRING(origAtom));
2007         if (!js_OnUnknownMethod(cx, regs.sp - 2))
2008             THROW();
2009     }
2010 #endif
2011 }
2012
2013 void JS_FASTCALL
2014 stubs::Length(VMFrame &f)
2015 {
2016     JSFrameRegs &regs = f.regs;
2017     Value *vp = &regs.sp[-1];
2018
2019     if (vp->isString()) {
2020         vp->setInt32(vp->toString()->length());
2021         return;
2022     } else if (vp->isObject()) {
2023         JSObject *obj = &vp->toObject();
2024         if (obj->isArray()) {
2025             jsuint length = obj->getArrayLength();
2026             regs.sp[-1].setNumber(length);
2027             return;
2028         } else if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
2029             uint32 length = obj->getArgsInitialLength();
2030             JS_ASSERT(length < INT32_MAX);
2031             regs.sp[-1].setInt32(int32_t(length));
2032             return;
2033         }
2034     }
2035
2036     if (!InlineGetProp(f))
2037         THROW();
2038 }
2039
2040 void JS_FASTCALL
2041 stubs::Iter(VMFrame &f, uint32 flags)
2042 {
2043     if (!js_ValueToIterator(f.cx, flags, &f.regs.sp[-1]))
2044         THROW();
2045     JS_ASSERT(!f.regs.sp[-1].isPrimitive());
2046 }
2047
2048 static void
2049 InitPropOrMethod(VMFrame &f, JSAtom *atom, JSOp op)
2050 {
2051     JSContext *cx = f.cx;
2052     JSRuntime *rt = cx->runtime;
2053     JSFrameRegs &regs = f.regs;
2054
2055     /* Load the property's initial value into rval. */
2056     JS_ASSERT(regs.sp - f.fp()->base() >= 2);
2057     Value rval;
2058     rval = regs.sp[-1];
2059
2060     /* Load the object being initialized into lval/obj. */
2061     JSObject *obj = &regs.sp[-2].toObject();
2062     JS_ASSERT(obj->isNative());
2063
2064     /*
2065      * Probe the property cache.
2066      *
2067      * We can not assume that the object created by JSOP_NEWINIT is still
2068      * single-threaded as the debugger can access it from other threads.
2069      * So check first.
2070      *
2071      * On a hit, if the cached shape has a non-default setter, it must be
2072      * __proto__. If shape->previous() != obj->lastProperty(), there must be a
2073      * repeated property name. The fast path does not handle these two cases.
2074      */
2075     PropertyCacheEntry *entry;
2076     const Shape *shape;
2077     if (JS_PROPERTY_CACHE(cx).testForInit(rt, regs.pc, obj, &shape, &entry) &&
2078         shape->hasDefaultSetter() &&
2079         shape->previous() == obj->lastProperty())
2080     {
2081         /* Fast path. Property cache hit. */
2082         uint32 slot = shape->slot;
2083
2084         JS_ASSERT(slot == obj->slotSpan());
2085         JS_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
2086         if (slot < obj->numSlots()) {
2087             JS_ASSERT(obj->getSlot(slot).isUndefined());
2088         } else {
2089             if (!obj->allocSlot(cx, &slot))
2090                 THROW();
2091             JS_ASSERT(slot == shape->slot);
2092         }
2093
2094         /* A new object, or one we just extended in a recent initprop op. */
2095         JS_ASSERT(!obj->lastProperty() ||
2096                   obj->shape() == obj->lastProperty()->shape);
2097         obj->extend(cx, shape);
2098
2099         /*
2100          * No method change check here because here we are adding a new
2101          * property, not updating an existing slot's value that might
2102          * contain a method of a branded shape.
2103          */
2104         obj->nativeSetSlot(slot, rval);
2105     } else {
2106         PCMETER(JS_PROPERTY_CACHE(cx).inipcmisses++);
2107
2108         /* Get the immediate property name into id. */
2109         jsid id = ATOM_TO_JSID(atom);
2110
2111         uintN defineHow = (op == JSOP_INITMETHOD)
2112                           ? JSDNP_CACHE_RESULT | JSDNP_SET_METHOD
2113                           : JSDNP_CACHE_RESULT;
2114         if (!(JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom)
2115               ? js_SetPropertyHelper(cx, obj, id, defineHow, &rval, false)
2116               : js_DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
2117                                         JSPROP_ENUMERATE, 0, 0, NULL,
2118                                         defineHow))) {
2119             THROW();
2120         }
2121     }
2122 }
2123
2124 void JS_FASTCALL
2125 stubs::InitProp(VMFrame &f, JSAtom *atom)
2126 {
2127     InitPropOrMethod(f, atom, JSOP_INITPROP);
2128 }
2129
2130 void JS_FASTCALL
2131 stubs::InitMethod(VMFrame &f, JSAtom *atom)
2132 {
2133     InitPropOrMethod(f, atom, JSOP_INITMETHOD);
2134 }
2135
2136 void JS_FASTCALL
2137 stubs::IterNext(VMFrame &f)
2138 {
2139     JS_ASSERT(f.regs.sp - 1 >= f.fp()->base());
2140     JS_ASSERT(f.regs.sp[-1].isObject());
2141
2142     JSObject *iterobj = &f.regs.sp[-1].toObject();
2143     f.regs.sp[0].setNull();
2144     f.regs.sp++;
2145     if (!js_IteratorNext(f.cx, iterobj, &f.regs.sp[-1]))
2146         THROW();
2147 }
2148
2149 JSBool JS_FASTCALL
2150 stubs::IterMore(VMFrame &f)
2151 {
2152     JS_ASSERT(f.regs.sp - 1 >= f.fp()->base());
2153     JS_ASSERT(f.regs.sp[-1].isObject());
2154
2155     Value v;
2156     JSObject *iterobj = &f.regs.sp[-1].toObject();
2157     if (!js_IteratorMore(f.cx, iterobj, &v))
2158         THROWV(JS_FALSE);
2159
2160     return v.toBoolean();
2161 }
2162
2163 void JS_FASTCALL
2164 stubs::EndIter(VMFrame &f)
2165 {
2166     JS_ASSERT(f.regs.sp - 1 >= f.fp()->base());
2167     if (!js_CloseIterator(f.cx, &f.regs.sp[-1].toObject()))
2168         THROW();
2169 }
2170
2171 JSString * JS_FASTCALL
2172 stubs::TypeOf(VMFrame &f)
2173 {
2174     const Value &ref = f.regs.sp[-1];
2175     JSType type = JS_TypeOfValue(f.cx, Jsvalify(ref));
2176     JSAtom *atom = f.cx->runtime->atomState.typeAtoms[type];
2177     return ATOM_TO_STRING(atom);
2178 }
2179
2180 void JS_FASTCALL
2181 stubs::StrictEq(VMFrame &f)
2182 {
2183     const Value &rhs = f.regs.sp[-1];
2184     const Value &lhs = f.regs.sp[-2];
2185     JSBool equal;
2186     if (!StrictlyEqual(f.cx, lhs, rhs, &equal))
2187         THROW();
2188     f.regs.sp--;
2189     f.regs.sp[-1].setBoolean(equal == JS_TRUE);
2190 }
2191
2192 void JS_FASTCALL
2193 stubs::StrictNe(VMFrame &f)
2194 {
2195     const Value &rhs = f.regs.sp[-1];
2196     const Value &lhs = f.regs.sp[-2];
2197     JSBool equal;
2198     if (!StrictlyEqual(f.cx, lhs, rhs, &equal))
2199         THROW();
2200     f.regs.sp--;
2201     f.regs.sp[-1].setBoolean(equal != JS_TRUE);
2202 }
2203
2204 void JS_FASTCALL
2205 stubs::Throw(VMFrame &f)
2206 {
2207     JSContext *cx = f.cx;
2208
2209     JS_ASSERT(!cx->isExceptionPending());
2210     cx->setPendingException(f.regs.sp[-1]);
2211     THROW();
2212 }
2213
2214 JSObject * JS_FASTCALL
2215 stubs::FlatLambda(VMFrame &f, JSFunction *fun)
2216 {
2217     JSObject *obj = js_NewFlatClosure(f.cx, fun, JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH);
2218     if (!obj)
2219         THROWV(NULL);
2220     return obj;
2221 }
2222
2223 void JS_FASTCALL
2224 stubs::Arguments(VMFrame &f)
2225 {
2226     f.regs.sp++;
2227     if (!js_GetArgsValue(f.cx, f.fp(), &f.regs.sp[-1]))
2228         THROW();
2229 }
2230
2231 JSBool JS_FASTCALL
2232 stubs::InstanceOf(VMFrame &f)
2233 {
2234     JSContext *cx = f.cx;
2235     JSFrameRegs &regs = f.regs;
2236
2237     const Value &rref = regs.sp[-1];
2238     if (rref.isPrimitive()) {
2239         js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
2240                             -1, rref, NULL);
2241         THROWV(JS_FALSE);
2242     }
2243     JSObject *obj = &rref.toObject();
2244     const Value &lref = regs.sp[-2];
2245     JSBool cond = JS_FALSE;
2246     if (!HasInstance(cx, obj, &lref, &cond))
2247         THROWV(JS_FALSE);
2248     f.regs.sp[-2].setBoolean(cond);
2249     return cond;
2250 }
2251
2252 void JS_FASTCALL
2253 stubs::FastInstanceOf(VMFrame &f)
2254 {
2255     const Value &lref = f.regs.sp[-1];
2256
2257     if (lref.isPrimitive()) {
2258         /*
2259          * Throw a runtime error if instanceof is called on a function that
2260          * has a non-object as its .prototype value.
2261          */
2262         js_ReportValueError(f.cx, JSMSG_BAD_PROTOTYPE, -1, f.regs.sp[-2], NULL);
2263         THROW();
2264     }
2265
2266     f.regs.sp[-3].setBoolean(js_IsDelegate(f.cx, &lref.toObject(), f.regs.sp[-3]));
2267 }
2268
2269 void JS_FASTCALL
2270 stubs::ArgCnt(VMFrame &f)
2271 {
2272     JSContext *cx = f.cx;
2273     JSRuntime *rt = cx->runtime;
2274     JSStackFrame *fp = f.fp();
2275
2276     jsid id = ATOM_TO_JSID(rt->atomState.lengthAtom);
2277     f.regs.sp++;
2278     if (!js_GetArgsProperty(cx, fp, id, &f.regs.sp[-1]))
2279         THROW();
2280 }
2281
2282 void JS_FASTCALL
2283 stubs::EnterBlock(VMFrame &f, JSObject *obj)
2284 {
2285     JSFrameRegs &regs = f.regs;
2286 #ifdef DEBUG
2287     JSStackFrame *fp = f.fp();
2288 #endif
2289
2290     JS_ASSERT(obj->isStaticBlock());
2291     JS_ASSERT(fp->base() + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp);
2292     Value *vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj);
2293     JS_ASSERT(regs.sp < vp);
2294     JS_ASSERT(vp <= fp->slots() + fp->script()->nslots);
2295     SetValueRangeToUndefined(regs.sp, vp);
2296     regs.sp = vp;
2297
2298 #ifdef DEBUG
2299     JSContext *cx = f.cx;
2300
2301     /*
2302      * The young end of fp->scopeChain() may omit blocks if we haven't closed
2303      * over them, but if there are any closure blocks on fp->scopeChain(), they'd
2304      * better be (clones of) ancestors of the block we're entering now;
2305      * anything else we should have popped off fp->scopeChain() when we left its
2306      * static scope.
2307      */
2308     JSObject *obj2 = &fp->scopeChain();
2309     Class *clasp;
2310     while ((clasp = obj2->getClass()) == &js_WithClass)
2311         obj2 = obj2->getParent();
2312     if (clasp == &js_BlockClass &&
2313         obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, fp)) {
2314         JSObject *youngestProto = obj2->getProto();
2315         JS_ASSERT(youngestProto->isStaticBlock());
2316         JSObject *parent = obj;
2317         while ((parent = parent->getParent()) != youngestProto)
2318             JS_ASSERT(parent);
2319     }
2320 #endif
2321 }
2322
2323 void JS_FASTCALL
2324 stubs::LeaveBlock(VMFrame &f, JSObject *blockChain)
2325 {
2326     JSContext *cx = f.cx;
2327     JSStackFrame *fp = f.fp();
2328
2329 #ifdef DEBUG
2330     JS_ASSERT(blockChain->isStaticBlock());
2331     uintN blockDepth = OBJ_BLOCK_DEPTH(cx, blockChain);
2332
2333     JS_ASSERT(blockDepth <= StackDepth(fp->script()));
2334 #endif
2335     /*
2336      * If we're about to leave the dynamic scope of a block that has been
2337      * cloned onto fp->scopeChain(), clear its private data, move its locals from
2338      * the stack into the clone, and pop it off the chain.
2339      */
2340     JSObject *obj = &fp->scopeChain();
2341     if (obj->getProto() == blockChain) {
2342         JS_ASSERT(obj->getClass() == &js_BlockClass);
2343         if (!js_PutBlockObject(cx, JS_TRUE))
2344             THROW();
2345     }
2346 }
2347
2348 void * JS_FASTCALL
2349 stubs::LookupSwitch(VMFrame &f, jsbytecode *pc)
2350 {
2351     jsbytecode *jpc = pc;
2352     JSScript *script = f.fp()->script();
2353     bool ctor = f.fp()->isConstructing();
2354
2355     /* This is correct because the compiler adjusts the stack beforehand. */
2356     Value lval = f.regs.sp[-1];
2357
2358     if (!lval.isPrimitive()) {
2359         void* native = script->nativeCodeForPC(ctor, pc + GET_JUMP_OFFSET(pc));
2360         JS_ASSERT(native);
2361         return native;
2362     }
2363
2364     JS_ASSERT(pc[0] == JSOP_LOOKUPSWITCH);
2365
2366     pc += JUMP_OFFSET_LEN;
2367     uint32 npairs = GET_UINT16(pc);
2368     pc += UINT16_LEN;
2369
2370     JS_ASSERT(npairs);
2371
2372     if (lval.isString()) {
2373         JSLinearString *str = lval.toString()->ensureLinear(f.cx);
2374         if (!str)
2375             THROWV(NULL);
2376         for (uint32 i = 1; i <= npairs; i++) {
2377             Value rval = script->getConst(GET_INDEX(pc));
2378             pc += INDEX_LEN;
2379             if (rval.isString()) {
2380                 JSLinearString *rhs = rval.toString()->assertIsLinear();
2381                 if (rhs == str || EqualStrings(str, rhs)) {
2382                     void* native = script->nativeCodeForPC(ctor,
2383                                                            jpc + GET_JUMP_OFFSET(pc));
2384                     JS_ASSERT(native);
2385                     return native;
2386                 }
2387             }
2388             pc += JUMP_OFFSET_LEN;
2389         }
2390     } else if (lval.isNumber()) {
2391         double d = lval.toNumber();
2392         for (uint32 i = 1; i <= npairs; i++) {
2393             Value rval = script->getConst(GET_INDEX(pc));
2394             pc += INDEX_LEN;
2395             if (rval.isNumber() && d == rval.toNumber()) {
2396                 void* native = script->nativeCodeForPC(ctor,
2397                                                        jpc + GET_JUMP_OFFSET(pc));
2398                 JS_ASSERT(native);
2399                 return native;
2400             }
2401             pc += JUMP_OFFSET_LEN;
2402         }
2403     } else {
2404         for (uint32 i = 1; i <= npairs; i++) {
2405             Value rval = script->getConst(GET_INDEX(pc));
2406             pc += INDEX_LEN;
2407             if (lval == rval) {
2408                 void* native = script->nativeCodeForPC(ctor,
2409                                                        jpc + GET_JUMP_OFFSET(pc));
2410                 JS_ASSERT(native);
2411                 return native;
2412             }
2413             pc += JUMP_OFFSET_LEN;
2414         }
2415     }
2416
2417     void* native = script->nativeCodeForPC(ctor, jpc + GET_JUMP_OFFSET(jpc));
2418     JS_ASSERT(native);
2419     return native;
2420 }
2421
2422 void * JS_FASTCALL
2423 stubs::TableSwitch(VMFrame &f, jsbytecode *origPc)
2424 {
2425     jsbytecode * const originalPC = origPc;
2426     jsbytecode *pc = originalPC;
2427     uint32 jumpOffset = GET_JUMP_OFFSET(pc);
2428     pc += JUMP_OFFSET_LEN;
2429
2430     /* Note: compiler adjusts the stack beforehand. */
2431     Value rval = f.regs.sp[-1];
2432
2433     jsint tableIdx;
2434     if (rval.isInt32()) {
2435         tableIdx = rval.toInt32();
2436     } else if (rval.isDouble()) {
2437         double d = rval.toDouble();
2438         if (d == 0) {
2439             /* Treat -0 (double) as 0. */
2440             tableIdx = 0;
2441         } else if (!JSDOUBLE_IS_INT32(d, (int32_t *)&tableIdx)) {
2442             goto finally;
2443         }
2444     } else {
2445         goto finally;
2446     }
2447
2448     {
2449         jsint low = GET_JUMP_OFFSET(pc);
2450         pc += JUMP_OFFSET_LEN;
2451         jsint high = GET_JUMP_OFFSET(pc);
2452         pc += JUMP_OFFSET_LEN;
2453
2454         tableIdx -= low;
2455         if ((jsuint) tableIdx < (jsuint)(high - low + 1)) {
2456             pc += JUMP_OFFSET_LEN * tableIdx;
2457             uint32 candidateOffset = GET_JUMP_OFFSET(pc);
2458             if (candidateOffset)
2459                 jumpOffset = candidateOffset;
2460         }
2461     }
2462
2463 finally:
2464     /* Provide the native address. */
2465     JSScript* script = f.fp()->script();
2466     void* native = script->nativeCodeForPC(f.fp()->isConstructing(),
2467                                            originalPC + jumpOffset);
2468     JS_ASSERT(native);
2469     return native;
2470 }
2471
2472 void JS_FASTCALL
2473 stubs::Unbrand(VMFrame &f)
2474 {
2475     const Value &thisv = f.regs.sp[-1];
2476     if (!thisv.isObject())
2477         return;
2478     JSObject *obj = &thisv.toObject();
2479     if (obj->isNative())
2480         obj->unbrand(f.cx);
2481 }
2482
2483 void JS_FASTCALL
2484 stubs::Pos(VMFrame &f)
2485 {
2486     if (!ValueToNumber(f.cx, &f.regs.sp[-1]))
2487         THROW();
2488 }
2489
2490 void JS_FASTCALL
2491 stubs::ArgSub(VMFrame &f, uint32 n)
2492 {
2493     jsid id = INT_TO_JSID(n);
2494     Value rval;
2495     if (!js_GetArgsProperty(f.cx, f.fp(), id, &rval))
2496         THROW();
2497     f.regs.sp[0] = rval;
2498 }
2499
2500 void JS_FASTCALL
2501 stubs::DelName(VMFrame &f, JSAtom *atom)
2502 {
2503     jsid id = ATOM_TO_JSID(atom);
2504     JSObject *obj, *obj2;
2505     JSProperty *prop;
2506     if (!js_FindProperty(f.cx, id, &obj, &obj2, &prop))
2507         THROW();
2508
2509     /* Strict mode code should never contain JSOP_DELNAME opcodes. */
2510     JS_ASSERT(!f.fp()->script()->strictModeCode);
2511
2512     /* ECMA says to return true if name is undefined or inherited. */
2513     f.regs.sp++;
2514     f.regs.sp[-1] = BooleanValue(true);
2515     if (prop) {
2516         if (!obj->deleteProperty(f.cx, id, &f.regs.sp[-1], false))
2517             THROW();
2518     }
2519 }
2520
2521 template<JSBool strict>
2522 void JS_FASTCALL
2523 stubs::DelProp(VMFrame &f, JSAtom *atom)
2524 {
2525     JSContext *cx = f.cx;
2526
2527     JSObject *obj = ValueToObject(cx, &f.regs.sp[-1]);
2528     if (!obj)
2529         THROW();
2530
2531     Value rval;
2532     if (!obj->deleteProperty(cx, ATOM_TO_JSID(atom), &rval, strict))
2533         THROW();
2534
2535     f.regs.sp[-1] = rval;
2536 }
2537
2538 template void JS_FASTCALL stubs::DelProp<true>(VMFrame &f, JSAtom *atom);
2539 template void JS_FASTCALL stubs::DelProp<false>(VMFrame &f, JSAtom *atom);
2540
2541 template<JSBool strict>
2542 void JS_FASTCALL
2543 stubs::DelElem(VMFrame &f)
2544 {
2545     JSContext *cx = f.cx;
2546
2547     JSObject *obj = ValueToObject(cx, &f.regs.sp[-2]);
2548     if (!obj)
2549         THROW();
2550
2551     jsid id;
2552     if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
2553         THROW();
2554
2555     if (!obj->deleteProperty(cx, id, &f.regs.sp[-2], strict))
2556         THROW();
2557 }
2558
2559 void JS_FASTCALL
2560 stubs::DefVarOrConst(VMFrame &f, JSAtom *atom)
2561 {
2562     JSContext *cx = f.cx;
2563     JSStackFrame *fp = f.fp();
2564
2565     JSObject *obj = &fp->varobj(cx);
2566     JS_ASSERT(!obj->getOps()->defineProperty);
2567     uintN attrs = JSPROP_ENUMERATE;
2568     if (!fp->isEvalFrame())
2569         attrs |= JSPROP_PERMANENT;
2570
2571     /* Lookup id in order to check for redeclaration problems. */
2572     jsid id = ATOM_TO_JSID(atom);
2573     bool shouldDefine;
2574     if (JSOp(*f.regs.pc) == JSOP_DEFVAR) {
2575         /*
2576          * Redundant declaration of a |var|, even one for a non-writable
2577          * property like |undefined| in ES5, does nothing.
2578          */
2579         JSProperty *prop;
2580         JSObject *obj2;
2581         if (!obj->lookupProperty(cx, id, &obj2, &prop))
2582             THROW();
2583         shouldDefine = (!prop || obj2 != obj);
2584     } else {
2585         JS_ASSERT(JSOp(*f.regs.pc) == JSOP_DEFCONST);
2586         attrs |= JSPROP_READONLY;
2587         if (!CheckRedeclaration(cx, obj, id, attrs))
2588             THROW();
2589
2590         /*
2591          * As attrs includes readonly, CheckRedeclaration can succeed only
2592          * if prop does not exist.
2593          */
2594         shouldDefine = true;
2595     }
2596
2597     /* Bind a variable only if it's not yet defined. */
2598     if (shouldDefine && 
2599         !js_DefineNativeProperty(cx, obj, id, UndefinedValue(), PropertyStub, StrictPropertyStub,
2600                                      attrs, 0, 0, NULL)) {
2601         THROW();
2602     }
2603 }
2604
2605 void JS_FASTCALL
2606 stubs::SetConst(VMFrame &f, JSAtom *atom)
2607 {
2608     JSContext *cx = f.cx;
2609     JSStackFrame *fp = f.fp();
2610
2611     JSObject *obj = &fp->varobj(cx);
2612     const Value &ref = f.regs.sp[-1];
2613     if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), ref,
2614                              PropertyStub, StrictPropertyStub,
2615                              JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) {
2616         THROW();
2617     }
2618 }
2619
2620 JSBool JS_FASTCALL
2621 stubs::In(VMFrame &f)
2622 {
2623     JSContext *cx = f.cx;
2624
2625     const Value &rref = f.regs.sp[-1];
2626     if (!rref.isObject()) {
2627         js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rref, NULL);
2628         THROWV(JS_FALSE);
2629     }
2630
2631     JSObject *obj = &rref.toObject();
2632     jsid id;
2633     if (!FetchElementId(f, obj, f.regs.sp[-2], id, &f.regs.sp[-2]))
2634         THROWV(JS_FALSE);
2635
2636     JSObject *obj2;
2637     JSProperty *prop;
2638     if (!obj->lookupProperty(cx, id, &obj2, &prop))
2639         THROWV(JS_FALSE);
2640
2641     return !!prop;
2642 }
2643
2644 template void JS_FASTCALL stubs::DelElem<true>(VMFrame &f);
2645 template void JS_FASTCALL stubs::DelElem<false>(VMFrame &f);
2646
2647 void JS_FASTCALL
2648 stubs::Exception(VMFrame &f)
2649 {
2650     f.regs.sp[0] = f.cx->getPendingException();
2651     f.cx->clearPendingException();
2652 }
2653 template <bool Clamped>
2654 int32 JS_FASTCALL
2655 stubs::ConvertToTypedInt(JSContext *cx, Value *vp)
2656 {
2657     JS_ASSERT(!vp->isInt32());
2658
2659     if (vp->isDouble()) {
2660         if (Clamped)
2661             return js_TypedArray_uint8_clamp_double(vp->toDouble());
2662         return js_DoubleToECMAInt32(vp->toDouble());
2663     }
2664
2665     if (vp->isNull() || vp->isObject() || vp->isUndefined())
2666         return 0;
2667
2668     if (vp->isBoolean())
2669         return vp->toBoolean() ? 1 : 0;
2670
2671     JS_ASSERT(vp->isString());
2672
2673     int32 i32 = 0;
2674 #ifdef DEBUG
2675     bool success = 
2676 #endif
2677         StringToNumberType<jsint>(cx, vp->toString(), &i32);
2678     JS_ASSERT(success);
2679
2680     return i32;
2681 }
2682
2683 template int32 JS_FASTCALL stubs::ConvertToTypedInt<true>(JSContext *, Value *);
2684 template int32 JS_FASTCALL stubs::ConvertToTypedInt<false>(JSContext *, Value *);
2685
2686 void JS_FASTCALL
2687 stubs::ConvertToTypedFloat(JSContext *cx, Value *vp)
2688 {
2689     JS_ASSERT(!vp->isDouble() && !vp->isInt32());
2690
2691     if (vp->isNull()) {
2692         vp->setDouble(0);
2693     } else if (vp->isObject() || vp->isUndefined()) {
2694         vp->setDouble(js_NaN);
2695     } else if (vp->isBoolean()) {
2696         vp->setDouble(vp->toBoolean() ? 1 : 0);
2697     } else {
2698         JS_ASSERT(vp->isString());
2699         double d = 0;
2700 #ifdef DEBUG
2701         bool success = 
2702 #endif
2703             StringToNumberType<double>(cx, vp->toString(), &d);
2704         JS_ASSERT(success);
2705         vp->setDouble(d);
2706     }
2707 }
2708