Compile JSOP_FORGLOBAL,ARG,PROP,NAME,ELEM (bug 597455, r=dmandelin).
[mozilla-central.git] / js / src / methodjit / StubCalls.cpp
blob80d34e39ca454f008e2408aab5b1b94da6d0c9c4
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 #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"
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"
69 #ifdef XP_WIN
70 # include "jswin.h"
71 #endif
73 #include "jsautooplen.h"
75 using namespace js;
76 using namespace js::mjit;
77 using namespace JSC;
79 void JS_FASTCALL
80 stubs::BindName(VMFrame &f)
82 PropertyCacheEntry *entry;
84 /* Fast-path should have caught this. See comment in interpreter. */
85 JS_ASSERT(f.fp()->scopeChain().getParent());
87 JSAtom *atom;
88 JSObject *obj2;
89 JSContext *cx = f.cx;
90 JSObject *obj = &f.fp()->scopeChain();
91 JS_PROPERTY_CACHE(cx).test(cx, f.regs.pc, obj, obj2, entry, atom);
92 if (atom) {
93 jsid id = ATOM_TO_JSID(atom);
94 obj = js_FindIdentifierBase(cx, &f.fp()->scopeChain(), id);
95 if (!obj)
96 THROW();
98 f.regs.sp++;
99 f.regs.sp[-1].setObject(*obj);
102 void JS_FASTCALL
103 stubs::BindNameNoCache(VMFrame &f, JSAtom *atom)
105 JSObject *obj = js_FindIdentifierBase(f.cx, &f.fp()->scopeChain(), ATOM_TO_JSID(atom));
106 if (!obj)
107 THROW();
108 f.regs.sp[0].setObject(*obj);
111 JSObject * JS_FASTCALL
112 stubs::BindGlobalName(VMFrame &f)
114 return f.fp()->scopeChain().getGlobal();
117 template<JSBool strict>
118 void JS_FASTCALL
119 stubs::SetName(VMFrame &f, JSAtom *origAtom)
121 JSContext *cx = f.cx;
123 Value rval = f.regs.sp[-1];
124 Value &lref = f.regs.sp[-2];
125 JSObject *obj = ValueToObject(cx, &lref);
126 if (!obj)
127 THROW();
129 do {
130 PropertyCache *cache = &JS_PROPERTY_CACHE(cx);
133 * Probe the property cache, specializing for two important
134 * set-property cases. First:
136 * function f(a, b, c) {
137 * var o = {p:a, q:b, r:c};
138 * return o;
141 * or similar real-world cases, which evolve a newborn native
142 * object predicatably through some bounded number of property
143 * additions. And second:
145 * o.p = x;
147 * in a frequently executed method or loop body, where p will
148 * (possibly after the first iteration) always exist in native
149 * object o.
151 PropertyCacheEntry *entry;
152 JSObject *obj2;
153 JSAtom *atom;
154 if (cache->testForSet(cx, f.regs.pc, obj, &entry, &obj2, &atom)) {
156 * Fast property cache hit, only partially confirmed by
157 * testForSet. We know that the entry applies to regs.pc and
158 * that obj's shape matches.
160 * The entry predicts either a new property to be added
161 * directly to obj by this set, or on an existing "own"
162 * property, or on a prototype property that has a setter.
164 const Shape *shape = entry->vword.toShape();
165 JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable());
166 JS_ASSERT_IF(shape->hasSlot(), entry->vcapTag() == 0);
169 * Fastest path: check whether obj already has the cached shape and
170 * call NATIVE_SET and break to get out of the do-while(0). But we
171 * can call NATIVE_SET only for a direct or proto-setter hit.
173 if (!entry->adding()) {
174 if (entry->vcapTag() == 0 ||
175 ((obj2 = obj->getProto()) && obj2->shape() == entry->vshape()))
177 #ifdef DEBUG
178 if (entry->directHit()) {
179 JS_ASSERT(obj->nativeContains(*shape));
180 } else {
181 JS_ASSERT(obj2->nativeContains(*shape));
182 JS_ASSERT(entry->vcapTag() == 1);
183 JS_ASSERT(entry->kshape != entry->vshape());
184 JS_ASSERT(!shape->hasSlot());
186 #endif
188 PCMETER(cache->pchits++);
189 PCMETER(cache->setpchits++);
190 NATIVE_SET(cx, obj, shape, entry, &rval);
191 break;
193 } else {
194 JS_ASSERT(obj->isExtensible());
196 if (obj->nativeEmpty()) {
197 if (!obj->ensureClassReservedSlotsForEmptyObject(cx))
198 THROW();
201 uint32 slot;
202 if (shape->previous() == obj->lastProperty() &&
203 entry->vshape() == cx->runtime->protoHazardShape &&
204 shape->hasDefaultSetter()) {
205 slot = shape->slot;
206 JS_ASSERT(slot == obj->slotSpan());
209 * Fast path: adding a plain old property that was once at
210 * the frontier of the property tree, whose slot is next to
211 * claim among the already-allocated slots in obj, where
212 * shape->table has not been created yet.
214 PCMETER(cache->pchits++);
215 PCMETER(cache->addpchits++);
217 if (slot < obj->numSlots()) {
218 JS_ASSERT(obj->getSlot(slot).isUndefined());
219 } else {
220 if (!obj->allocSlot(cx, &slot))
221 THROW();
222 JS_ASSERT(slot == shape->slot);
225 /* Simply extend obj's property tree path with shape! */
226 obj->extend(cx, shape);
229 * No method change check here because here we are adding a
230 * new property, not updating an existing slot's value that
231 * might contain a method of a branded shape.
233 obj->setSlot(slot, rval);
236 * Purge the property cache of the id we may have just
237 * shadowed in obj's scope and proto chains.
239 js_PurgeScopeChain(cx, obj, shape->id);
240 break;
243 PCMETER(cache->setpcmisses++);
244 atom = NULL;
245 } else if (!atom) {
247 * Slower property cache hit, fully confirmed by testForSet (in the
248 * slow path, via fullTest).
250 const Shape *shape = NULL;
251 if (obj == obj2) {
252 shape = entry->vword.toShape();
253 JS_ASSERT(shape->writable());
254 JS_ASSERT(obj2->isExtensible());
255 NATIVE_SET(cx, obj, shape, entry, &rval);
257 if (shape)
258 break;
261 if (!atom)
262 atom = origAtom;
263 jsid id = ATOM_TO_JSID(atom);
264 if (entry && JS_LIKELY(!obj->getOps()->setProperty)) {
265 uintN defineHow;
266 JSOp op = JSOp(*f.regs.pc);
267 if (op == JSOP_SETMETHOD)
268 defineHow = JSDNP_CACHE_RESULT | JSDNP_SET_METHOD;
269 else if (op == JSOP_SETNAME)
270 defineHow = JSDNP_CACHE_RESULT | JSDNP_UNQUALIFIED;
271 else
272 defineHow = JSDNP_CACHE_RESULT;
273 if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rval, strict))
274 THROW();
275 } else {
276 if (!obj->setProperty(cx, id, &rval, strict))
277 THROW();
279 } while (0);
281 f.regs.sp[-2] = f.regs.sp[-1];
284 template void JS_FASTCALL stubs::SetName<true>(VMFrame &f, JSAtom *origAtom);
285 template void JS_FASTCALL stubs::SetName<false>(VMFrame &f, JSAtom *origAtom);
287 template<JSBool strict>
288 void JS_FASTCALL
289 stubs::SetPropNoCache(VMFrame &f, JSAtom *atom)
291 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
292 if (!obj)
293 THROW();
294 Value rval = f.regs.sp[-1];
295 if (!obj->setProperty(f.cx, ATOM_TO_JSID(atom), &f.regs.sp[-1], strict))
296 THROW();
297 f.regs.sp[-2] = rval;
300 template void JS_FASTCALL stubs::SetPropNoCache<true>(VMFrame &f, JSAtom *origAtom);
301 template void JS_FASTCALL stubs::SetPropNoCache<false>(VMFrame &f, JSAtom *origAtom);
303 template<JSBool strict>
304 void JS_FASTCALL
305 stubs::SetGlobalNameDumb(VMFrame &f, JSAtom *atom)
307 JSContext *cx = f.cx;
309 Value rval = f.regs.sp[-1];
310 Value &lref = f.regs.sp[-2];
311 JSObject *obj = ValueToObject(cx, &lref);
312 if (!obj)
313 THROW();
314 jsid id = ATOM_TO_JSID(atom);
315 if (!obj->setProperty(cx, id, &rval, strict))
316 THROW();
318 f.regs.sp[-2] = f.regs.sp[-1];
321 template void JS_FASTCALL stubs::SetGlobalNameDumb<true>(VMFrame &f, JSAtom *atom);
322 template void JS_FASTCALL stubs::SetGlobalNameDumb<false>(VMFrame &f, JSAtom *atom);
324 template<JSBool strict>
325 void JS_FASTCALL
326 stubs::SetGlobalName(VMFrame &f, JSAtom *atom)
328 SetName<strict>(f, atom);
331 template void JS_FASTCALL stubs::SetGlobalName<true>(VMFrame &f, JSAtom *atom);
332 template void JS_FASTCALL stubs::SetGlobalName<false>(VMFrame &f, JSAtom *atom);
334 static JSObject *
335 NameOp(VMFrame &f, JSObject *obj, bool callname = false)
337 JSContext *cx = f.cx;
339 const Shape *shape;
340 Value rval;
342 PropertyCacheEntry *entry;
343 JSObject *obj2;
344 JSAtom *atom;
345 JS_PROPERTY_CACHE(cx).test(cx, f.regs.pc, obj, obj2, entry, atom);
346 if (!atom) {
347 if (entry->vword.isFunObj()) {
348 f.regs.sp++;
349 f.regs.sp[-1].setObject(entry->vword.toFunObj());
350 } else if (entry->vword.isSlot()) {
351 uintN slot = entry->vword.toSlot();
352 f.regs.sp++;
353 f.regs.sp[-1] = obj2->nativeGetSlot(slot);
354 } else {
355 JS_ASSERT(entry->vword.isShape());
356 shape = entry->vword.toShape();
357 NATIVE_GET(cx, obj, obj2, shape, JSGET_METHOD_BARRIER, &rval, return NULL);
358 f.regs.sp++;
359 f.regs.sp[-1] = rval;
363 * Push results, the same as below, but with a prop$ hit there
364 * is no need to test for the unusual and uncacheable case where
365 * the caller determines |this|.
367 #if DEBUG
368 Class *clasp;
369 JS_ASSERT(!obj->getParent() ||
370 (clasp = obj->getClass()) == &js_CallClass ||
371 clasp == &js_BlockClass ||
372 clasp == &js_DeclEnvClass);
373 #endif
374 if (callname) {
375 f.regs.sp++;
376 f.regs.sp[-1].setUndefined();
378 return obj;
381 jsid id;
382 id = ATOM_TO_JSID(atom);
383 JSProperty *prop;
384 if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
385 return NULL;
386 if (!prop) {
387 /* Kludge to allow (typeof foo == "undefined") tests. */
388 JSOp op2 = js_GetOpcode(cx, f.fp()->script(), f.regs.pc + JSOP_NAME_LENGTH);
389 if (op2 == JSOP_TYPEOF) {
390 f.regs.sp++;
391 f.regs.sp[-1].setUndefined();
392 return obj;
394 ReportAtomNotDefined(cx, atom);
395 return NULL;
398 /* Take the slow path if prop was not found in a native object. */
399 if (!obj->isNative() || !obj2->isNative()) {
400 if (!obj->getProperty(cx, id, &rval))
401 return NULL;
402 } else {
403 shape = (Shape *)prop;
404 JSObject *normalized = obj;
405 if (normalized->getClass() == &js_WithClass && !shape->hasDefaultGetter())
406 normalized = js_UnwrapWithObject(cx, normalized);
407 NATIVE_GET(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, &rval, return NULL);
410 f.regs.sp++;
411 f.regs.sp[-1] = rval;
412 if (callname) {
413 Class *clasp;
414 JSObject *thisp = obj;
415 if (!thisp->getParent() ||
416 (clasp = thisp->getClass()) == &js_CallClass ||
417 clasp == &js_BlockClass ||
418 clasp == &js_DeclEnvClass) {
419 f.regs.sp++;
420 f.regs.sp[-1].setUndefined();
421 } else {
422 thisp = thisp->thisObject(cx);
423 if (!thisp)
424 return NULL;
425 f.regs.sp++;
426 f.regs.sp[-1].setObject(*thisp);
429 return obj;
432 void JS_FASTCALL
433 stubs::Name(VMFrame &f)
435 if (!NameOp(f, &f.fp()->scopeChain()))
436 THROW();
439 void JS_FASTCALL
440 stubs::GetGlobalName(VMFrame &f)
442 JSObject *globalObj = f.fp()->scopeChain().getGlobal();
443 if (!NameOp(f, globalObj))
444 THROW();
447 void JS_FASTCALL
448 stubs::GetElem(VMFrame &f)
450 JSContext *cx = f.cx;
451 JSFrameRegs &regs = f.regs;
453 Value &lref = regs.sp[-2];
454 Value &rref = regs.sp[-1];
455 if (lref.isString() && rref.isInt32()) {
456 JSString *str = lref.toString();
457 int32_t i = rref.toInt32();
458 if ((size_t)i < str->length()) {
459 str = JSString::getUnitString(cx, str, (size_t)i);
460 if (!str)
461 THROW();
462 f.regs.sp[-2].setString(str);
463 return;
467 JSObject *obj = ValueToObject(cx, &lref);
468 if (!obj)
469 THROW();
471 const Value *copyFrom;
472 Value rval;
473 jsid id;
474 if (rref.isInt32()) {
475 int32_t i = rref.toInt32();
476 if (obj->isDenseArray()) {
477 jsuint idx = jsuint(i);
479 if (idx < obj->getArrayLength() &&
480 idx < obj->getDenseArrayCapacity()) {
481 copyFrom = obj->addressOfDenseArrayElement(idx);
482 if (!copyFrom->isMagic())
483 goto end_getelem;
485 } else if (obj->isArguments()) {
486 uint32 arg = uint32(i);
488 if (arg < obj->getArgsInitialLength()) {
489 copyFrom = obj->addressOfArgsElement(arg);
490 if (!copyFrom->isMagic()) {
491 if (JSStackFrame *afp = (JSStackFrame *) obj->getPrivate())
492 copyFrom = &afp->canonicalActualArg(arg);
493 goto end_getelem;
497 if (JS_LIKELY(INT_FITS_IN_JSID(i)))
498 id = INT_TO_JSID(i);
499 else
500 goto intern_big_int;
502 } else {
503 intern_big_int:
504 if (!js_InternNonIntElementId(cx, obj, rref, &id))
505 THROW();
508 if (!obj->getProperty(cx, id, &rval))
509 THROW();
510 copyFrom = &rval;
512 end_getelem:
513 f.regs.sp[-2] = *copyFrom;
516 static inline bool
517 FetchElementId(VMFrame &f, JSObject *obj, const Value &idval, jsid &id, Value *vp)
519 int32_t i_;
520 if (ValueFitsInInt32(idval, &i_) && INT_FITS_IN_JSID(i_)) {
521 id = INT_TO_JSID(i_);
522 return true;
524 return !!js_InternNonIntElementId(f.cx, obj, idval, &id, vp);
527 void JS_FASTCALL
528 stubs::CallElem(VMFrame &f)
530 JSContext *cx = f.cx;
531 JSFrameRegs &regs = f.regs;
533 /* Find the object on which to look for |this|'s properties. */
534 Value thisv = regs.sp[-2];
535 JSObject *thisObj = ValuePropertyBearer(cx, thisv, -2);
536 if (!thisObj)
537 THROW();
539 /* Fetch index and convert it to id suitable for use with thisObj. */
540 jsid id;
541 if (!FetchElementId(f, thisObj, regs.sp[-1], id, &regs.sp[-2]))
542 THROW();
544 /* Get or set the element. */
545 if (!js_GetMethod(cx, thisObj, id, JSGET_NO_METHOD_BARRIER, &regs.sp[-2]))
546 THROW();
548 #if JS_HAS_NO_SUCH_METHOD
549 if (JS_UNLIKELY(regs.sp[-2].isUndefined()) && thisv.isObject()) {
550 regs.sp[-2] = regs.sp[-1];
551 regs.sp[-1].setObject(*thisObj);
552 if (!js_OnUnknownMethod(cx, regs.sp - 2))
553 THROW();
554 } else
555 #endif
557 regs.sp[-1] = thisv;
561 template<JSBool strict>
562 void JS_FASTCALL
563 stubs::SetElem(VMFrame &f)
565 JSContext *cx = f.cx;
566 JSFrameRegs &regs = f.regs;
568 Value &objval = regs.sp[-3];
569 Value &idval = regs.sp[-2];
570 Value retval = regs.sp[-1];
572 JSObject *obj;
573 jsid id;
575 obj = ValueToObject(cx, &objval);
576 if (!obj)
577 THROW();
579 if (!FetchElementId(f, obj, idval, id, &regs.sp[-2]))
580 THROW();
582 do {
583 if (obj->isDenseArray() && JSID_IS_INT(id)) {
584 jsuint length = obj->getDenseArrayCapacity();
585 jsint i = JSID_TO_INT(id);
586 if ((jsuint)i < length) {
587 if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
588 if (js_PrototypeHasIndexedProperties(cx, obj))
589 break;
590 if ((jsuint)i >= obj->getArrayLength())
591 obj->setArrayLength(i + 1);
593 obj->setDenseArrayElement(i, regs.sp[-1]);
594 goto end_setelem;
597 } while (0);
598 if (!obj->setProperty(cx, id, &retval, strict))
599 THROW();
600 end_setelem:
601 /* :FIXME: Moving the assigned object into the lowest stack slot
602 * is a temporary hack. What we actually want is an implementation
603 * of popAfterSet() that allows popping more than one value;
604 * this logic can then be handled in Compiler.cpp. */
605 regs.sp[-3] = retval;
608 template void JS_FASTCALL stubs::SetElem<true>(VMFrame &f);
609 template void JS_FASTCALL stubs::SetElem<false>(VMFrame &f);
611 void JS_FASTCALL
612 stubs::CallName(VMFrame &f)
614 JSObject *obj = NameOp(f, &f.fp()->scopeChain(), true);
615 if (!obj)
616 THROW();
619 void JS_FASTCALL
620 stubs::BitOr(VMFrame &f)
622 int32_t i, j;
624 if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i) ||
625 !ValueToECMAInt32(f.cx, f.regs.sp[-1], &j)) {
626 THROW();
628 i = i | j;
629 f.regs.sp[-2].setInt32(i);
632 void JS_FASTCALL
633 stubs::BitXor(VMFrame &f)
635 int32_t i, j;
637 if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i) ||
638 !ValueToECMAInt32(f.cx, f.regs.sp[-1], &j)) {
639 THROW();
641 i = i ^ j;
642 f.regs.sp[-2].setInt32(i);
645 void JS_FASTCALL
646 stubs::BitAnd(VMFrame &f)
648 int32_t i, j;
650 if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i) ||
651 !ValueToECMAInt32(f.cx, f.regs.sp[-1], &j)) {
652 THROW();
654 i = i & j;
655 f.regs.sp[-2].setInt32(i);
658 void JS_FASTCALL
659 stubs::BitNot(VMFrame &f)
661 int32_t i;
663 if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &i))
664 THROW();
665 i = ~i;
666 f.regs.sp[-1].setInt32(i);
669 void JS_FASTCALL
670 stubs::Lsh(VMFrame &f)
672 int32_t i, j;
673 if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i))
674 THROW();
675 if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &j))
676 THROW();
677 i = i << (j & 31);
678 f.regs.sp[-2].setInt32(i);
681 void JS_FASTCALL
682 stubs::Rsh(VMFrame &f)
684 int32_t i, j;
685 if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i))
686 THROW();
687 if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &j))
688 THROW();
689 i = i >> (j & 31);
690 f.regs.sp[-2].setInt32(i);
693 void JS_FASTCALL
694 stubs::Ursh(VMFrame &f)
696 uint32_t u;
697 if (!ValueToECMAUint32(f.cx, f.regs.sp[-2], &u))
698 THROW();
699 int32_t j;
700 if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &j))
701 THROW();
703 u >>= (j & 31);
705 f.regs.sp[-2].setNumber(uint32(u));
708 template <int32 N>
709 static inline bool
710 PostInc(VMFrame &f, Value *vp)
712 double d;
713 if (!ValueToNumber(f.cx, *vp, &d))
714 return false;
715 f.regs.sp++;
716 f.regs.sp[-1].setDouble(d);
717 d += N;
718 vp->setDouble(d);
719 return true;
722 template <int32 N>
723 static inline bool
724 PreInc(VMFrame &f, Value *vp)
726 double d;
727 if (!ValueToNumber(f.cx, *vp, &d))
728 return false;
729 d += N;
730 vp->setDouble(d);
731 f.regs.sp++;
732 f.regs.sp[-1].setDouble(d);
733 return true;
736 void JS_FASTCALL
737 stubs::VpInc(VMFrame &f, Value *vp)
739 if (!PostInc<1>(f, vp))
740 THROW();
743 void JS_FASTCALL
744 stubs::VpDec(VMFrame &f, Value *vp)
746 if (!PostInc<-1>(f, vp))
747 THROW();
750 void JS_FASTCALL
751 stubs::DecVp(VMFrame &f, Value *vp)
753 if (!PreInc<-1>(f, vp))
754 THROW();
757 void JS_FASTCALL
758 stubs::IncVp(VMFrame &f, Value *vp)
760 if (!PreInc<1>(f, vp))
761 THROW();
764 void JS_FASTCALL
765 stubs::LocalInc(VMFrame &f, uint32 slot)
767 double d;
768 if (!ValueToNumber(f.cx, f.regs.sp[-2], &d))
769 THROW();
770 f.regs.sp[-2].setNumber(d);
771 f.regs.sp[-1].setNumber(d + 1);
772 f.fp()->slots()[slot] = f.regs.sp[-1];
775 void JS_FASTCALL
776 stubs::LocalDec(VMFrame &f, uint32 slot)
778 double d;
779 if (!ValueToNumber(f.cx, f.regs.sp[-2], &d))
780 THROW();
781 f.regs.sp[-2].setNumber(d);
782 f.regs.sp[-1].setNumber(d - 1);
783 f.fp()->slots()[slot] = f.regs.sp[-1];
786 void JS_FASTCALL
787 stubs::IncLocal(VMFrame &f, uint32 slot)
789 double d;
790 if (!ValueToNumber(f.cx, f.regs.sp[-1], &d))
791 THROW();
792 f.regs.sp[-1].setNumber(d + 1);
793 f.fp()->slots()[slot] = f.regs.sp[-1];
796 void JS_FASTCALL
797 stubs::DecLocal(VMFrame &f, uint32 slot)
799 double d;
800 if (!ValueToNumber(f.cx, f.regs.sp[-1], &d))
801 THROW();
802 f.regs.sp[-1].setNumber(d - 1);
803 f.fp()->slots()[slot] = f.regs.sp[-1];
806 template<JSBool strict>
807 void JS_FASTCALL
808 stubs::DefFun(VMFrame &f, JSFunction *fun)
810 JSObject *obj2;
812 JSContext *cx = f.cx;
813 JSStackFrame *fp = f.fp();
816 * A top-level function defined in Global or Eval code (see ECMA-262
817 * Ed. 3), or else a SpiderMonkey extension: a named function statement in
818 * a compound statement (not at the top statement level of global code, or
819 * at the top level of a function body).
821 JSObject *obj = FUN_OBJECT(fun);
823 if (FUN_NULL_CLOSURE(fun)) {
825 * Even a null closure needs a parent for principals finding.
826 * FIXME: bug 476950, although debugger users may also demand some kind
827 * of scope link for debugger-assisted eval-in-frame.
829 obj2 = &fp->scopeChain();
830 } else {
831 JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
833 obj2 = js_GetScopeChainFast(cx, fp, JSOP_DEFFUN, JSOP_DEFFUN_LENGTH);
834 if (!obj2)
835 THROW();
839 * If static link is not current scope, clone fun's object to link to the
840 * current scope via parent. We do this to enable sharing of compiled
841 * functions among multiple equivalent scopes, amortizing the cost of
842 * compilation over a number of executions. Examples include XUL scripts
843 * and event handlers shared among Firefox or other Mozilla app chrome
844 * windows, and user-defined JS functions precompiled and then shared among
845 * requests in server-side JS.
847 if (obj->getParent() != obj2) {
848 obj = CloneFunctionObject(cx, fun, obj2);
849 if (!obj)
850 THROW();
854 * ECMA requires functions defined when entering Eval code to be
855 * impermanent.
857 uintN attrs = fp->isEvalFrame()
858 ? JSPROP_ENUMERATE
859 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
862 * We define the function as a property of the variable object and not the
863 * current scope chain even for the case of function expression statements
864 * and functions defined by eval inside let or with blocks.
866 JSObject *parent = &fp->varobj(cx);
869 * Check for a const property of the same name -- or any kind of property
870 * if executing with the strict option. We check here at runtime as well
871 * as at compile-time, to handle eval as well as multiple HTML script tags.
873 jsid id = ATOM_TO_JSID(fun->atom);
874 JSProperty *prop = NULL;
875 JSObject *pobj;
876 JSBool ok = CheckRedeclaration(cx, parent, id, attrs, &pobj, &prop);
877 if (!ok)
878 THROW();
881 * We deviate from ES3 10.1.3, ES5 10.5, by using JSObject::setProperty not
882 * JSObject::defineProperty for a function declaration in eval code whose
883 * id is already bound to a JSPROP_PERMANENT property, to ensure that such
884 * properties can't be deleted.
886 * We also use JSObject::setProperty for the existing properties of Call
887 * objects with matching attributes to preserve the internal (JSPropertyOp)
888 * getters and setters that update the value of the property in the stack
889 * frame. See bug 467495.
891 bool doSet = false;
892 if (prop) {
893 JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT)));
894 JS_ASSERT((attrs == JSPROP_ENUMERATE) == fp->isEvalFrame());
896 if (attrs == JSPROP_ENUMERATE) {
897 /* In eval code: assign rather than (re-)define, always. */
898 doSet = true;
899 } else if (parent->isCall()) {
900 JS_ASSERT(parent == pobj);
902 uintN oldAttrs = ((Shape *) prop)->attributes();
903 JS_ASSERT(!(oldAttrs & (JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER)));
906 * We may be processing a function sub-statement or declaration in
907 * function code: we assign rather than redefine if the essential
908 * JSPROP_PERMANENT (not [[Configurable]] in ES5 terms) attribute
909 * is not changing (note that JSPROP_ENUMERATE is set for all Call
910 * object properties).
912 JS_ASSERT(oldAttrs & attrs & JSPROP_ENUMERATE);
913 if (oldAttrs & JSPROP_PERMANENT)
914 doSet = true;
918 Value rval = ObjectValue(*obj);
919 ok = doSet
920 ? parent->setProperty(cx, id, &rval, strict)
921 : parent->defineProperty(cx, id, rval, PropertyStub, PropertyStub, attrs);
922 if (!ok)
923 THROW();
926 template void JS_FASTCALL stubs::DefFun<true>(VMFrame &f, JSFunction *fun);
927 template void JS_FASTCALL stubs::DefFun<false>(VMFrame &f, JSFunction *fun);
929 #define DEFAULT_VALUE(cx, n, hint, v) \
930 JS_BEGIN_MACRO \
931 JS_ASSERT(v.isObject()); \
932 JS_ASSERT(v == regs.sp[n]); \
933 if (!DefaultValue(cx, &v.toObject(), hint, &regs.sp[n])) \
934 THROWV(JS_FALSE); \
935 v = regs.sp[n]; \
936 JS_END_MACRO
938 #define RELATIONAL(OP) \
939 JS_BEGIN_MACRO \
940 JSContext *cx = f.cx; \
941 JSFrameRegs &regs = f.regs; \
942 Value rval = regs.sp[-1]; \
943 Value lval = regs.sp[-2]; \
944 bool cond; \
945 if (lval.isObject()) \
946 DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \
947 if (rval.isObject()) \
948 DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \
949 if (lval.isString() && rval.isString()) { \
950 JSString *l = lval.toString(), *r = rval.toString(); \
951 cond = js_CompareStrings(l, r) OP 0; \
952 } else { \
953 double l, r; \
954 if (!ValueToNumber(cx, lval, &l) || \
955 !ValueToNumber(cx, rval, &r)) { \
956 THROWV(JS_FALSE); \
958 cond = JSDOUBLE_COMPARE(l, OP, r, false); \
960 regs.sp[-2].setBoolean(cond); \
961 return cond; \
962 JS_END_MACRO
964 JSBool JS_FASTCALL
965 stubs::LessThan(VMFrame &f)
967 RELATIONAL(<);
970 JSBool JS_FASTCALL
971 stubs::LessEqual(VMFrame &f)
973 RELATIONAL(<=);
976 JSBool JS_FASTCALL
977 stubs::GreaterThan(VMFrame &f)
979 RELATIONAL(>);
982 JSBool JS_FASTCALL
983 stubs::GreaterEqual(VMFrame &f)
985 RELATIONAL(>=);
988 JSBool JS_FASTCALL
989 stubs::ValueToBoolean(VMFrame &f)
991 return js_ValueToBoolean(f.regs.sp[-1]);
994 void JS_FASTCALL
995 stubs::Not(VMFrame &f)
997 JSBool b = !js_ValueToBoolean(f.regs.sp[-1]);
998 f.regs.sp[-1].setBoolean(b);
1001 template <JSBool EQ, bool IFNAN>
1002 static inline bool
1003 StubEqualityOp(VMFrame &f)
1005 JSContext *cx = f.cx;
1006 JSFrameRegs &regs = f.regs;
1008 Value rval = regs.sp[-1];
1009 Value lval = regs.sp[-2];
1011 JSBool cond;
1013 /* The string==string case is easily the hottest; try it first. */
1014 if (lval.isString() && rval.isString()) {
1015 JSString *l = lval.toString();
1016 JSString *r = rval.toString();
1017 cond = js_EqualStrings(l, r) == EQ;
1018 } else
1019 #if JS_HAS_XML_SUPPORT
1020 if ((lval.isObject() && lval.toObject().isXML()) ||
1021 (rval.isObject() && rval.toObject().isXML())) {
1022 if (!js_TestXMLEquality(cx, lval, rval, &cond))
1023 return false;
1024 cond = cond == EQ;
1025 } else
1026 #endif
1028 if (SameType(lval, rval)) {
1029 JS_ASSERT(!lval.isString()); /* this case is handled above */
1030 if (lval.isDouble()) {
1031 double l = lval.toDouble();
1032 double r = rval.toDouble();
1033 if (EQ)
1034 cond = JSDOUBLE_COMPARE(l, ==, r, IFNAN);
1035 else
1036 cond = JSDOUBLE_COMPARE(l, !=, r, IFNAN);
1037 } else if (lval.isObject()) {
1038 JSObject *l = &lval.toObject(), *r = &rval.toObject();
1039 if (EqualityOp eq = l->getClass()->ext.equality) {
1040 if (!eq(cx, l, &rval, &cond))
1041 return false;
1042 cond = cond == EQ;
1043 } else {
1044 cond = (l == r) == EQ;
1046 } else if (lval.isNullOrUndefined()) {
1047 cond = EQ;
1048 } else {
1049 cond = (lval.payloadAsRawUint32() == rval.payloadAsRawUint32()) == EQ;
1051 } else {
1052 if (lval.isNullOrUndefined()) {
1053 cond = rval.isNullOrUndefined() == EQ;
1054 } else if (rval.isNullOrUndefined()) {
1055 cond = !EQ;
1056 } else {
1057 if (lval.isObject()) {
1058 if (!DefaultValue(cx, &lval.toObject(), JSTYPE_VOID, &regs.sp[-2]))
1059 return false;
1060 lval = regs.sp[-2];
1063 if (rval.isObject()) {
1064 if (!DefaultValue(cx, &rval.toObject(), JSTYPE_VOID, &regs.sp[-1]))
1065 return false;
1066 rval = regs.sp[-1];
1070 * The string==string case is repeated because DefaultValue() can
1071 * convert lval/rval to strings.
1073 if (lval.isString() && rval.isString()) {
1074 JSString *l = lval.toString();
1075 JSString *r = rval.toString();
1076 cond = js_EqualStrings(l, r) == EQ;
1077 } else {
1078 double l, r;
1079 if (!ValueToNumber(cx, lval, &l) ||
1080 !ValueToNumber(cx, rval, &r)) {
1081 return false;
1084 if (EQ)
1085 cond = JSDOUBLE_COMPARE(l, ==, r, false);
1086 else
1087 cond = JSDOUBLE_COMPARE(l, !=, r, true);
1092 regs.sp[-2].setBoolean(cond);
1093 return true;
1096 JSBool JS_FASTCALL
1097 stubs::Equal(VMFrame &f)
1099 if (!StubEqualityOp<JS_TRUE, false>(f))
1100 THROWV(JS_FALSE);
1101 return f.regs.sp[-2].toBoolean();
1104 JSBool JS_FASTCALL
1105 stubs::NotEqual(VMFrame &f)
1107 if (!StubEqualityOp<JS_FALSE, true>(f))
1108 THROWV(JS_FALSE);
1109 return f.regs.sp[-2].toBoolean();
1112 static inline bool
1113 DefaultValue(VMFrame &f, JSType hint, Value &v, int n)
1115 JS_ASSERT(v.isObject());
1116 if (!DefaultValue(f.cx, &v.toObject(), hint, &f.regs.sp[n]))
1117 return false;
1118 v = f.regs.sp[n];
1119 return true;
1122 void JS_FASTCALL
1123 stubs::Add(VMFrame &f)
1125 JSContext *cx = f.cx;
1126 JSFrameRegs &regs = f.regs;
1127 Value rval = regs.sp[-1];
1128 Value lval = regs.sp[-2];
1130 /* The string + string case is easily the hottest; try it first. */
1131 bool lIsString = lval.isString();
1132 bool rIsString = rval.isString();
1133 JSString *lstr, *rstr;
1134 if (lIsString && rIsString) {
1135 lstr = lval.toString();
1136 rstr = rval.toString();
1137 goto string_concat;
1139 } else
1140 #if JS_HAS_XML_SUPPORT
1141 if (lval.isObject() && lval.toObject().isXML() &&
1142 rval.isObject() && rval.toObject().isXML()) {
1143 if (!js_ConcatenateXML(cx, &lval.toObject(), &rval.toObject(), &rval))
1144 THROW();
1145 regs.sp--;
1146 regs.sp[-1] = rval;
1147 } else
1148 #endif
1150 /* These can convert lval/rval to strings. */
1151 if (lval.isObject() && !DefaultValue(f, JSTYPE_VOID, lval, -2))
1152 THROW();
1153 if (rval.isObject() && !DefaultValue(f, JSTYPE_VOID, rval, -1))
1154 THROW();
1155 if ((lIsString = lval.isString()) || (rIsString = rval.isString())) {
1156 if (lIsString) {
1157 lstr = lval.toString();
1158 } else {
1159 lstr = js_ValueToString(cx, lval);
1160 if (!lstr)
1161 THROW();
1162 regs.sp[-2].setString(lstr);
1164 if (rIsString) {
1165 rstr = rval.toString();
1166 } else {
1167 rstr = js_ValueToString(cx, rval);
1168 if (!rstr)
1169 THROW();
1170 regs.sp[-1].setString(rstr);
1172 goto string_concat;
1174 } else {
1175 double l, r;
1176 if (!ValueToNumber(cx, lval, &l) || !ValueToNumber(cx, rval, &r))
1177 THROW();
1178 l += r;
1179 regs.sp--;
1180 regs.sp[-1].setNumber(l);
1183 return;
1185 string_concat:
1186 JSString *str = js_ConcatStrings(cx, lstr, rstr);
1187 if (!str)
1188 THROW();
1189 regs.sp--;
1190 regs.sp[-1].setString(str);
1194 void JS_FASTCALL
1195 stubs::Sub(VMFrame &f)
1197 JSContext *cx = f.cx;
1198 JSFrameRegs &regs = f.regs;
1199 double d1, d2;
1200 if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
1201 !ValueToNumber(cx, regs.sp[-1], &d2)) {
1202 THROW();
1204 double d = d1 - d2;
1205 regs.sp[-2].setNumber(d);
1208 void JS_FASTCALL
1209 stubs::Mul(VMFrame &f)
1211 JSContext *cx = f.cx;
1212 JSFrameRegs &regs = f.regs;
1213 double d1, d2;
1214 if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
1215 !ValueToNumber(cx, regs.sp[-1], &d2)) {
1216 THROW();
1218 double d = d1 * d2;
1219 regs.sp[-2].setNumber(d);
1222 void JS_FASTCALL
1223 stubs::Div(VMFrame &f)
1225 JSContext *cx = f.cx;
1226 JSRuntime *rt = cx->runtime;
1227 JSFrameRegs &regs = f.regs;
1229 double d1, d2;
1230 if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
1231 !ValueToNumber(cx, regs.sp[-1], &d2)) {
1232 THROW();
1234 if (d2 == 0) {
1235 const Value *vp;
1236 #ifdef XP_WIN
1237 /* XXX MSVC miscompiles such that (NaN == 0) */
1238 if (JSDOUBLE_IS_NaN(d2))
1239 vp = &rt->NaNValue;
1240 else
1241 #endif
1242 if (d1 == 0 || JSDOUBLE_IS_NaN(d1))
1243 vp = &rt->NaNValue;
1244 else if (JSDOUBLE_IS_NEG(d1) != JSDOUBLE_IS_NEG(d2))
1245 vp = &rt->negativeInfinityValue;
1246 else
1247 vp = &rt->positiveInfinityValue;
1248 regs.sp[-2] = *vp;
1249 } else {
1250 d1 /= d2;
1251 regs.sp[-2].setNumber(d1);
1255 void JS_FASTCALL
1256 stubs::Mod(VMFrame &f)
1258 JSContext *cx = f.cx;
1259 JSFrameRegs &regs = f.regs;
1261 Value &lref = regs.sp[-2];
1262 Value &rref = regs.sp[-1];
1263 int32_t l, r;
1264 if (lref.isInt32() && rref.isInt32() &&
1265 (l = lref.toInt32()) >= 0 && (r = rref.toInt32()) > 0) {
1266 int32_t mod = l % r;
1267 regs.sp[-2].setInt32(mod);
1268 } else {
1269 double d1, d2;
1270 if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
1271 !ValueToNumber(cx, regs.sp[-1], &d2)) {
1272 THROW();
1274 if (d2 == 0) {
1275 regs.sp[-2].setDouble(js_NaN);
1276 } else {
1277 d1 = js_fmod(d1, d2);
1278 regs.sp[-2].setDouble(d1);
1283 JSObject *JS_FASTCALL
1284 stubs::NewArray(VMFrame &f, uint32 len)
1286 JSObject *obj = js_NewArrayObject(f.cx, len, f.regs.sp - len);
1287 if (!obj)
1288 THROWV(NULL);
1289 return obj;
1292 void JS_FASTCALL
1293 stubs::Debugger(VMFrame &f, jsbytecode *pc)
1295 JSDebuggerHandler handler = f.cx->debugHooks->debuggerHandler;
1296 if (handler) {
1297 Value rval;
1298 switch (handler(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval),
1299 f.cx->debugHooks->debuggerHandlerData)) {
1300 case JSTRAP_THROW:
1301 f.cx->throwing = JS_TRUE;
1302 f.cx->exception = rval;
1303 THROW();
1305 case JSTRAP_RETURN:
1306 f.cx->throwing = JS_FALSE;
1307 f.cx->fp()->setReturnValue(rval);
1308 #if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64)
1309 *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *,
1310 JS_METHODJIT_DATA(f.cx).trampolines.forceReturnFast);
1311 #else
1312 *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *,
1313 JS_METHODJIT_DATA(f.cx).trampolines.forceReturn);
1314 #endif
1315 break;
1317 case JSTRAP_ERROR:
1318 f.cx->throwing = JS_FALSE;
1319 THROW();
1321 default:
1322 break;
1327 void JS_FASTCALL
1328 stubs::Interrupt(VMFrame &f, jsbytecode *pc)
1330 if (!js_HandleExecutionInterrupt(f.cx))
1331 THROW();
1334 void JS_FASTCALL
1335 stubs::Trap(VMFrame &f, jsbytecode *pc)
1337 Value rval;
1339 switch (JS_HandleTrap(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval))) {
1340 case JSTRAP_THROW:
1341 f.cx->throwing = JS_TRUE;
1342 f.cx->exception = rval;
1343 THROW();
1345 case JSTRAP_RETURN:
1346 f.cx->throwing = JS_FALSE;
1347 f.cx->fp()->setReturnValue(rval);
1348 #if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64)
1349 *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *,
1350 JS_METHODJIT_DATA(f.cx).trampolines.forceReturnFast);
1351 #else
1352 *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *,
1353 JS_METHODJIT_DATA(f.cx).trampolines.forceReturn);
1354 #endif
1355 break;
1357 case JSTRAP_ERROR:
1358 f.cx->throwing = JS_FALSE;
1359 THROW();
1361 default:
1362 break;
1366 void JS_FASTCALL
1367 stubs::This(VMFrame &f)
1369 if (!f.fp()->computeThis(f.cx))
1370 THROW();
1371 f.regs.sp[-1] = f.fp()->thisValue();
1374 void JS_FASTCALL
1375 stubs::Neg(VMFrame &f)
1377 double d;
1378 if (!ValueToNumber(f.cx, f.regs.sp[-1], &d))
1379 THROW();
1380 d = -d;
1381 f.regs.sp[-1].setNumber(d);
1384 JSObject * JS_FASTCALL
1385 stubs::NewInitArray(VMFrame &f, uint32 count)
1387 JSContext *cx = f.cx;
1388 gc::FinalizeKind kind = GuessObjectGCKind(count, true);
1390 JSObject *obj = NewArrayWithKind(cx, kind);
1391 if (!obj || !obj->ensureSlots(cx, count))
1392 THROWV(NULL);
1393 return obj;
1396 JSObject * JS_FASTCALL
1397 stubs::NewInitObject(VMFrame &f, uint32 count)
1399 JSContext *cx = f.cx;
1400 gc::FinalizeKind kind = GuessObjectGCKind(count, false);
1402 JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
1403 if (!obj || !obj->ensureSlots(cx, count))
1404 THROWV(NULL);
1406 return obj;
1409 void JS_FASTCALL
1410 stubs::InitElem(VMFrame &f, uint32 last)
1412 JSContext *cx = f.cx;
1413 JSFrameRegs &regs = f.regs;
1415 /* Pop the element's value into rval. */
1416 JS_ASSERT(regs.sp - f.fp()->base() >= 3);
1417 const Value &rref = regs.sp[-1];
1419 /* Find the object being initialized at top of stack. */
1420 const Value &lref = regs.sp[-3];
1421 JS_ASSERT(lref.isObject());
1422 JSObject *obj = &lref.toObject();
1424 /* Fetch id now that we have obj. */
1425 jsid id;
1426 const Value &idval = regs.sp[-2];
1427 if (!FetchElementId(f, obj, idval, id, &regs.sp[-2]))
1428 THROW();
1431 * Check for property redeclaration strict warning (we may be in an object
1432 * initialiser, not an array initialiser).
1434 if (!CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER, NULL, NULL))
1435 THROW();
1438 * If rref is a hole, do not call JSObject::defineProperty. In this case,
1439 * obj must be an array, so if the current op is the last element
1440 * initialiser, set the array length to one greater than id.
1442 if (rref.isMagic(JS_ARRAY_HOLE)) {
1443 JS_ASSERT(obj->isArray());
1444 JS_ASSERT(JSID_IS_INT(id));
1445 JS_ASSERT(jsuint(JSID_TO_INT(id)) < JS_ARGS_LENGTH_MAX);
1446 if (last && !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1)))
1447 THROW();
1448 } else {
1449 if (!obj->defineProperty(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE))
1450 THROW();
1454 void JS_FASTCALL
1455 stubs::GetUpvar(VMFrame &f, uint32 ck)
1457 /* :FIXME: We can do better, this stub isn't needed. */
1458 uint32 staticLevel = f.fp()->script()->staticLevel;
1459 UpvarCookie cookie;
1460 cookie.fromInteger(ck);
1461 f.regs.sp[0] = GetUpvar(f.cx, staticLevel, cookie);
1464 JSObject * JS_FASTCALL
1465 stubs::DefLocalFun(VMFrame &f, JSFunction *fun)
1468 * Define a local function (i.e., one nested at the top level of another
1469 * function), parented by the current scope chain, stored in a local
1470 * variable slot that the compiler allocated. This is an optimization over
1471 * JSOP_DEFFUN that avoids requiring a call object for the outer function's
1472 * activation.
1474 JS_ASSERT(fun->isInterpreted());
1475 JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
1476 JSObject *obj = FUN_OBJECT(fun);
1478 if (FUN_NULL_CLOSURE(fun)) {
1479 obj = CloneFunctionObject(f.cx, fun, &f.fp()->scopeChain());
1480 if (!obj)
1481 THROWV(NULL);
1482 } else {
1483 JSObject *parent = js_GetScopeChainFast(f.cx, f.fp(), JSOP_DEFLOCALFUN,
1484 JSOP_DEFLOCALFUN_LENGTH);
1485 if (!parent)
1486 THROWV(NULL);
1488 if (obj->getParent() != parent) {
1489 obj = CloneFunctionObject(f.cx, fun, parent);
1490 if (!obj)
1491 THROWV(NULL);
1495 return obj;
1498 JSObject * JS_FASTCALL
1499 stubs::DefLocalFun_FC(VMFrame &f, JSFunction *fun)
1501 JSObject *obj = js_NewFlatClosure(f.cx, fun, JSOP_DEFLOCALFUN_FC, JSOP_DEFLOCALFUN_FC_LENGTH);
1502 if (!obj)
1503 THROWV(NULL);
1504 return obj;
1507 JSObject * JS_FASTCALL
1508 stubs::RegExp(VMFrame &f, JSObject *regex)
1511 * Push a regexp object cloned from the regexp literal object mapped by the
1512 * bytecode at pc. ES5 finally fixed this bad old ES3 design flaw which was
1513 * flouted by many browser-based implementations.
1515 * We avoid the js_GetScopeChain call here and pass fp->scopeChain() as
1516 * js_GetClassPrototype uses the latter only to locate the global.
1518 JSObject *proto;
1519 if (!js_GetClassPrototype(f.cx, &f.fp()->scopeChain(), JSProto_RegExp, &proto))
1520 THROWV(NULL);
1521 JS_ASSERT(proto);
1522 JSObject *obj = js_CloneRegExpObject(f.cx, regex, proto);
1523 if (!obj)
1524 THROWV(NULL);
1525 return obj;
1528 JSObject * JS_FASTCALL
1529 stubs::LambdaForInit(VMFrame &f, JSFunction *fun)
1531 JSObject *obj = FUN_OBJECT(fun);
1532 if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) {
1533 fun->setMethodAtom(f.fp()->script()->getAtom(GET_SLOTNO(f.regs.pc)));
1534 return obj;
1536 return Lambda(f, fun);
1539 JSObject * JS_FASTCALL
1540 stubs::LambdaForSet(VMFrame &f, JSFunction *fun)
1542 JSObject *obj = FUN_OBJECT(fun);
1543 if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) {
1544 const Value &lref = f.regs.sp[-1];
1545 if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) {
1546 fun->setMethodAtom(f.fp()->script()->getAtom(GET_SLOTNO(f.regs.pc)));
1547 return obj;
1550 return Lambda(f, fun);
1553 JSObject * JS_FASTCALL
1554 stubs::LambdaJoinableForCall(VMFrame &f, JSFunction *fun)
1556 JSObject *obj = FUN_OBJECT(fun);
1557 if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) {
1559 * Array.prototype.sort and String.prototype.replace are
1560 * optimized as if they are special form. We know that they
1561 * won't leak the joined function object in obj, therefore
1562 * we don't need to clone that compiler- created function
1563 * object for identity/mutation reasons.
1565 int iargc = GET_ARGC(f.regs.pc);
1568 * Note that we have not yet pushed obj as the final argument,
1569 * so regs.sp[1 - (iargc + 2)], and not regs.sp[-(iargc + 2)],
1570 * is the callee for this JSOP_CALL.
1572 const Value &cref = f.regs.sp[1 - (iargc + 2)];
1573 JSObject *callee;
1575 if (IsFunctionObject(cref, &callee)) {
1576 JSFunction *calleeFun = callee->getFunctionPrivate();
1577 Native native = calleeFun->maybeNative();
1579 if (native) {
1580 if (iargc == 1 && native == array_sort)
1581 return obj;
1582 if (iargc == 2 && native == str_replace)
1583 return obj;
1587 return Lambda(f, fun);
1590 JSObject * JS_FASTCALL
1591 stubs::LambdaJoinableForNull(VMFrame &f, JSFunction *fun)
1593 JSObject *obj = FUN_OBJECT(fun);
1594 if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) {
1595 jsbytecode *pc2 = f.regs.pc + JSOP_NULL_LENGTH;
1596 JSOp op2 = JSOp(*pc2);
1598 if (op2 == JSOP_CALL && GET_ARGC(pc2) == 0)
1599 return obj;
1601 return Lambda(f, fun);
1604 JSObject * JS_FASTCALL
1605 stubs::Lambda(VMFrame &f, JSFunction *fun)
1607 JSObject *obj = FUN_OBJECT(fun);
1609 JSObject *parent;
1610 if (FUN_NULL_CLOSURE(fun)) {
1611 parent = &f.fp()->scopeChain();
1612 } else {
1613 parent = js_GetScopeChainFast(f.cx, f.fp(), JSOP_LAMBDA, JSOP_LAMBDA_LENGTH);
1614 if (!parent)
1615 THROWV(NULL);
1618 obj = CloneFunctionObject(f.cx, fun, parent);
1619 if (!obj)
1620 THROWV(NULL);
1622 return obj;
1625 /* Test whether v is an int in the range [-2^31 + 1, 2^31 - 2] */
1626 static JS_ALWAYS_INLINE bool
1627 CanIncDecWithoutOverflow(int32_t i)
1629 return (i > JSVAL_INT_MIN) && (i < JSVAL_INT_MAX);
1632 template <int32 N, bool POST, JSBool strict>
1633 static inline bool
1634 ObjIncOp(VMFrame &f, JSObject *obj, jsid id)
1636 JSContext *cx = f.cx;
1637 JSStackFrame *fp = f.fp();
1639 f.regs.sp[0].setNull();
1640 f.regs.sp++;
1641 if (!obj->getProperty(cx, id, &f.regs.sp[-1]))
1642 return false;
1644 Value &ref = f.regs.sp[-1];
1645 int32_t tmp;
1646 if (JS_LIKELY(ref.isInt32() && CanIncDecWithoutOverflow(tmp = ref.toInt32()))) {
1647 if (POST)
1648 ref.getInt32Ref() = tmp + N;
1649 else
1650 ref.getInt32Ref() = tmp += N;
1651 fp->setAssigning();
1652 JSBool ok = obj->setProperty(cx, id, &ref, strict);
1653 fp->clearAssigning();
1654 if (!ok)
1655 return false;
1658 * We must set regs.sp[-1] to tmp for both post and pre increments
1659 * as the setter overwrites regs.sp[-1].
1661 ref.setInt32(tmp);
1662 } else {
1663 Value v;
1664 double d;
1665 if (!ValueToNumber(cx, ref, &d))
1666 return false;
1667 if (POST) {
1668 ref.setDouble(d);
1669 d += N;
1670 } else {
1671 d += N;
1672 ref.setDouble(d);
1674 v.setDouble(d);
1675 fp->setAssigning();
1676 JSBool ok = obj->setProperty(cx, id, &v, strict);
1677 fp->clearAssigning();
1678 if (!ok)
1679 return false;
1682 return true;
1685 template <int32 N, bool POST, JSBool strict>
1686 static inline bool
1687 NameIncDec(VMFrame &f, JSObject *obj, JSAtom *origAtom)
1689 JSContext *cx = f.cx;
1691 JSAtom *atom;
1692 JSObject *obj2;
1693 JSProperty *prop;
1694 PropertyCacheEntry *entry;
1695 JS_PROPERTY_CACHE(cx).test(cx, f.regs.pc, obj, obj2, entry, atom);
1696 if (!atom) {
1697 if (obj == obj2 && entry->vword.isSlot()) {
1698 uint32 slot = entry->vword.toSlot();
1699 Value &rref = obj->nativeGetSlotRef(slot);
1700 int32_t tmp;
1701 if (JS_LIKELY(rref.isInt32() && CanIncDecWithoutOverflow(tmp = rref.toInt32()))) {
1702 int32_t inc = tmp + N;
1703 if (!POST)
1704 tmp = inc;
1705 rref.getInt32Ref() = inc;
1706 f.regs.sp[0].setInt32(tmp);
1707 return true;
1710 atom = origAtom;
1713 jsid id = ATOM_TO_JSID(atom);
1714 if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
1715 return false;
1716 if (!prop) {
1717 ReportAtomNotDefined(cx, atom);
1718 return false;
1720 return ObjIncOp<N, POST, strict>(f, obj, id);
1723 template<JSBool strict>
1724 void JS_FASTCALL
1725 stubs::PropInc(VMFrame &f, JSAtom *atom)
1727 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
1728 if (!obj)
1729 THROW();
1730 if (!ObjIncOp<1, true, strict>(f, obj, ATOM_TO_JSID(atom)))
1731 THROW();
1732 f.regs.sp[-2] = f.regs.sp[-1];
1735 template void JS_FASTCALL stubs::PropInc<true>(VMFrame &f, JSAtom *atom);
1736 template void JS_FASTCALL stubs::PropInc<false>(VMFrame &f, JSAtom *atom);
1738 template<JSBool strict>
1739 void JS_FASTCALL
1740 stubs::PropDec(VMFrame &f, JSAtom *atom)
1742 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
1743 if (!obj)
1744 THROW();
1745 if (!ObjIncOp<-1, true, strict>(f, obj, ATOM_TO_JSID(atom)))
1746 THROW();
1747 f.regs.sp[-2] = f.regs.sp[-1];
1750 template void JS_FASTCALL stubs::PropDec<true>(VMFrame &f, JSAtom *atom);
1751 template void JS_FASTCALL stubs::PropDec<false>(VMFrame &f, JSAtom *atom);
1753 template<JSBool strict>
1754 void JS_FASTCALL
1755 stubs::IncProp(VMFrame &f, JSAtom *atom)
1757 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
1758 if (!obj)
1759 THROW();
1760 if (!ObjIncOp<1, false, strict>(f, obj, ATOM_TO_JSID(atom)))
1761 THROW();
1762 f.regs.sp[-2] = f.regs.sp[-1];
1765 template void JS_FASTCALL stubs::IncProp<true>(VMFrame &f, JSAtom *atom);
1766 template void JS_FASTCALL stubs::IncProp<false>(VMFrame &f, JSAtom *atom);
1768 template<JSBool strict>
1769 void JS_FASTCALL
1770 stubs::DecProp(VMFrame &f, JSAtom *atom)
1772 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
1773 if (!obj)
1774 THROW();
1775 if (!ObjIncOp<-1, false, strict>(f, obj, ATOM_TO_JSID(atom)))
1776 THROW();
1777 f.regs.sp[-2] = f.regs.sp[-1];
1780 template void JS_FASTCALL stubs::DecProp<true>(VMFrame &f, JSAtom *atom);
1781 template void JS_FASTCALL stubs::DecProp<false>(VMFrame &f, JSAtom *atom);
1783 template<JSBool strict>
1784 void JS_FASTCALL
1785 stubs::ElemInc(VMFrame &f)
1787 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
1788 if (!obj)
1789 THROW();
1790 jsid id;
1791 if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
1792 THROW();
1793 if (!ObjIncOp<1, true, strict>(f, obj, id))
1794 THROW();
1795 f.regs.sp[-3] = f.regs.sp[-1];
1798 template void JS_FASTCALL stubs::ElemInc<true>(VMFrame &f);
1799 template void JS_FASTCALL stubs::ElemInc<false>(VMFrame &f);
1801 template<JSBool strict>
1802 void JS_FASTCALL
1803 stubs::ElemDec(VMFrame &f)
1805 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
1806 if (!obj)
1807 THROW();
1808 jsid id;
1809 if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
1810 THROW();
1811 if (!ObjIncOp<-1, true, strict>(f, obj, id))
1812 THROW();
1813 f.regs.sp[-3] = f.regs.sp[-1];
1816 template void JS_FASTCALL stubs::ElemDec<true>(VMFrame &f);
1817 template void JS_FASTCALL stubs::ElemDec<false>(VMFrame &f);
1819 template<JSBool strict>
1820 void JS_FASTCALL
1821 stubs::IncElem(VMFrame &f)
1823 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
1824 if (!obj)
1825 THROW();
1826 jsid id;
1827 if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
1828 THROW();
1829 if (!ObjIncOp<1, false, strict>(f, obj, id))
1830 THROW();
1831 f.regs.sp[-3] = f.regs.sp[-1];
1834 template void JS_FASTCALL stubs::IncElem<true>(VMFrame &f);
1835 template void JS_FASTCALL stubs::IncElem<false>(VMFrame &f);
1837 template<JSBool strict>
1838 void JS_FASTCALL
1839 stubs::DecElem(VMFrame &f)
1841 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
1842 if (!obj)
1843 THROW();
1844 jsid id;
1845 if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
1846 THROW();
1847 if (!ObjIncOp<-1, false, strict>(f, obj, id))
1848 THROW();
1849 f.regs.sp[-3] = f.regs.sp[-1];
1852 template void JS_FASTCALL stubs::DecElem<true>(VMFrame &f);
1853 template void JS_FASTCALL stubs::DecElem<false>(VMFrame &f);
1855 template<JSBool strict>
1856 void JS_FASTCALL
1857 stubs::NameInc(VMFrame &f, JSAtom *atom)
1859 JSObject *obj = &f.fp()->scopeChain();
1860 if (!NameIncDec<1, true, strict>(f, obj, atom))
1861 THROW();
1864 template void JS_FASTCALL stubs::NameInc<true>(VMFrame &f, JSAtom *atom);
1865 template void JS_FASTCALL stubs::NameInc<false>(VMFrame &f, JSAtom *atom);
1867 template<JSBool strict>
1868 void JS_FASTCALL
1869 stubs::NameDec(VMFrame &f, JSAtom *atom)
1871 JSObject *obj = &f.fp()->scopeChain();
1872 if (!NameIncDec<-1, true, strict>(f, obj, atom))
1873 THROW();
1876 template void JS_FASTCALL stubs::NameDec<true>(VMFrame &f, JSAtom *atom);
1877 template void JS_FASTCALL stubs::NameDec<false>(VMFrame &f, JSAtom *atom);
1879 template<JSBool strict>
1880 void JS_FASTCALL
1881 stubs::IncName(VMFrame &f, JSAtom *atom)
1883 JSObject *obj = &f.fp()->scopeChain();
1884 if (!NameIncDec<1, false, strict>(f, obj, atom))
1885 THROW();
1888 template void JS_FASTCALL stubs::IncName<true>(VMFrame &f, JSAtom *atom);
1889 template void JS_FASTCALL stubs::IncName<false>(VMFrame &f, JSAtom *atom);
1891 template<JSBool strict>
1892 void JS_FASTCALL
1893 stubs::DecName(VMFrame &f, JSAtom *atom)
1895 JSObject *obj = &f.fp()->scopeChain();
1896 if (!NameIncDec<-1, false, strict>(f, obj, atom))
1897 THROW();
1900 template void JS_FASTCALL stubs::DecName<true>(VMFrame &f, JSAtom *atom);
1901 template void JS_FASTCALL stubs::DecName<false>(VMFrame &f, JSAtom *atom);
1903 template<JSBool strict>
1904 void JS_FASTCALL
1905 stubs::GlobalNameInc(VMFrame &f, JSAtom *atom)
1907 JSObject *obj = f.fp()->scopeChain().getGlobal();
1908 if (!NameIncDec<1, true, strict>(f, obj, atom))
1909 THROW();
1912 template void JS_FASTCALL stubs::GlobalNameInc<true>(VMFrame &f, JSAtom *atom);
1913 template void JS_FASTCALL stubs::GlobalNameInc<false>(VMFrame &f, JSAtom *atom);
1915 template<JSBool strict>
1916 void JS_FASTCALL
1917 stubs::GlobalNameDec(VMFrame &f, JSAtom *atom)
1919 JSObject *obj = f.fp()->scopeChain().getGlobal();
1920 if (!NameIncDec<-1, true, strict>(f, obj, atom))
1921 THROW();
1924 template void JS_FASTCALL stubs::GlobalNameDec<true>(VMFrame &f, JSAtom *atom);
1925 template void JS_FASTCALL stubs::GlobalNameDec<false>(VMFrame &f, JSAtom *atom);
1927 template<JSBool strict>
1928 void JS_FASTCALL
1929 stubs::IncGlobalName(VMFrame &f, JSAtom *atom)
1931 JSObject *obj = f.fp()->scopeChain().getGlobal();
1932 if (!NameIncDec<1, false, strict>(f, obj, atom))
1933 THROW();
1936 template void JS_FASTCALL stubs::IncGlobalName<true>(VMFrame &f, JSAtom *atom);
1937 template void JS_FASTCALL stubs::IncGlobalName<false>(VMFrame &f, JSAtom *atom);
1939 template<JSBool strict>
1940 void JS_FASTCALL
1941 stubs::DecGlobalName(VMFrame &f, JSAtom *atom)
1943 JSObject *obj = f.fp()->scopeChain().getGlobal();
1944 if (!NameIncDec<-1, false, strict>(f, obj, atom))
1945 THROW();
1948 template void JS_FASTCALL stubs::DecGlobalName<true>(VMFrame &f, JSAtom *atom);
1949 template void JS_FASTCALL stubs::DecGlobalName<false>(VMFrame &f, JSAtom *atom);
1951 static bool JS_FASTCALL
1952 InlineGetProp(VMFrame &f)
1954 JSContext *cx = f.cx;
1955 JSFrameRegs &regs = f.regs;
1957 Value *vp = &f.regs.sp[-1];
1958 JSObject *obj = ValueToObject(f.cx, vp);
1959 if (!obj)
1960 return false;
1962 Value rval;
1963 do {
1965 * We do not impose the method read barrier if in an imacro,
1966 * assuming any property gets it does (e.g., for 'toString'
1967 * from JSOP_NEW) will not be leaked to the calling script.
1969 JSObject *aobj = js_GetProtoIfDenseArray(obj);
1971 PropertyCacheEntry *entry;
1972 JSObject *obj2;
1973 JSAtom *atom;
1974 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
1975 if (!atom) {
1976 if (entry->vword.isFunObj()) {
1977 rval.setObject(entry->vword.toFunObj());
1978 } else if (entry->vword.isSlot()) {
1979 uint32 slot = entry->vword.toSlot();
1980 rval = obj2->nativeGetSlot(slot);
1981 } else {
1982 JS_ASSERT(entry->vword.isShape());
1983 const Shape *shape = entry->vword.toShape();
1984 NATIVE_GET(cx, obj, obj2, shape,
1985 f.fp()->hasImacropc() ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER,
1986 &rval, return false);
1988 break;
1991 jsid id = ATOM_TO_JSID(atom);
1992 if (JS_LIKELY(!aobj->getOps()->getProperty)
1993 ? !js_GetPropertyHelper(cx, obj, id,
1994 f.fp()->hasImacropc()
1995 ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
1996 : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER,
1997 &rval)
1998 : !obj->getProperty(cx, id, &rval)) {
1999 return false;
2001 } while(0);
2003 regs.sp[-1] = rval;
2004 return true;
2007 void JS_FASTCALL
2008 stubs::GetProp(VMFrame &f)
2010 if (!InlineGetProp(f))
2011 THROW();
2014 void JS_FASTCALL
2015 stubs::GetPropNoCache(VMFrame &f, JSAtom *atom)
2017 JSContext *cx = f.cx;
2019 Value *vp = &f.regs.sp[-1];
2020 JSObject *obj = ValueToObject(cx, vp);
2021 if (!obj)
2022 THROW();
2024 if (!obj->getProperty(cx, ATOM_TO_JSID(atom), vp))
2025 THROW();
2028 void JS_FASTCALL
2029 stubs::CallProp(VMFrame &f, JSAtom *origAtom)
2031 JSContext *cx = f.cx;
2032 JSFrameRegs &regs = f.regs;
2034 Value lval;
2035 lval = regs.sp[-1];
2037 Value objv;
2038 if (lval.isObject()) {
2039 objv = lval;
2040 } else {
2041 JSProtoKey protoKey;
2042 if (lval.isString()) {
2043 protoKey = JSProto_String;
2044 } else if (lval.isNumber()) {
2045 protoKey = JSProto_Number;
2046 } else if (lval.isBoolean()) {
2047 protoKey = JSProto_Boolean;
2048 } else {
2049 JS_ASSERT(lval.isNull() || lval.isUndefined());
2050 js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
2051 THROW();
2053 JSObject *pobj;
2054 if (!js_GetClassPrototype(cx, NULL, protoKey, &pobj))
2055 THROW();
2056 objv.setObject(*pobj);
2059 JSObject *aobj = js_GetProtoIfDenseArray(&objv.toObject());
2060 Value rval;
2062 PropertyCacheEntry *entry;
2063 JSObject *obj2;
2064 JSAtom *atom;
2065 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
2066 if (!atom) {
2067 if (entry->vword.isFunObj()) {
2068 rval.setObject(entry->vword.toFunObj());
2069 } else if (entry->vword.isSlot()) {
2070 uint32 slot = entry->vword.toSlot();
2071 rval = obj2->nativeGetSlot(slot);
2072 } else {
2073 JS_ASSERT(entry->vword.isShape());
2074 const Shape *shape = entry->vword.toShape();
2075 NATIVE_GET(cx, &objv.toObject(), obj2, shape, JSGET_NO_METHOD_BARRIER, &rval,
2076 THROW());
2078 regs.sp++;
2079 regs.sp[-2] = rval;
2080 regs.sp[-1] = lval;
2081 } else {
2083 * Cache miss: use the immediate atom that was loaded for us under
2084 * PropertyCache::test.
2086 jsid id;
2087 id = ATOM_TO_JSID(origAtom);
2089 regs.sp++;
2090 regs.sp[-1].setNull();
2091 if (lval.isObject()) {
2092 if (!js_GetMethod(cx, &objv.toObject(), id,
2093 JS_LIKELY(!aobj->getOps()->getProperty)
2094 ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
2095 : JSGET_NO_METHOD_BARRIER,
2096 &rval)) {
2097 THROW();
2099 regs.sp[-1] = objv;
2100 regs.sp[-2] = rval;
2101 } else {
2102 JS_ASSERT(!objv.toObject().getOps()->getProperty);
2103 if (!js_GetPropertyHelper(cx, &objv.toObject(), id,
2104 JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER,
2105 &rval)) {
2106 THROW();
2108 regs.sp[-1] = lval;
2109 regs.sp[-2] = rval;
2112 #if JS_HAS_NO_SUCH_METHOD
2113 if (JS_UNLIKELY(rval.isUndefined()) && regs.sp[-1].isObject()) {
2114 regs.sp[-2].setString(ATOM_TO_STRING(origAtom));
2115 if (!js_OnUnknownMethod(cx, regs.sp - 2))
2116 THROW();
2118 #endif
2121 void JS_FASTCALL
2122 stubs::Length(VMFrame &f)
2124 JSFrameRegs &regs = f.regs;
2125 Value *vp = &regs.sp[-1];
2127 if (vp->isString()) {
2128 vp->setInt32(vp->toString()->length());
2129 return;
2130 } else if (vp->isObject()) {
2131 JSObject *obj = &vp->toObject();
2132 if (obj->isArray()) {
2133 jsuint length = obj->getArrayLength();
2134 regs.sp[-1].setNumber(length);
2135 return;
2136 } else if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
2137 uint32 length = obj->getArgsInitialLength();
2138 JS_ASSERT(length < INT32_MAX);
2139 regs.sp[-1].setInt32(int32_t(length));
2140 return;
2144 if (!InlineGetProp(f))
2145 THROW();
2148 void JS_FASTCALL
2149 stubs::Iter(VMFrame &f, uint32 flags)
2151 if (!js_ValueToIterator(f.cx, flags, &f.regs.sp[-1]))
2152 THROW();
2153 JS_ASSERT(!f.regs.sp[-1].isPrimitive());
2156 static void
2157 InitPropOrMethod(VMFrame &f, JSAtom *atom, JSOp op)
2159 JSContext *cx = f.cx;
2160 JSRuntime *rt = cx->runtime;
2161 JSFrameRegs &regs = f.regs;
2163 /* Load the property's initial value into rval. */
2164 JS_ASSERT(regs.sp - f.fp()->base() >= 2);
2165 Value rval;
2166 rval = regs.sp[-1];
2168 /* Load the object being initialized into lval/obj. */
2169 JSObject *obj = &regs.sp[-2].toObject();
2170 JS_ASSERT(obj->isNative());
2173 * Probe the property cache.
2175 * We can not assume that the object created by JSOP_NEWINIT is still
2176 * single-threaded as the debugger can access it from other threads.
2177 * So check first.
2179 * On a hit, if the cached shape has a non-default setter, it must be
2180 * __proto__. If shape->previous() != obj->lastProperty(), there must be a
2181 * repeated property name. The fast path does not handle these two cases.
2183 PropertyCacheEntry *entry;
2184 const Shape *shape;
2185 if (JS_PROPERTY_CACHE(cx).testForInit(rt, regs.pc, obj, &shape, &entry) &&
2186 shape->hasDefaultSetter() &&
2187 shape->previous() == obj->lastProperty())
2189 /* Fast path. Property cache hit. */
2190 uint32 slot = shape->slot;
2192 JS_ASSERT(slot == obj->slotSpan());
2193 JS_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
2194 if (slot < obj->numSlots()) {
2195 JS_ASSERT(obj->getSlot(slot).isUndefined());
2196 } else {
2197 if (!obj->allocSlot(cx, &slot))
2198 THROW();
2199 JS_ASSERT(slot == shape->slot);
2202 /* A new object, or one we just extended in a recent initprop op. */
2203 JS_ASSERT(!obj->lastProperty() ||
2204 obj->shape() == obj->lastProperty()->shape);
2205 obj->extend(cx, shape);
2208 * No method change check here because here we are adding a new
2209 * property, not updating an existing slot's value that might
2210 * contain a method of a branded shape.
2212 obj->nativeSetSlot(slot, rval);
2213 } else {
2214 PCMETER(JS_PROPERTY_CACHE(cx).inipcmisses++);
2216 /* Get the immediate property name into id. */
2217 jsid id = ATOM_TO_JSID(atom);
2219 /* No need to check for duplicate property; the compiler already did. */
2221 uintN defineHow = (op == JSOP_INITMETHOD)
2222 ? JSDNP_CACHE_RESULT | JSDNP_SET_METHOD
2223 : JSDNP_CACHE_RESULT;
2224 if (!(JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom)
2225 ? js_SetPropertyHelper(cx, obj, id, defineHow, &rval, false)
2226 : js_DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
2227 JSPROP_ENUMERATE, 0, 0, NULL,
2228 defineHow))) {
2229 THROW();
2234 void JS_FASTCALL
2235 stubs::InitProp(VMFrame &f, JSAtom *atom)
2237 InitPropOrMethod(f, atom, JSOP_INITPROP);
2240 void JS_FASTCALL
2241 stubs::InitMethod(VMFrame &f, JSAtom *atom)
2243 InitPropOrMethod(f, atom, JSOP_INITMETHOD);
2246 void JS_FASTCALL
2247 stubs::IterNext(VMFrame &f)
2249 JS_ASSERT(f.regs.sp - 1 >= f.fp()->base());
2250 JS_ASSERT(f.regs.sp[-1].isObject());
2252 JSObject *iterobj = &f.regs.sp[-1].toObject();
2253 f.regs.sp[0].setNull();
2254 f.regs.sp++;
2255 if (!js_IteratorNext(f.cx, iterobj, &f.regs.sp[-1]))
2256 THROW();
2259 JSBool JS_FASTCALL
2260 stubs::IterMore(VMFrame &f)
2262 JS_ASSERT(f.regs.sp - 1 >= f.fp()->base());
2263 JS_ASSERT(f.regs.sp[-1].isObject());
2265 Value v;
2266 JSObject *iterobj = &f.regs.sp[-1].toObject();
2267 if (!js_IteratorMore(f.cx, iterobj, &v))
2268 THROWV(JS_FALSE);
2270 return v.toBoolean();
2273 void JS_FASTCALL
2274 stubs::EndIter(VMFrame &f)
2276 JS_ASSERT(f.regs.sp - 1 >= f.fp()->base());
2277 if (!js_CloseIterator(f.cx, &f.regs.sp[-1].toObject()))
2278 THROW();
2281 JSString * JS_FASTCALL
2282 stubs::TypeOf(VMFrame &f)
2284 const Value &ref = f.regs.sp[-1];
2285 JSType type = JS_TypeOfValue(f.cx, Jsvalify(ref));
2286 JSAtom *atom = f.cx->runtime->atomState.typeAtoms[type];
2287 return ATOM_TO_STRING(atom);
2290 void JS_FASTCALL
2291 stubs::StrictEq(VMFrame &f)
2293 const Value &rhs = f.regs.sp[-1];
2294 const Value &lhs = f.regs.sp[-2];
2295 const bool b = StrictlyEqual(f.cx, lhs, rhs) == true;
2296 f.regs.sp--;
2297 f.regs.sp[-1].setBoolean(b);
2300 void JS_FASTCALL
2301 stubs::StrictNe(VMFrame &f)
2303 const Value &rhs = f.regs.sp[-1];
2304 const Value &lhs = f.regs.sp[-2];
2305 const bool b = StrictlyEqual(f.cx, lhs, rhs) != true;
2306 f.regs.sp--;
2307 f.regs.sp[-1].setBoolean(b);
2310 void JS_FASTCALL
2311 stubs::Throw(VMFrame &f)
2313 JSContext *cx = f.cx;
2315 JS_ASSERT(!cx->throwing);
2316 cx->throwing = JS_TRUE;
2317 cx->exception = f.regs.sp[-1];
2318 THROW();
2321 JSObject * JS_FASTCALL
2322 stubs::FlatLambda(VMFrame &f, JSFunction *fun)
2324 JSObject *obj = js_NewFlatClosure(f.cx, fun, JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH);
2325 if (!obj)
2326 THROWV(NULL);
2327 return obj;
2330 void JS_FASTCALL
2331 stubs::Arguments(VMFrame &f)
2333 f.regs.sp++;
2334 if (!js_GetArgsValue(f.cx, f.fp(), &f.regs.sp[-1]))
2335 THROW();
2338 JSBool JS_FASTCALL
2339 stubs::InstanceOf(VMFrame &f)
2341 JSContext *cx = f.cx;
2342 JSFrameRegs &regs = f.regs;
2344 const Value &rref = regs.sp[-1];
2345 if (rref.isPrimitive()) {
2346 js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
2347 -1, rref, NULL);
2348 THROWV(JS_FALSE);
2350 JSObject *obj = &rref.toObject();
2351 const Value &lref = regs.sp[-2];
2352 JSBool cond = JS_FALSE;
2353 if (!HasInstance(cx, obj, &lref, &cond))
2354 THROWV(JS_FALSE);
2355 f.regs.sp[-2].setBoolean(cond);
2356 return cond;
2359 void JS_FASTCALL
2360 stubs::FastInstanceOf(VMFrame &f)
2362 const Value &lref = f.regs.sp[-1];
2364 if (lref.isPrimitive()) {
2366 * Throw a runtime error if instanceof is called on a function that
2367 * has a non-object as its .prototype value.
2369 js_ReportValueError(f.cx, JSMSG_BAD_PROTOTYPE, -1, f.regs.sp[-2], NULL);
2370 THROW();
2373 f.regs.sp[-3].setBoolean(js_IsDelegate(f.cx, &lref.toObject(), f.regs.sp[-3]));
2376 void JS_FASTCALL
2377 stubs::ArgCnt(VMFrame &f)
2379 JSContext *cx = f.cx;
2380 JSRuntime *rt = cx->runtime;
2381 JSStackFrame *fp = f.fp();
2383 jsid id = ATOM_TO_JSID(rt->atomState.lengthAtom);
2384 f.regs.sp++;
2385 if (!js_GetArgsProperty(cx, fp, id, &f.regs.sp[-1]))
2386 THROW();
2389 void JS_FASTCALL
2390 stubs::EnterBlock(VMFrame &f, JSObject *obj)
2392 JSFrameRegs &regs = f.regs;
2393 #ifdef DEBUG
2394 JSStackFrame *fp = f.fp();
2395 #endif
2397 JS_ASSERT(obj->isStaticBlock());
2398 JS_ASSERT(fp->base() + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp);
2399 Value *vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj);
2400 JS_ASSERT(regs.sp < vp);
2401 JS_ASSERT(vp <= fp->slots() + fp->script()->nslots);
2402 SetValueRangeToUndefined(regs.sp, vp);
2403 regs.sp = vp;
2405 #ifdef DEBUG
2406 JSContext *cx = f.cx;
2409 * The young end of fp->scopeChain() may omit blocks if we haven't closed
2410 * over them, but if there are any closure blocks on fp->scopeChain(), they'd
2411 * better be (clones of) ancestors of the block we're entering now;
2412 * anything else we should have popped off fp->scopeChain() when we left its
2413 * static scope.
2415 JSObject *obj2 = &fp->scopeChain();
2416 Class *clasp;
2417 while ((clasp = obj2->getClass()) == &js_WithClass)
2418 obj2 = obj2->getParent();
2419 if (clasp == &js_BlockClass &&
2420 obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, fp)) {
2421 JSObject *youngestProto = obj2->getProto();
2422 JS_ASSERT(youngestProto->isStaticBlock());
2423 JSObject *parent = obj;
2424 while ((parent = parent->getParent()) != youngestProto)
2425 JS_ASSERT(parent);
2427 #endif
2430 void JS_FASTCALL
2431 stubs::LeaveBlock(VMFrame &f, JSObject *blockChain)
2433 JSContext *cx = f.cx;
2434 JSStackFrame *fp = f.fp();
2436 #ifdef DEBUG
2437 JS_ASSERT(blockChain->isStaticBlock());
2438 uintN blockDepth = OBJ_BLOCK_DEPTH(cx, blockChain);
2440 JS_ASSERT(blockDepth <= StackDepth(fp->script()));
2441 #endif
2443 * If we're about to leave the dynamic scope of a block that has been
2444 * cloned onto fp->scopeChain(), clear its private data, move its locals from
2445 * the stack into the clone, and pop it off the chain.
2447 JSObject *obj = &fp->scopeChain();
2448 if (obj->getProto() == blockChain) {
2449 JS_ASSERT(obj->getClass() == &js_BlockClass);
2450 if (!js_PutBlockObject(cx, JS_TRUE))
2451 THROW();
2455 void * JS_FASTCALL
2456 stubs::LookupSwitch(VMFrame &f, jsbytecode *pc)
2458 jsbytecode *jpc = pc;
2459 JSScript *script = f.fp()->script();
2460 void **nmap = script->nativeMap(f.fp()->isConstructing());
2462 /* This is correct because the compiler adjusts the stack beforehand. */
2463 Value lval = f.regs.sp[-1];
2465 if (!lval.isPrimitive()) {
2466 ptrdiff_t offs = (pc + GET_JUMP_OFFSET(pc)) - script->code;
2467 JS_ASSERT(nmap[offs]);
2468 return nmap[offs];
2471 JS_ASSERT(pc[0] == JSOP_LOOKUPSWITCH);
2473 pc += JUMP_OFFSET_LEN;
2474 uint32 npairs = GET_UINT16(pc);
2475 pc += UINT16_LEN;
2477 JS_ASSERT(npairs);
2479 if (lval.isString()) {
2480 JSString *str = lval.toString();
2481 for (uint32 i = 1; i <= npairs; i++) {
2482 Value rval = script->getConst(GET_INDEX(pc));
2483 pc += INDEX_LEN;
2484 if (rval.isString()) {
2485 JSString *rhs = rval.toString();
2486 if (rhs == str || js_EqualStrings(str, rhs)) {
2487 ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code;
2488 JS_ASSERT(nmap[offs]);
2489 return nmap[offs];
2492 pc += JUMP_OFFSET_LEN;
2494 } else if (lval.isNumber()) {
2495 double d = lval.toNumber();
2496 for (uint32 i = 1; i <= npairs; i++) {
2497 Value rval = script->getConst(GET_INDEX(pc));
2498 pc += INDEX_LEN;
2499 if (rval.isNumber() && d == rval.toNumber()) {
2500 ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code;
2501 JS_ASSERT(nmap[offs]);
2502 return nmap[offs];
2504 pc += JUMP_OFFSET_LEN;
2506 } else {
2507 for (uint32 i = 1; i <= npairs; i++) {
2508 Value rval = script->getConst(GET_INDEX(pc));
2509 pc += INDEX_LEN;
2510 if (lval == rval) {
2511 ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code;
2512 JS_ASSERT(nmap[offs]);
2513 return nmap[offs];
2515 pc += JUMP_OFFSET_LEN;
2519 ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(jpc)) - script->code;
2520 JS_ASSERT(nmap[offs]);
2521 return nmap[offs];
2524 void * JS_FASTCALL
2525 stubs::TableSwitch(VMFrame &f, jsbytecode *origPc)
2527 jsbytecode * const originalPC = origPc;
2528 jsbytecode *pc = originalPC;
2529 uint32 jumpOffset = GET_JUMP_OFFSET(pc);
2530 pc += JUMP_OFFSET_LEN;
2532 /* Note: compiler adjusts the stack beforehand. */
2533 Value rval = f.regs.sp[-1];
2535 jsint tableIdx;
2536 if (rval.isInt32()) {
2537 tableIdx = rval.toInt32();
2538 } else if (rval.isDouble()) {
2539 double d = rval.toDouble();
2540 if (d == 0) {
2541 /* Treat -0 (double) as 0. */
2542 tableIdx = 0;
2543 } else if (!JSDOUBLE_IS_INT32(d, (int32_t *)&tableIdx)) {
2544 goto finally;
2546 } else {
2547 goto finally;
2551 uint32 low = GET_JUMP_OFFSET(pc);
2552 pc += JUMP_OFFSET_LEN;
2553 uint32 high = GET_JUMP_OFFSET(pc);
2554 pc += JUMP_OFFSET_LEN;
2556 tableIdx -= low;
2557 if ((jsuint) tableIdx < (jsuint)(high - low + 1)) {
2558 pc += JUMP_OFFSET_LEN * tableIdx;
2559 uint32 candidateOffset = GET_JUMP_OFFSET(pc);
2560 if (candidateOffset)
2561 jumpOffset = candidateOffset;
2565 finally:
2566 JSScript *script = f.fp()->script();
2567 void **nmap = script->nativeMap(f.fp()->isConstructing());
2569 /* Provide the native address. */
2570 ptrdiff_t offset = (originalPC + jumpOffset) - script->code;
2571 JS_ASSERT(nmap[offset]);
2572 return nmap[offset];
2575 void JS_FASTCALL
2576 stubs::Unbrand(VMFrame &f)
2578 const Value &thisv = f.regs.sp[-1];
2579 if (!thisv.isObject())
2580 return;
2581 JSObject *obj = &thisv.toObject();
2582 if (obj->isNative() && !obj->unbrand(f.cx))
2583 THROW();
2586 void JS_FASTCALL
2587 stubs::Pos(VMFrame &f)
2589 if (!ValueToNumber(f.cx, &f.regs.sp[-1]))
2590 THROW();
2593 void JS_FASTCALL
2594 stubs::ArgSub(VMFrame &f, uint32 n)
2596 jsid id = INT_TO_JSID(n);
2597 Value rval;
2598 if (!js_GetArgsProperty(f.cx, f.fp(), id, &rval))
2599 THROW();
2600 f.regs.sp[0] = rval;
2603 void JS_FASTCALL
2604 stubs::DelName(VMFrame &f, JSAtom *atom)
2606 jsid id = ATOM_TO_JSID(atom);
2607 JSObject *obj, *obj2;
2608 JSProperty *prop;
2609 if (!js_FindProperty(f.cx, id, &obj, &obj2, &prop))
2610 THROW();
2612 /* Strict mode code should never contain JSOP_DELNAME opcodes. */
2613 JS_ASSERT(!f.fp()->script()->strictModeCode);
2615 /* ECMA says to return true if name is undefined or inherited. */
2616 f.regs.sp++;
2617 f.regs.sp[-1] = BooleanValue(true);
2618 if (prop) {
2619 if (!obj->deleteProperty(f.cx, id, &f.regs.sp[-1], false))
2620 THROW();
2624 template<JSBool strict>
2625 void JS_FASTCALL
2626 stubs::DelProp(VMFrame &f, JSAtom *atom)
2628 JSContext *cx = f.cx;
2630 JSObject *obj = ValueToObject(cx, &f.regs.sp[-1]);
2631 if (!obj)
2632 THROW();
2634 Value rval;
2635 if (!obj->deleteProperty(cx, ATOM_TO_JSID(atom), &rval, strict))
2636 THROW();
2638 f.regs.sp[-1] = rval;
2641 template void JS_FASTCALL stubs::DelProp<true>(VMFrame &f, JSAtom *atom);
2642 template void JS_FASTCALL stubs::DelProp<false>(VMFrame &f, JSAtom *atom);
2644 template<JSBool strict>
2645 void JS_FASTCALL
2646 stubs::DelElem(VMFrame &f)
2648 JSContext *cx = f.cx;
2650 JSObject *obj = ValueToObject(cx, &f.regs.sp[-2]);
2651 if (!obj)
2652 THROW();
2654 jsid id;
2655 if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
2656 THROW();
2658 if (!obj->deleteProperty(cx, id, &f.regs.sp[-2], strict))
2659 THROW();
2662 void JS_FASTCALL
2663 stubs::DefVar(VMFrame &f, JSAtom *atom)
2665 JSContext *cx = f.cx;
2666 JSStackFrame *fp = f.fp();
2668 JSObject *obj = &fp->varobj(cx);
2669 JS_ASSERT(!obj->getOps()->defineProperty);
2670 uintN attrs = JSPROP_ENUMERATE;
2671 if (!fp->isEvalFrame())
2672 attrs |= JSPROP_PERMANENT;
2674 /* Lookup id in order to check for redeclaration problems. */
2675 jsid id = ATOM_TO_JSID(atom);
2676 JSProperty *prop = NULL;
2677 JSObject *obj2;
2680 * Redundant declaration of a |var|, even one for a non-writable
2681 * property like |undefined| in ES5, does nothing.
2683 if (!obj->lookupProperty(cx, id, &obj2, &prop))
2684 THROW();
2686 /* Bind a variable only if it's not yet defined. */
2687 if (!prop) {
2688 if (!js_DefineNativeProperty(cx, obj, id, UndefinedValue(), PropertyStub, PropertyStub,
2689 attrs, 0, 0, &prop)) {
2690 THROW();
2692 JS_ASSERT(prop);
2693 obj2 = obj;
2697 JSBool JS_FASTCALL
2698 stubs::In(VMFrame &f)
2700 JSContext *cx = f.cx;
2702 const Value &rref = f.regs.sp[-1];
2703 if (!rref.isObject()) {
2704 js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rref, NULL);
2705 THROWV(JS_FALSE);
2708 JSObject *obj = &rref.toObject();
2709 jsid id;
2710 if (!FetchElementId(f, obj, f.regs.sp[-2], id, &f.regs.sp[-2]))
2711 THROWV(JS_FALSE);
2713 JSObject *obj2;
2714 JSProperty *prop;
2715 if (!obj->lookupProperty(cx, id, &obj2, &prop))
2716 THROWV(JS_FALSE);
2718 return !!prop;
2721 template void JS_FASTCALL stubs::DelElem<true>(VMFrame &f);
2722 template void JS_FASTCALL stubs::DelElem<false>(VMFrame &f);