CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / js / src / methodjit / StubCalls.cpp
blobabaffd825355126681b6328d12a93b1ca32001f8
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"
68 #include "jstypedarray.h"
70 #ifdef XP_WIN
71 # include "jswin.h"
72 #endif
74 #include "jsautooplen.h"
76 using namespace js;
77 using namespace js::mjit;
78 using namespace JSC;
80 void JS_FASTCALL
81 stubs::BindName(VMFrame &f)
83 PropertyCacheEntry *entry;
85 /* Fast-path should have caught this. See comment in interpreter. */
86 JS_ASSERT(f.fp()->scopeChain().getParent());
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();
99 f.regs.sp++;
100 f.regs.sp[-1].setObject(*obj);
103 void JS_FASTCALL
104 stubs::BindNameNoCache(VMFrame &f, JSAtom *atom)
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);
112 JSObject * JS_FASTCALL
113 stubs::BindGlobalName(VMFrame &f)
115 return f.fp()->scopeChain().getGlobal();
118 template<JSBool strict>
119 void JS_FASTCALL
120 stubs::SetName(VMFrame &f, JSAtom *origAtom)
122 JSContext *cx = f.cx;
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();
130 do {
131 PropertyCache *cache = &JS_PROPERTY_CACHE(cx);
134 * Probe the property cache, specializing for two important
135 * set-property cases. First:
137 * function f(a, b, c) {
138 * var o = {p:a, q:b, r:c};
139 * return o;
142 * or similar real-world cases, which evolve a newborn native
143 * object predicatably through some bounded number of property
144 * additions. And second:
146 * o.p = x;
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.
152 PropertyCacheEntry *entry;
153 JSObject *obj2;
154 JSAtom *atom;
155 if (cache->testForSet(cx, f.regs.pc, obj, &entry, &obj2, &atom)) {
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.
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.
165 const Shape *shape = entry->vword.toShape();
166 JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable());
167 JS_ASSERT_IF(shape->hasSlot(), entry->vcapTag() == 0);
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.
174 if (!entry->adding()) {
175 if (entry->vcapTag() == 0 ||
176 ((obj2 = obj->getProto()) && obj2->shape() == entry->vshape()))
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());
187 #endif
189 PCMETER(cache->pchits++);
190 PCMETER(cache->setpchits++);
191 NATIVE_SET(cx, obj, shape, entry, strict, &rval);
192 break;
194 } else {
195 JS_ASSERT(obj->isExtensible());
197 if (obj->nativeEmpty()) {
198 if (!obj->ensureClassReservedSlotsForEmptyObject(cx))
199 THROW();
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());
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.
215 PCMETER(cache->pchits++);
216 PCMETER(cache->addpchits++);
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);
226 /* Simply extend obj's property tree path with shape! */
227 obj->extend(cx, shape);
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.
234 obj->setSlot(slot, rval);
237 * Purge the property cache of the id we may have just
238 * shadowed in obj's scope and proto chains.
240 js_PurgeScopeChain(cx, obj, shape->id);
241 break;
244 PCMETER(cache->setpcmisses++);
246 atom = origAtom;
247 } else {
248 JS_ASSERT(atom);
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();
267 } while (0);
269 f.regs.sp[-2] = f.regs.sp[-1];
272 template void JS_FASTCALL stubs::SetName<true>(VMFrame &f, JSAtom *origAtom);
273 template void JS_FASTCALL stubs::SetName<false>(VMFrame &f, JSAtom *origAtom);
275 template<JSBool strict>
276 void JS_FASTCALL
277 stubs::SetPropNoCache(VMFrame &f, JSAtom *atom)
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;
288 template void JS_FASTCALL stubs::SetPropNoCache<true>(VMFrame &f, JSAtom *origAtom);
289 template void JS_FASTCALL stubs::SetPropNoCache<false>(VMFrame &f, JSAtom *origAtom);
291 template<JSBool strict>
292 void JS_FASTCALL
293 stubs::SetGlobalNameNoCache(VMFrame &f, JSAtom *atom)
295 JSContext *cx = f.cx;
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();
306 f.regs.sp[-2] = f.regs.sp[-1];
309 template void JS_FASTCALL stubs::SetGlobalNameNoCache<true>(VMFrame &f, JSAtom *atom);
310 template void JS_FASTCALL stubs::SetGlobalNameNoCache<false>(VMFrame &f, JSAtom *atom);
312 template<JSBool strict>
313 void JS_FASTCALL
314 stubs::SetGlobalName(VMFrame &f, JSAtom *atom)
316 SetName<strict>(f, atom);
319 template void JS_FASTCALL stubs::SetGlobalName<true>(VMFrame &f, JSAtom *atom);
320 template void JS_FASTCALL stubs::SetGlobalName<false>(VMFrame &f, JSAtom *atom);
322 static JSObject *
323 NameOp(VMFrame &f, JSObject *obj, bool callname = false)
325 JSContext *cx = f.cx;
327 const Shape *shape;
328 Value rval;
330 PropertyCacheEntry *entry;
331 JSObject *obj2;
332 JSAtom *atom;
333 JS_PROPERTY_CACHE(cx).test(cx, f.regs.pc, obj, obj2, entry, atom);
334 if (!atom) {
335 if (entry->vword.isFunObj()) {
336 f.regs.sp++;
337 f.regs.sp[-1].setObject(entry->vword.toFunObj());
338 } else if (entry->vword.isSlot()) {
339 uintN slot = entry->vword.toSlot();
340 f.regs.sp++;
341 f.regs.sp[-1] = obj2->nativeGetSlot(slot);
342 } else {
343 JS_ASSERT(entry->vword.isShape());
344 shape = entry->vword.toShape();
345 NATIVE_GET(cx, obj, obj2, shape, JSGET_METHOD_BARRIER, &rval, return NULL);
346 f.regs.sp++;
347 f.regs.sp[-1] = rval;
351 * Push results, the same as below, but with a prop$ hit there
352 * is no need to test for the unusual and uncacheable case where
353 * the caller determines |this|.
355 #if DEBUG
356 Class *clasp;
357 JS_ASSERT(!obj->getParent() ||
358 (clasp = obj->getClass()) == &js_CallClass ||
359 clasp == &js_BlockClass ||
360 clasp == &js_DeclEnvClass);
361 #endif
362 if (callname) {
363 f.regs.sp++;
364 f.regs.sp[-1].setUndefined();
366 return obj;
369 jsid id;
370 id = ATOM_TO_JSID(atom);
371 JSProperty *prop;
372 if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
373 return NULL;
374 if (!prop) {
375 /* Kludge to allow (typeof foo == "undefined") tests. */
376 JSOp op2 = js_GetOpcode(cx, f.fp()->script(), f.regs.pc + JSOP_NAME_LENGTH);
377 if (op2 == JSOP_TYPEOF) {
378 f.regs.sp++;
379 f.regs.sp[-1].setUndefined();
380 return obj;
382 ReportAtomNotDefined(cx, atom);
383 return NULL;
386 /* Take the slow path if prop was not found in a native object. */
387 if (!obj->isNative() || !obj2->isNative()) {
388 if (!obj->getProperty(cx, id, &rval))
389 return NULL;
390 } else {
391 shape = (Shape *)prop;
392 JSObject *normalized = obj;
393 if (normalized->getClass() == &js_WithClass && !shape->hasDefaultGetter())
394 normalized = js_UnwrapWithObject(cx, normalized);
395 NATIVE_GET(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, &rval, return NULL);
398 f.regs.sp++;
399 f.regs.sp[-1] = rval;
400 if (callname) {
401 Class *clasp;
402 JSObject *thisp = obj;
403 if (!thisp->getParent() ||
404 (clasp = thisp->getClass()) == &js_CallClass ||
405 clasp == &js_BlockClass ||
406 clasp == &js_DeclEnvClass) {
407 f.regs.sp++;
408 f.regs.sp[-1].setUndefined();
409 } else {
410 thisp = thisp->thisObject(cx);
411 if (!thisp)
412 return NULL;
413 f.regs.sp++;
414 f.regs.sp[-1].setObject(*thisp);
417 return obj;
420 void JS_FASTCALL
421 stubs::Name(VMFrame &f)
423 if (!NameOp(f, &f.fp()->scopeChain()))
424 THROW();
427 void JS_FASTCALL
428 stubs::GetGlobalName(VMFrame &f)
430 JSObject *globalObj = f.fp()->scopeChain().getGlobal();
431 if (!NameOp(f, globalObj))
432 THROW();
435 void JS_FASTCALL
436 stubs::GetElem(VMFrame &f)
438 JSContext *cx = f.cx;
439 JSFrameRegs &regs = f.regs;
441 Value &lref = regs.sp[-2];
442 Value &rref = regs.sp[-1];
443 if (lref.isString() && rref.isInt32()) {
444 JSString *str = lref.toString();
445 int32_t i = rref.toInt32();
446 if ((size_t)i < str->length()) {
447 str = JSString::getUnitString(cx, str, (size_t)i);
448 if (!str)
449 THROW();
450 f.regs.sp[-2].setString(str);
451 return;
455 JSObject *obj = ValueToObject(cx, &lref);
456 if (!obj)
457 THROW();
459 const Value *copyFrom;
460 Value rval;
461 jsid id;
462 if (rref.isInt32()) {
463 int32_t i = rref.toInt32();
464 if (obj->isDenseArray()) {
465 jsuint idx = jsuint(i);
467 if (idx < obj->getArrayLength() &&
468 idx < obj->getDenseArrayCapacity()) {
469 copyFrom = obj->addressOfDenseArrayElement(idx);
470 if (!copyFrom->isMagic())
471 goto end_getelem;
473 } else if (obj->isArguments()) {
474 uint32 arg = uint32(i);
476 if (arg < obj->getArgsInitialLength()) {
477 copyFrom = obj->addressOfArgsElement(arg);
478 if (!copyFrom->isMagic()) {
479 if (JSStackFrame *afp = (JSStackFrame *) obj->getPrivate())
480 copyFrom = &afp->canonicalActualArg(arg);
481 goto end_getelem;
485 if (JS_LIKELY(INT_FITS_IN_JSID(i)))
486 id = INT_TO_JSID(i);
487 else
488 goto intern_big_int;
490 } else {
491 int32_t i;
492 if (ValueFitsInInt32(rref, &i) && INT_FITS_IN_JSID(i)) {
493 id = INT_TO_JSID(i);
494 } else {
495 intern_big_int:
496 if (!js_InternNonIntElementId(cx, obj, rref, &id))
497 THROW();
501 if (!obj->getProperty(cx, id, &rval))
502 THROW();
503 copyFrom = &rval;
505 end_getelem:
506 f.regs.sp[-2] = *copyFrom;
509 static inline bool
510 FetchElementId(VMFrame &f, JSObject *obj, const Value &idval, jsid &id, Value *vp)
512 int32_t i_;
513 if (ValueFitsInInt32(idval, &i_) && INT_FITS_IN_JSID(i_)) {
514 id = INT_TO_JSID(i_);
515 return true;
517 return !!js_InternNonIntElementId(f.cx, obj, idval, &id, vp);
520 void JS_FASTCALL
521 stubs::CallElem(VMFrame &f)
523 JSContext *cx = f.cx;
524 JSFrameRegs &regs = f.regs;
526 /* Find the object on which to look for |this|'s properties. */
527 Value thisv = regs.sp[-2];
528 JSObject *thisObj = ValuePropertyBearer(cx, thisv, -2);
529 if (!thisObj)
530 THROW();
532 /* Fetch index and convert it to id suitable for use with thisObj. */
533 jsid id;
534 if (!FetchElementId(f, thisObj, regs.sp[-1], id, &regs.sp[-2]))
535 THROW();
537 /* Get or set the element. */
538 if (!js_GetMethod(cx, thisObj, id, JSGET_NO_METHOD_BARRIER, &regs.sp[-2]))
539 THROW();
541 #if JS_HAS_NO_SUCH_METHOD
542 if (JS_UNLIKELY(regs.sp[-2].isUndefined()) && thisv.isObject()) {
543 regs.sp[-2] = regs.sp[-1];
544 regs.sp[-1].setObject(*thisObj);
545 if (!js_OnUnknownMethod(cx, regs.sp - 2))
546 THROW();
547 } else
548 #endif
550 regs.sp[-1] = thisv;
554 template<JSBool strict>
555 void JS_FASTCALL
556 stubs::SetElem(VMFrame &f)
558 JSContext *cx = f.cx;
559 JSFrameRegs &regs = f.regs;
561 Value &objval = regs.sp[-3];
562 Value &idval = regs.sp[-2];
563 Value rval = regs.sp[-1];
565 JSObject *obj;
566 jsid id;
568 obj = ValueToObject(cx, &objval);
569 if (!obj)
570 THROW();
572 if (!FetchElementId(f, obj, idval, id, &regs.sp[-2]))
573 THROW();
575 do {
576 if (obj->isDenseArray() && JSID_IS_INT(id)) {
577 jsuint length = obj->getDenseArrayCapacity();
578 jsint i = JSID_TO_INT(id);
579 if ((jsuint)i < length) {
580 if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
581 if (js_PrototypeHasIndexedProperties(cx, obj))
582 break;
583 if ((jsuint)i >= obj->getArrayLength())
584 obj->setArrayLength(i + 1);
586 obj->setDenseArrayElement(i, rval);
587 goto end_setelem;
590 } while (0);
591 if (!obj->setProperty(cx, id, &rval, strict))
592 THROW();
593 end_setelem:
594 /* :FIXME: Moving the assigned object into the lowest stack slot
595 * is a temporary hack. What we actually want is an implementation
596 * of popAfterSet() that allows popping more than one value;
597 * this logic can then be handled in Compiler.cpp. */
598 regs.sp[-3] = regs.sp[-1];
601 template void JS_FASTCALL stubs::SetElem<true>(VMFrame &f);
602 template void JS_FASTCALL stubs::SetElem<false>(VMFrame &f);
604 void JS_FASTCALL
605 stubs::CallName(VMFrame &f)
607 JSObject *obj = NameOp(f, &f.fp()->scopeChain(), true);
608 if (!obj)
609 THROW();
612 void JS_FASTCALL
613 stubs::BitOr(VMFrame &f)
615 int32_t i, j;
617 if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i) ||
618 !ValueToECMAInt32(f.cx, f.regs.sp[-1], &j)) {
619 THROW();
621 i = i | j;
622 f.regs.sp[-2].setInt32(i);
625 void JS_FASTCALL
626 stubs::BitXor(VMFrame &f)
628 int32_t i, j;
630 if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i) ||
631 !ValueToECMAInt32(f.cx, f.regs.sp[-1], &j)) {
632 THROW();
634 i = i ^ j;
635 f.regs.sp[-2].setInt32(i);
638 void JS_FASTCALL
639 stubs::BitAnd(VMFrame &f)
641 int32_t i, j;
643 if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i) ||
644 !ValueToECMAInt32(f.cx, f.regs.sp[-1], &j)) {
645 THROW();
647 i = i & j;
648 f.regs.sp[-2].setInt32(i);
651 void JS_FASTCALL
652 stubs::BitNot(VMFrame &f)
654 int32_t i;
656 if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &i))
657 THROW();
658 i = ~i;
659 f.regs.sp[-1].setInt32(i);
662 void JS_FASTCALL
663 stubs::Lsh(VMFrame &f)
665 int32_t i, j;
666 if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i))
667 THROW();
668 if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &j))
669 THROW();
670 i = i << (j & 31);
671 f.regs.sp[-2].setInt32(i);
674 void JS_FASTCALL
675 stubs::Rsh(VMFrame &f)
677 int32_t i, j;
678 if (!ValueToECMAInt32(f.cx, f.regs.sp[-2], &i))
679 THROW();
680 if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &j))
681 THROW();
682 i = i >> (j & 31);
683 f.regs.sp[-2].setInt32(i);
686 void JS_FASTCALL
687 stubs::Ursh(VMFrame &f)
689 uint32_t u;
690 if (!ValueToECMAUint32(f.cx, f.regs.sp[-2], &u))
691 THROW();
692 int32_t j;
693 if (!ValueToECMAInt32(f.cx, f.regs.sp[-1], &j))
694 THROW();
696 u >>= (j & 31);
698 f.regs.sp[-2].setNumber(uint32(u));
701 template<JSBool strict>
702 void JS_FASTCALL
703 stubs::DefFun(VMFrame &f, JSFunction *fun)
705 JSObject *obj2;
707 JSContext *cx = f.cx;
708 JSStackFrame *fp = f.fp();
711 * A top-level function defined in Global or Eval code (see ECMA-262
712 * Ed. 3), or else a SpiderMonkey extension: a named function statement in
713 * a compound statement (not at the top statement level of global code, or
714 * at the top level of a function body).
716 JSObject *obj = FUN_OBJECT(fun);
718 if (FUN_NULL_CLOSURE(fun)) {
720 * Even a null closure needs a parent for principals finding.
721 * FIXME: bug 476950, although debugger users may also demand some kind
722 * of scope link for debugger-assisted eval-in-frame.
724 obj2 = &fp->scopeChain();
725 } else {
726 JS_ASSERT(!fun->isFlatClosure());
728 obj2 = GetScopeChainFast(cx, fp, JSOP_DEFFUN, JSOP_DEFFUN_LENGTH);
729 if (!obj2)
730 THROW();
734 * If static link is not current scope, clone fun's object to link to the
735 * current scope via parent. We do this to enable sharing of compiled
736 * functions among multiple equivalent scopes, amortizing the cost of
737 * compilation over a number of executions. Examples include XUL scripts
738 * and event handlers shared among Firefox or other Mozilla app chrome
739 * windows, and user-defined JS functions precompiled and then shared among
740 * requests in server-side JS.
742 if (obj->getParent() != obj2) {
743 obj = CloneFunctionObject(cx, fun, obj2);
744 if (!obj)
745 THROW();
749 * ECMA requires functions defined when entering Eval code to be
750 * impermanent.
752 uintN attrs = fp->isEvalFrame()
753 ? JSPROP_ENUMERATE
754 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
757 * We define the function as a property of the variable object and not the
758 * current scope chain even for the case of function expression statements
759 * and functions defined by eval inside let or with blocks.
761 JSObject *parent = &fp->varobj(cx);
763 /* ES5 10.5 (NB: with subsequent errata). */
764 jsid id = ATOM_TO_JSID(fun->atom);
765 JSProperty *prop = NULL;
766 JSObject *pobj;
767 if (!parent->lookupProperty(cx, id, &pobj, &prop))
768 THROW();
770 Value rval = ObjectValue(*obj);
772 do {
773 /* Steps 5d, 5f. */
774 if (!prop || pobj != parent) {
775 if (!parent->defineProperty(cx, id, rval, PropertyStub, StrictPropertyStub, attrs))
776 THROW();
777 break;
780 /* Step 5e. */
781 JS_ASSERT(parent->isNative());
782 Shape *shape = reinterpret_cast<Shape *>(prop);
783 if (parent->isGlobal()) {
784 if (shape->configurable()) {
785 if (!parent->defineProperty(cx, id, rval, PropertyStub, StrictPropertyStub, attrs))
786 THROW();
787 break;
790 if (shape->isAccessorDescriptor() || !shape->writable() || !shape->enumerable()) {
791 JSAutoByteString bytes;
792 if (const char *name = js_ValueToPrintable(cx, IdToValue(id), &bytes)) {
793 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
794 JSMSG_CANT_REDEFINE_PROP, name);
796 THROW();
801 * Non-global properties, and global properties which we aren't simply
802 * redefining, must be set. First, this preserves their attributes.
803 * Second, this will produce warnings and/or errors as necessary if the
804 * specified Call object property is not writable (const).
807 /* Step 5f. */
808 if (!parent->setProperty(cx, id, &rval, strict))
809 THROW();
810 } while (false);
813 template void JS_FASTCALL stubs::DefFun<true>(VMFrame &f, JSFunction *fun);
814 template void JS_FASTCALL stubs::DefFun<false>(VMFrame &f, JSFunction *fun);
816 #define DEFAULT_VALUE(cx, n, hint, v) \
817 JS_BEGIN_MACRO \
818 JS_ASSERT(v.isObject()); \
819 JS_ASSERT(v == regs.sp[n]); \
820 if (!DefaultValue(cx, &v.toObject(), hint, &regs.sp[n])) \
821 THROWV(JS_FALSE); \
822 v = regs.sp[n]; \
823 JS_END_MACRO
825 #define RELATIONAL(OP) \
826 JS_BEGIN_MACRO \
827 JSContext *cx = f.cx; \
828 JSFrameRegs &regs = f.regs; \
829 Value rval = regs.sp[-1]; \
830 Value lval = regs.sp[-2]; \
831 bool cond; \
832 if (lval.isObject()) \
833 DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \
834 if (rval.isObject()) \
835 DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \
836 if (lval.isString() && rval.isString()) { \
837 JSString *l = lval.toString(), *r = rval.toString(); \
838 JSBool cmp; \
839 if (!CompareStrings(cx, l, r, &cmp)) \
840 THROWV(JS_FALSE); \
841 cond = cmp OP 0; \
842 } else { \
843 double l, r; \
844 if (!ValueToNumber(cx, lval, &l) || \
845 !ValueToNumber(cx, rval, &r)) { \
846 THROWV(JS_FALSE); \
848 cond = JSDOUBLE_COMPARE(l, OP, r, false); \
850 regs.sp[-2].setBoolean(cond); \
851 return cond; \
852 JS_END_MACRO
854 JSBool JS_FASTCALL
855 stubs::LessThan(VMFrame &f)
857 RELATIONAL(<);
860 JSBool JS_FASTCALL
861 stubs::LessEqual(VMFrame &f)
863 RELATIONAL(<=);
866 JSBool JS_FASTCALL
867 stubs::GreaterThan(VMFrame &f)
869 RELATIONAL(>);
872 JSBool JS_FASTCALL
873 stubs::GreaterEqual(VMFrame &f)
875 RELATIONAL(>=);
878 JSBool JS_FASTCALL
879 stubs::ValueToBoolean(VMFrame &f)
881 return js_ValueToBoolean(f.regs.sp[-1]);
884 void JS_FASTCALL
885 stubs::Not(VMFrame &f)
887 JSBool b = !js_ValueToBoolean(f.regs.sp[-1]);
888 f.regs.sp[-1].setBoolean(b);
891 template <JSBool EQ, bool IFNAN>
892 static inline bool
893 StubEqualityOp(VMFrame &f)
895 JSContext *cx = f.cx;
896 JSFrameRegs &regs = f.regs;
898 Value rval = regs.sp[-1];
899 Value lval = regs.sp[-2];
901 JSBool cond;
903 /* The string==string case is easily the hottest; try it first. */
904 if (lval.isString() && rval.isString()) {
905 JSString *l = lval.toString();
906 JSString *r = rval.toString();
907 JSBool equal;
908 if (!EqualStrings(cx, l, r, &equal))
909 return false;
910 cond = equal == EQ;
911 } else
912 #if JS_HAS_XML_SUPPORT
913 if ((lval.isObject() && lval.toObject().isXML()) ||
914 (rval.isObject() && rval.toObject().isXML())) {
915 if (!js_TestXMLEquality(cx, lval, rval, &cond))
916 return false;
917 cond = cond == EQ;
918 } else
919 #endif
921 if (SameType(lval, rval)) {
922 JS_ASSERT(!lval.isString()); /* this case is handled above */
923 if (lval.isDouble()) {
924 double l = lval.toDouble();
925 double r = rval.toDouble();
926 if (EQ)
927 cond = JSDOUBLE_COMPARE(l, ==, r, IFNAN);
928 else
929 cond = JSDOUBLE_COMPARE(l, !=, r, IFNAN);
930 } else if (lval.isObject()) {
931 JSObject *l = &lval.toObject(), *r = &rval.toObject();
932 l->assertSpecialEqualitySynced();
933 if (EqualityOp eq = l->getClass()->ext.equality) {
934 if (!eq(cx, l, &rval, &cond))
935 return false;
936 cond = cond == EQ;
937 } else {
938 cond = (l == r) == EQ;
940 } else if (lval.isNullOrUndefined()) {
941 cond = EQ;
942 } else {
943 cond = (lval.payloadAsRawUint32() == rval.payloadAsRawUint32()) == EQ;
945 } else {
946 if (lval.isNullOrUndefined()) {
947 cond = rval.isNullOrUndefined() == EQ;
948 } else if (rval.isNullOrUndefined()) {
949 cond = !EQ;
950 } else {
951 if (lval.isObject()) {
952 if (!DefaultValue(cx, &lval.toObject(), JSTYPE_VOID, &regs.sp[-2]))
953 return false;
954 lval = regs.sp[-2];
957 if (rval.isObject()) {
958 if (!DefaultValue(cx, &rval.toObject(), JSTYPE_VOID, &regs.sp[-1]))
959 return false;
960 rval = regs.sp[-1];
964 * The string==string case is repeated because DefaultValue() can
965 * convert lval/rval to strings.
967 if (lval.isString() && rval.isString()) {
968 JSString *l = lval.toString();
969 JSString *r = rval.toString();
970 JSBool equal;
971 if (!EqualStrings(cx, l, r, &equal))
972 return false;
973 cond = equal == EQ;
974 } else {
975 double l, r;
976 if (!ValueToNumber(cx, lval, &l) ||
977 !ValueToNumber(cx, rval, &r)) {
978 return false;
981 if (EQ)
982 cond = JSDOUBLE_COMPARE(l, ==, r, false);
983 else
984 cond = JSDOUBLE_COMPARE(l, !=, r, true);
989 regs.sp[-2].setBoolean(cond);
990 return true;
993 JSBool JS_FASTCALL
994 stubs::Equal(VMFrame &f)
996 if (!StubEqualityOp<JS_TRUE, false>(f))
997 THROWV(JS_FALSE);
998 return f.regs.sp[-2].toBoolean();
1001 JSBool JS_FASTCALL
1002 stubs::NotEqual(VMFrame &f)
1004 if (!StubEqualityOp<JS_FALSE, true>(f))
1005 THROWV(JS_FALSE);
1006 return f.regs.sp[-2].toBoolean();
1009 static inline bool
1010 DefaultValue(VMFrame &f, JSType hint, Value &v, int n)
1012 JS_ASSERT(v.isObject());
1013 if (!DefaultValue(f.cx, &v.toObject(), hint, &f.regs.sp[n]))
1014 return false;
1015 v = f.regs.sp[n];
1016 return true;
1019 void JS_FASTCALL
1020 stubs::Add(VMFrame &f)
1022 JSContext *cx = f.cx;
1023 JSFrameRegs &regs = f.regs;
1024 Value rval = regs.sp[-1];
1025 Value lval = regs.sp[-2];
1027 /* The string + string case is easily the hottest; try it first. */
1028 bool lIsString = lval.isString();
1029 bool rIsString = rval.isString();
1030 JSString *lstr, *rstr;
1031 if (lIsString && rIsString) {
1032 lstr = lval.toString();
1033 rstr = rval.toString();
1034 goto string_concat;
1036 } else
1037 #if JS_HAS_XML_SUPPORT
1038 if (lval.isObject() && lval.toObject().isXML() &&
1039 rval.isObject() && rval.toObject().isXML()) {
1040 if (!js_ConcatenateXML(cx, &lval.toObject(), &rval.toObject(), &rval))
1041 THROW();
1042 regs.sp--;
1043 regs.sp[-1] = rval;
1044 } else
1045 #endif
1047 /* These can convert lval/rval to strings. */
1048 if (lval.isObject() && !DefaultValue(f, JSTYPE_VOID, lval, -2))
1049 THROW();
1050 if (rval.isObject() && !DefaultValue(f, JSTYPE_VOID, rval, -1))
1051 THROW();
1052 if ((lIsString = lval.isString()) || (rIsString = rval.isString())) {
1053 if (lIsString) {
1054 lstr = lval.toString();
1055 } else {
1056 lstr = js_ValueToString(cx, lval);
1057 if (!lstr)
1058 THROW();
1059 regs.sp[-2].setString(lstr);
1061 if (rIsString) {
1062 rstr = rval.toString();
1063 } else {
1064 rstr = js_ValueToString(cx, rval);
1065 if (!rstr)
1066 THROW();
1067 regs.sp[-1].setString(rstr);
1069 goto string_concat;
1071 } else {
1072 double l, r;
1073 if (!ValueToNumber(cx, lval, &l) || !ValueToNumber(cx, rval, &r))
1074 THROW();
1075 l += r;
1076 regs.sp--;
1077 regs.sp[-1].setNumber(l);
1080 return;
1082 string_concat:
1083 JSString *str = js_ConcatStrings(cx, lstr, rstr);
1084 if (!str)
1085 THROW();
1086 regs.sp--;
1087 regs.sp[-1].setString(str);
1091 void JS_FASTCALL
1092 stubs::Sub(VMFrame &f)
1094 JSContext *cx = f.cx;
1095 JSFrameRegs &regs = f.regs;
1096 double d1, d2;
1097 if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
1098 !ValueToNumber(cx, regs.sp[-1], &d2)) {
1099 THROW();
1101 double d = d1 - d2;
1102 regs.sp[-2].setNumber(d);
1105 void JS_FASTCALL
1106 stubs::Mul(VMFrame &f)
1108 JSContext *cx = f.cx;
1109 JSFrameRegs &regs = f.regs;
1110 double d1, d2;
1111 if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
1112 !ValueToNumber(cx, regs.sp[-1], &d2)) {
1113 THROW();
1115 double d = d1 * d2;
1116 regs.sp[-2].setNumber(d);
1119 void JS_FASTCALL
1120 stubs::Div(VMFrame &f)
1122 JSContext *cx = f.cx;
1123 JSRuntime *rt = cx->runtime;
1124 JSFrameRegs &regs = f.regs;
1126 double d1, d2;
1127 if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
1128 !ValueToNumber(cx, regs.sp[-1], &d2)) {
1129 THROW();
1131 if (d2 == 0) {
1132 const Value *vp;
1133 #ifdef XP_WIN
1134 /* XXX MSVC miscompiles such that (NaN == 0) */
1135 if (JSDOUBLE_IS_NaN(d2))
1136 vp = &rt->NaNValue;
1137 else
1138 #endif
1139 if (d1 == 0 || JSDOUBLE_IS_NaN(d1))
1140 vp = &rt->NaNValue;
1141 else if (JSDOUBLE_IS_NEG(d1) != JSDOUBLE_IS_NEG(d2))
1142 vp = &rt->negativeInfinityValue;
1143 else
1144 vp = &rt->positiveInfinityValue;
1145 regs.sp[-2] = *vp;
1146 } else {
1147 d1 /= d2;
1148 regs.sp[-2].setNumber(d1);
1152 void JS_FASTCALL
1153 stubs::Mod(VMFrame &f)
1155 JSContext *cx = f.cx;
1156 JSFrameRegs &regs = f.regs;
1158 Value &lref = regs.sp[-2];
1159 Value &rref = regs.sp[-1];
1160 int32_t l, r;
1161 if (lref.isInt32() && rref.isInt32() &&
1162 (l = lref.toInt32()) >= 0 && (r = rref.toInt32()) > 0) {
1163 int32_t mod = l % r;
1164 regs.sp[-2].setInt32(mod);
1165 } else {
1166 double d1, d2;
1167 if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
1168 !ValueToNumber(cx, regs.sp[-1], &d2)) {
1169 THROW();
1171 if (d2 == 0) {
1172 regs.sp[-2].setDouble(js_NaN);
1173 } else {
1174 d1 = js_fmod(d1, d2);
1175 regs.sp[-2].setDouble(d1);
1180 void JS_FASTCALL
1181 stubs::Debugger(VMFrame &f, jsbytecode *pc)
1183 JSDebuggerHandler handler = f.cx->debugHooks->debuggerHandler;
1184 if (handler) {
1185 Value rval;
1186 switch (handler(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval),
1187 f.cx->debugHooks->debuggerHandlerData)) {
1188 case JSTRAP_THROW:
1189 f.cx->setPendingException(rval);
1190 THROW();
1192 case JSTRAP_RETURN:
1193 f.cx->clearPendingException();
1194 f.cx->fp()->setReturnValue(rval);
1195 #if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64)
1196 *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *,
1197 f.cx->jaegerCompartment()->forceReturnFastTrampoline());
1198 #else
1199 *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *,
1200 f.cx->jaegerCompartment()->forceReturnTrampoline());
1201 #endif
1202 break;
1204 case JSTRAP_ERROR:
1205 f.cx->clearPendingException();
1206 THROW();
1208 default:
1209 break;
1214 void JS_FASTCALL
1215 stubs::Interrupt(VMFrame &f, jsbytecode *pc)
1217 if (!js_HandleExecutionInterrupt(f.cx))
1218 THROW();
1221 void JS_FASTCALL
1222 stubs::Trap(VMFrame &f, uint32 trapTypes)
1224 Value rval;
1225 jsbytecode *pc = f.cx->regs->pc;
1228 * Trap may be called for a single-step interrupt trap and/or a
1229 * regular trap. Try the single-step first, and if it lets control
1230 * flow through or does not exist, do the regular trap.
1232 JSTrapStatus result = JSTRAP_CONTINUE;
1233 if (trapTypes & JSTRAP_SINGLESTEP) {
1235 * single step mode may be paused without recompiling by
1236 * setting the interruptHook to NULL.
1238 JSInterruptHook hook = f.cx->debugHooks->interruptHook;
1239 if (hook)
1240 result = hook(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval),
1241 f.cx->debugHooks->interruptHookData);
1244 if (result == JSTRAP_CONTINUE && (trapTypes & JSTRAP_TRAP))
1245 result = JS_HandleTrap(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval));
1247 switch (result) {
1248 case JSTRAP_THROW:
1249 f.cx->setPendingException(rval);
1250 THROW();
1252 case JSTRAP_RETURN:
1253 f.cx->clearPendingException();
1254 f.cx->fp()->setReturnValue(rval);
1255 #if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64)
1256 *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *,
1257 f.cx->jaegerCompartment()->forceReturnFastTrampoline());
1258 #else
1259 *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *,
1260 f.cx->jaegerCompartment()->forceReturnTrampoline());
1261 #endif
1262 break;
1264 case JSTRAP_ERROR:
1265 f.cx->clearPendingException();
1266 THROW();
1268 default:
1269 break;
1273 void JS_FASTCALL
1274 stubs::This(VMFrame &f)
1276 if (!f.fp()->computeThis(f.cx))
1277 THROW();
1278 f.regs.sp[-1] = f.fp()->thisValue();
1281 void JS_FASTCALL
1282 stubs::Neg(VMFrame &f)
1284 double d;
1285 if (!ValueToNumber(f.cx, f.regs.sp[-1], &d))
1286 THROW();
1287 d = -d;
1288 f.regs.sp[-1].setNumber(d);
1291 JSObject * JS_FASTCALL
1292 stubs::NewInitArray(VMFrame &f, uint32 count)
1294 JSObject *obj = NewDenseAllocatedArray(f.cx, count);
1295 if (!obj)
1296 THROWV(NULL);
1298 return obj;
1301 JSObject * JS_FASTCALL
1302 stubs::NewInitObject(VMFrame &f, JSObject *baseobj)
1304 JSContext *cx = f.cx;
1306 if (!baseobj) {
1307 gc::FinalizeKind kind = GuessObjectGCKind(0, false);
1308 JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
1309 if (!obj)
1310 THROWV(NULL);
1311 return obj;
1314 JSObject *obj = CopyInitializerObject(cx, baseobj);
1316 if (!obj)
1317 THROWV(NULL);
1318 return obj;
1321 void JS_FASTCALL
1322 stubs::InitElem(VMFrame &f, uint32 last)
1324 JSContext *cx = f.cx;
1325 JSFrameRegs &regs = f.regs;
1327 /* Pop the element's value into rval. */
1328 JS_ASSERT(regs.sp - f.fp()->base() >= 3);
1329 const Value &rref = regs.sp[-1];
1331 /* Find the object being initialized at top of stack. */
1332 const Value &lref = regs.sp[-3];
1333 JS_ASSERT(lref.isObject());
1334 JSObject *obj = &lref.toObject();
1336 /* Fetch id now that we have obj. */
1337 jsid id;
1338 const Value &idval = regs.sp[-2];
1339 if (!FetchElementId(f, obj, idval, id, &regs.sp[-2]))
1340 THROW();
1343 * If rref is a hole, do not call JSObject::defineProperty. In this case,
1344 * obj must be an array, so if the current op is the last element
1345 * initialiser, set the array length to one greater than id.
1347 if (rref.isMagic(JS_ARRAY_HOLE)) {
1348 JS_ASSERT(obj->isArray());
1349 JS_ASSERT(JSID_IS_INT(id));
1350 JS_ASSERT(jsuint(JSID_TO_INT(id)) < JS_ARGS_LENGTH_MAX);
1351 if (last && !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1)))
1352 THROW();
1353 } else {
1354 if (!obj->defineProperty(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE))
1355 THROW();
1359 void JS_FASTCALL
1360 stubs::GetUpvar(VMFrame &f, uint32 ck)
1362 /* :FIXME: We can do better, this stub isn't needed. */
1363 uint32 staticLevel = f.fp()->script()->staticLevel;
1364 UpvarCookie cookie;
1365 cookie.fromInteger(ck);
1366 f.regs.sp[0] = GetUpvar(f.cx, staticLevel, cookie);
1369 JSObject * JS_FASTCALL
1370 stubs::DefLocalFun(VMFrame &f, JSFunction *fun)
1373 * Define a local function (i.e., one nested at the top level of another
1374 * function), parented by the current scope chain, stored in a local
1375 * variable slot that the compiler allocated. This is an optimization over
1376 * JSOP_DEFFUN that avoids requiring a call object for the outer function's
1377 * activation.
1379 JS_ASSERT(fun->isInterpreted());
1380 JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
1381 JSObject *obj = FUN_OBJECT(fun);
1383 if (FUN_NULL_CLOSURE(fun)) {
1384 obj = CloneFunctionObject(f.cx, fun, &f.fp()->scopeChain());
1385 if (!obj)
1386 THROWV(NULL);
1387 } else {
1388 JSObject *parent = GetScopeChainFast(f.cx, f.fp(), JSOP_DEFLOCALFUN,
1389 JSOP_DEFLOCALFUN_LENGTH);
1390 if (!parent)
1391 THROWV(NULL);
1393 if (obj->getParent() != parent) {
1394 obj = CloneFunctionObject(f.cx, fun, parent);
1395 if (!obj)
1396 THROWV(NULL);
1400 return obj;
1403 JSObject * JS_FASTCALL
1404 stubs::DefLocalFun_FC(VMFrame &f, JSFunction *fun)
1406 JSObject *obj = js_NewFlatClosure(f.cx, fun, JSOP_DEFLOCALFUN_FC, JSOP_DEFLOCALFUN_FC_LENGTH);
1407 if (!obj)
1408 THROWV(NULL);
1409 return obj;
1412 JSObject * JS_FASTCALL
1413 stubs::RegExp(VMFrame &f, JSObject *regex)
1416 * Push a regexp object cloned from the regexp literal object mapped by the
1417 * bytecode at pc. ES5 finally fixed this bad old ES3 design flaw which was
1418 * flouted by many browser-based implementations.
1420 * We avoid the GetScopeChain call here and pass fp->scopeChain() as
1421 * js_GetClassPrototype uses the latter only to locate the global.
1423 JSObject *proto;
1424 if (!js_GetClassPrototype(f.cx, &f.fp()->scopeChain(), JSProto_RegExp, &proto))
1425 THROWV(NULL);
1426 JS_ASSERT(proto);
1427 JSObject *obj = js_CloneRegExpObject(f.cx, regex, proto);
1428 if (!obj)
1429 THROWV(NULL);
1430 return obj;
1433 JSObject * JS_FASTCALL
1434 stubs::LambdaForInit(VMFrame &f, JSFunction *fun)
1436 JSObject *obj = FUN_OBJECT(fun);
1437 if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) {
1438 fun->setMethodAtom(f.fp()->script()->getAtom(GET_SLOTNO(f.regs.pc)));
1439 return obj;
1441 return Lambda(f, fun);
1444 JSObject * JS_FASTCALL
1445 stubs::LambdaForSet(VMFrame &f, JSFunction *fun)
1447 JSObject *obj = FUN_OBJECT(fun);
1448 if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) {
1449 const Value &lref = f.regs.sp[-1];
1450 if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) {
1451 fun->setMethodAtom(f.fp()->script()->getAtom(GET_SLOTNO(f.regs.pc)));
1452 return obj;
1455 return Lambda(f, fun);
1458 JSObject * JS_FASTCALL
1459 stubs::LambdaJoinableForCall(VMFrame &f, JSFunction *fun)
1461 JSObject *obj = FUN_OBJECT(fun);
1462 if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) {
1464 * Array.prototype.sort and String.prototype.replace are
1465 * optimized as if they are special form. We know that they
1466 * won't leak the joined function object in obj, therefore
1467 * we don't need to clone that compiler- created function
1468 * object for identity/mutation reasons.
1470 int iargc = GET_ARGC(f.regs.pc);
1473 * Note that we have not yet pushed obj as the final argument,
1474 * so regs.sp[1 - (iargc + 2)], and not regs.sp[-(iargc + 2)],
1475 * is the callee for this JSOP_CALL.
1477 const Value &cref = f.regs.sp[1 - (iargc + 2)];
1478 JSObject *callee;
1480 if (IsFunctionObject(cref, &callee)) {
1481 JSFunction *calleeFun = callee->getFunctionPrivate();
1482 Native native = calleeFun->maybeNative();
1484 if (native) {
1485 if (iargc == 1 && native == array_sort)
1486 return obj;
1487 if (iargc == 2 && native == str_replace)
1488 return obj;
1492 return Lambda(f, fun);
1495 JSObject * JS_FASTCALL
1496 stubs::LambdaJoinableForNull(VMFrame &f, JSFunction *fun)
1498 JSObject *obj = FUN_OBJECT(fun);
1499 if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) {
1500 jsbytecode *pc2 = f.regs.pc + JSOP_NULL_LENGTH;
1501 JSOp op2 = JSOp(*pc2);
1503 if (op2 == JSOP_CALL && GET_ARGC(pc2) == 0)
1504 return obj;
1506 return Lambda(f, fun);
1509 JSObject * JS_FASTCALL
1510 stubs::Lambda(VMFrame &f, JSFunction *fun)
1512 JSObject *obj = FUN_OBJECT(fun);
1514 JSObject *parent;
1515 if (FUN_NULL_CLOSURE(fun)) {
1516 parent = &f.fp()->scopeChain();
1517 } else {
1518 parent = GetScopeChainFast(f.cx, f.fp(), JSOP_LAMBDA, JSOP_LAMBDA_LENGTH);
1519 if (!parent)
1520 THROWV(NULL);
1523 obj = CloneFunctionObject(f.cx, fun, parent);
1524 if (!obj)
1525 THROWV(NULL);
1527 return obj;
1530 /* Test whether v is an int in the range [-2^31 + 1, 2^31 - 2] */
1531 static JS_ALWAYS_INLINE bool
1532 CanIncDecWithoutOverflow(int32_t i)
1534 return (i > JSVAL_INT_MIN) && (i < JSVAL_INT_MAX);
1537 template <int32 N, bool POST, JSBool strict>
1538 static inline bool
1539 ObjIncOp(VMFrame &f, JSObject *obj, jsid id)
1541 JSContext *cx = f.cx;
1542 JSStackFrame *fp = f.fp();
1544 f.regs.sp[0].setNull();
1545 f.regs.sp++;
1546 if (!obj->getProperty(cx, id, &f.regs.sp[-1]))
1547 return false;
1549 Value &ref = f.regs.sp[-1];
1550 int32_t tmp;
1551 if (JS_LIKELY(ref.isInt32() && CanIncDecWithoutOverflow(tmp = ref.toInt32()))) {
1552 if (POST)
1553 ref.getInt32Ref() = tmp + N;
1554 else
1555 ref.getInt32Ref() = tmp += N;
1556 fp->setAssigning();
1557 JSBool ok = obj->setProperty(cx, id, &ref, strict);
1558 fp->clearAssigning();
1559 if (!ok)
1560 return false;
1563 * We must set regs.sp[-1] to tmp for both post and pre increments
1564 * as the setter overwrites regs.sp[-1].
1566 ref.setInt32(tmp);
1567 } else {
1568 Value v;
1569 double d;
1570 if (!ValueToNumber(cx, ref, &d))
1571 return false;
1572 if (POST) {
1573 ref.setDouble(d);
1574 d += N;
1575 } else {
1576 d += N;
1577 ref.setDouble(d);
1579 v.setDouble(d);
1580 fp->setAssigning();
1581 JSBool ok = obj->setProperty(cx, id, &v, strict);
1582 fp->clearAssigning();
1583 if (!ok)
1584 return false;
1587 return true;
1590 template <int32 N, bool POST, JSBool strict>
1591 static inline bool
1592 NameIncDec(VMFrame &f, JSObject *obj, JSAtom *origAtom)
1594 JSContext *cx = f.cx;
1596 JSAtom *atom;
1597 JSObject *obj2;
1598 JSProperty *prop;
1599 PropertyCacheEntry *entry;
1600 JS_PROPERTY_CACHE(cx).test(cx, f.regs.pc, obj, obj2, entry, atom);
1601 if (!atom) {
1602 if (obj == obj2 && entry->vword.isSlot()) {
1603 uint32 slot = entry->vword.toSlot();
1604 Value &rref = obj->nativeGetSlotRef(slot);
1605 int32_t tmp;
1606 if (JS_LIKELY(rref.isInt32() && CanIncDecWithoutOverflow(tmp = rref.toInt32()))) {
1607 int32_t inc = tmp + N;
1608 if (!POST)
1609 tmp = inc;
1610 rref.getInt32Ref() = inc;
1611 f.regs.sp[0].setInt32(tmp);
1612 return true;
1615 atom = origAtom;
1618 jsid id = ATOM_TO_JSID(atom);
1619 if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
1620 return false;
1621 if (!prop) {
1622 ReportAtomNotDefined(cx, atom);
1623 return false;
1625 return ObjIncOp<N, POST, strict>(f, obj, id);
1628 template<JSBool strict>
1629 void JS_FASTCALL
1630 stubs::PropInc(VMFrame &f, JSAtom *atom)
1632 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
1633 if (!obj)
1634 THROW();
1635 if (!ObjIncOp<1, true, strict>(f, obj, ATOM_TO_JSID(atom)))
1636 THROW();
1637 f.regs.sp[-2] = f.regs.sp[-1];
1640 template void JS_FASTCALL stubs::PropInc<true>(VMFrame &f, JSAtom *atom);
1641 template void JS_FASTCALL stubs::PropInc<false>(VMFrame &f, JSAtom *atom);
1643 template<JSBool strict>
1644 void JS_FASTCALL
1645 stubs::PropDec(VMFrame &f, JSAtom *atom)
1647 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
1648 if (!obj)
1649 THROW();
1650 if (!ObjIncOp<-1, true, strict>(f, obj, ATOM_TO_JSID(atom)))
1651 THROW();
1652 f.regs.sp[-2] = f.regs.sp[-1];
1655 template void JS_FASTCALL stubs::PropDec<true>(VMFrame &f, JSAtom *atom);
1656 template void JS_FASTCALL stubs::PropDec<false>(VMFrame &f, JSAtom *atom);
1658 template<JSBool strict>
1659 void JS_FASTCALL
1660 stubs::IncProp(VMFrame &f, JSAtom *atom)
1662 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
1663 if (!obj)
1664 THROW();
1665 if (!ObjIncOp<1, false, strict>(f, obj, ATOM_TO_JSID(atom)))
1666 THROW();
1667 f.regs.sp[-2] = f.regs.sp[-1];
1670 template void JS_FASTCALL stubs::IncProp<true>(VMFrame &f, JSAtom *atom);
1671 template void JS_FASTCALL stubs::IncProp<false>(VMFrame &f, JSAtom *atom);
1673 template<JSBool strict>
1674 void JS_FASTCALL
1675 stubs::DecProp(VMFrame &f, JSAtom *atom)
1677 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
1678 if (!obj)
1679 THROW();
1680 if (!ObjIncOp<-1, false, strict>(f, obj, ATOM_TO_JSID(atom)))
1681 THROW();
1682 f.regs.sp[-2] = f.regs.sp[-1];
1685 template void JS_FASTCALL stubs::DecProp<true>(VMFrame &f, JSAtom *atom);
1686 template void JS_FASTCALL stubs::DecProp<false>(VMFrame &f, JSAtom *atom);
1688 template<JSBool strict>
1689 void JS_FASTCALL
1690 stubs::ElemInc(VMFrame &f)
1692 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
1693 if (!obj)
1694 THROW();
1695 jsid id;
1696 if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
1697 THROW();
1698 if (!ObjIncOp<1, true, strict>(f, obj, id))
1699 THROW();
1700 f.regs.sp[-3] = f.regs.sp[-1];
1703 template void JS_FASTCALL stubs::ElemInc<true>(VMFrame &f);
1704 template void JS_FASTCALL stubs::ElemInc<false>(VMFrame &f);
1706 template<JSBool strict>
1707 void JS_FASTCALL
1708 stubs::ElemDec(VMFrame &f)
1710 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
1711 if (!obj)
1712 THROW();
1713 jsid id;
1714 if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
1715 THROW();
1716 if (!ObjIncOp<-1, true, strict>(f, obj, id))
1717 THROW();
1718 f.regs.sp[-3] = f.regs.sp[-1];
1721 template void JS_FASTCALL stubs::ElemDec<true>(VMFrame &f);
1722 template void JS_FASTCALL stubs::ElemDec<false>(VMFrame &f);
1724 template<JSBool strict>
1725 void JS_FASTCALL
1726 stubs::IncElem(VMFrame &f)
1728 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
1729 if (!obj)
1730 THROW();
1731 jsid id;
1732 if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
1733 THROW();
1734 if (!ObjIncOp<1, false, strict>(f, obj, id))
1735 THROW();
1736 f.regs.sp[-3] = f.regs.sp[-1];
1739 template void JS_FASTCALL stubs::IncElem<true>(VMFrame &f);
1740 template void JS_FASTCALL stubs::IncElem<false>(VMFrame &f);
1742 template<JSBool strict>
1743 void JS_FASTCALL
1744 stubs::DecElem(VMFrame &f)
1746 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
1747 if (!obj)
1748 THROW();
1749 jsid id;
1750 if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
1751 THROW();
1752 if (!ObjIncOp<-1, false, strict>(f, obj, id))
1753 THROW();
1754 f.regs.sp[-3] = f.regs.sp[-1];
1757 template void JS_FASTCALL stubs::DecElem<true>(VMFrame &f);
1758 template void JS_FASTCALL stubs::DecElem<false>(VMFrame &f);
1760 template<JSBool strict>
1761 void JS_FASTCALL
1762 stubs::NameInc(VMFrame &f, JSAtom *atom)
1764 JSObject *obj = &f.fp()->scopeChain();
1765 if (!NameIncDec<1, true, strict>(f, obj, atom))
1766 THROW();
1769 template void JS_FASTCALL stubs::NameInc<true>(VMFrame &f, JSAtom *atom);
1770 template void JS_FASTCALL stubs::NameInc<false>(VMFrame &f, JSAtom *atom);
1772 template<JSBool strict>
1773 void JS_FASTCALL
1774 stubs::NameDec(VMFrame &f, JSAtom *atom)
1776 JSObject *obj = &f.fp()->scopeChain();
1777 if (!NameIncDec<-1, true, strict>(f, obj, atom))
1778 THROW();
1781 template void JS_FASTCALL stubs::NameDec<true>(VMFrame &f, JSAtom *atom);
1782 template void JS_FASTCALL stubs::NameDec<false>(VMFrame &f, JSAtom *atom);
1784 template<JSBool strict>
1785 void JS_FASTCALL
1786 stubs::IncName(VMFrame &f, JSAtom *atom)
1788 JSObject *obj = &f.fp()->scopeChain();
1789 if (!NameIncDec<1, false, strict>(f, obj, atom))
1790 THROW();
1793 template void JS_FASTCALL stubs::IncName<true>(VMFrame &f, JSAtom *atom);
1794 template void JS_FASTCALL stubs::IncName<false>(VMFrame &f, JSAtom *atom);
1796 template<JSBool strict>
1797 void JS_FASTCALL
1798 stubs::DecName(VMFrame &f, JSAtom *atom)
1800 JSObject *obj = &f.fp()->scopeChain();
1801 if (!NameIncDec<-1, false, strict>(f, obj, atom))
1802 THROW();
1805 template void JS_FASTCALL stubs::DecName<true>(VMFrame &f, JSAtom *atom);
1806 template void JS_FASTCALL stubs::DecName<false>(VMFrame &f, JSAtom *atom);
1808 template<JSBool strict>
1809 void JS_FASTCALL
1810 stubs::GlobalNameInc(VMFrame &f, JSAtom *atom)
1812 JSObject *obj = f.fp()->scopeChain().getGlobal();
1813 if (!NameIncDec<1, true, strict>(f, obj, atom))
1814 THROW();
1817 template void JS_FASTCALL stubs::GlobalNameInc<true>(VMFrame &f, JSAtom *atom);
1818 template void JS_FASTCALL stubs::GlobalNameInc<false>(VMFrame &f, JSAtom *atom);
1820 template<JSBool strict>
1821 void JS_FASTCALL
1822 stubs::GlobalNameDec(VMFrame &f, JSAtom *atom)
1824 JSObject *obj = f.fp()->scopeChain().getGlobal();
1825 if (!NameIncDec<-1, true, strict>(f, obj, atom))
1826 THROW();
1829 template void JS_FASTCALL stubs::GlobalNameDec<true>(VMFrame &f, JSAtom *atom);
1830 template void JS_FASTCALL stubs::GlobalNameDec<false>(VMFrame &f, JSAtom *atom);
1832 template<JSBool strict>
1833 void JS_FASTCALL
1834 stubs::IncGlobalName(VMFrame &f, JSAtom *atom)
1836 JSObject *obj = f.fp()->scopeChain().getGlobal();
1837 if (!NameIncDec<1, false, strict>(f, obj, atom))
1838 THROW();
1841 template void JS_FASTCALL stubs::IncGlobalName<true>(VMFrame &f, JSAtom *atom);
1842 template void JS_FASTCALL stubs::IncGlobalName<false>(VMFrame &f, JSAtom *atom);
1844 template<JSBool strict>
1845 void JS_FASTCALL
1846 stubs::DecGlobalName(VMFrame &f, JSAtom *atom)
1848 JSObject *obj = f.fp()->scopeChain().getGlobal();
1849 if (!NameIncDec<-1, false, strict>(f, obj, atom))
1850 THROW();
1853 template void JS_FASTCALL stubs::DecGlobalName<true>(VMFrame &f, JSAtom *atom);
1854 template void JS_FASTCALL stubs::DecGlobalName<false>(VMFrame &f, JSAtom *atom);
1856 static bool JS_FASTCALL
1857 InlineGetProp(VMFrame &f)
1859 JSContext *cx = f.cx;
1860 JSFrameRegs &regs = f.regs;
1862 Value *vp = &f.regs.sp[-1];
1863 JSObject *obj = ValueToObject(f.cx, vp);
1864 if (!obj)
1865 return false;
1867 Value rval;
1868 do {
1870 * We do not impose the method read barrier if in an imacro,
1871 * assuming any property gets it does (e.g., for 'toString'
1872 * from JSOP_NEW) will not be leaked to the calling script.
1874 JSObject *aobj = js_GetProtoIfDenseArray(obj);
1876 PropertyCacheEntry *entry;
1877 JSObject *obj2;
1878 JSAtom *atom;
1879 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
1880 if (!atom) {
1881 if (entry->vword.isFunObj()) {
1882 rval.setObject(entry->vword.toFunObj());
1883 } else if (entry->vword.isSlot()) {
1884 uint32 slot = entry->vword.toSlot();
1885 rval = obj2->nativeGetSlot(slot);
1886 } else {
1887 JS_ASSERT(entry->vword.isShape());
1888 const Shape *shape = entry->vword.toShape();
1889 NATIVE_GET(cx, obj, obj2, shape,
1890 f.fp()->hasImacropc() ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER,
1891 &rval, return false);
1893 break;
1896 jsid id = ATOM_TO_JSID(atom);
1897 if (JS_LIKELY(!aobj->getOps()->getProperty)
1898 ? !js_GetPropertyHelper(cx, obj, id,
1899 f.fp()->hasImacropc()
1900 ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
1901 : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER,
1902 &rval)
1903 : !obj->getProperty(cx, id, &rval)) {
1904 return false;
1906 } while(0);
1908 regs.sp[-1] = rval;
1909 return true;
1912 void JS_FASTCALL
1913 stubs::GetProp(VMFrame &f)
1915 if (!InlineGetProp(f))
1916 THROW();
1919 void JS_FASTCALL
1920 stubs::GetPropNoCache(VMFrame &f, JSAtom *atom)
1922 JSContext *cx = f.cx;
1924 Value *vp = &f.regs.sp[-1];
1925 JSObject *obj = ValueToObject(cx, vp);
1926 if (!obj)
1927 THROW();
1929 if (!obj->getProperty(cx, ATOM_TO_JSID(atom), vp))
1930 THROW();
1933 void JS_FASTCALL
1934 stubs::CallProp(VMFrame &f, JSAtom *origAtom)
1936 JSContext *cx = f.cx;
1937 JSFrameRegs &regs = f.regs;
1939 Value lval;
1940 lval = regs.sp[-1];
1942 Value objv;
1943 if (lval.isObject()) {
1944 objv = lval;
1945 } else {
1946 JSProtoKey protoKey;
1947 if (lval.isString()) {
1948 protoKey = JSProto_String;
1949 } else if (lval.isNumber()) {
1950 protoKey = JSProto_Number;
1951 } else if (lval.isBoolean()) {
1952 protoKey = JSProto_Boolean;
1953 } else {
1954 JS_ASSERT(lval.isNull() || lval.isUndefined());
1955 js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
1956 THROW();
1958 JSObject *pobj;
1959 if (!js_GetClassPrototype(cx, NULL, protoKey, &pobj))
1960 THROW();
1961 objv.setObject(*pobj);
1964 JSObject *aobj = js_GetProtoIfDenseArray(&objv.toObject());
1965 Value rval;
1967 PropertyCacheEntry *entry;
1968 JSObject *obj2;
1969 JSAtom *atom;
1970 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
1971 if (!atom) {
1972 if (entry->vword.isFunObj()) {
1973 rval.setObject(entry->vword.toFunObj());
1974 } else if (entry->vword.isSlot()) {
1975 uint32 slot = entry->vword.toSlot();
1976 rval = obj2->nativeGetSlot(slot);
1977 } else {
1978 JS_ASSERT(entry->vword.isShape());
1979 const Shape *shape = entry->vword.toShape();
1980 NATIVE_GET(cx, &objv.toObject(), obj2, shape, JSGET_NO_METHOD_BARRIER, &rval,
1981 THROW());
1983 regs.sp++;
1984 regs.sp[-2] = rval;
1985 regs.sp[-1] = lval;
1986 } else {
1988 * Cache miss: use the immediate atom that was loaded for us under
1989 * PropertyCache::test.
1991 jsid id;
1992 id = ATOM_TO_JSID(origAtom);
1994 regs.sp++;
1995 regs.sp[-1].setNull();
1996 if (lval.isObject()) {
1997 if (!js_GetMethod(cx, &objv.toObject(), id,
1998 JS_LIKELY(!aobj->getOps()->getProperty)
1999 ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
2000 : JSGET_NO_METHOD_BARRIER,
2001 &rval)) {
2002 THROW();
2004 regs.sp[-1] = objv;
2005 regs.sp[-2] = rval;
2006 } else {
2007 JS_ASSERT(!objv.toObject().getOps()->getProperty);
2008 if (!js_GetPropertyHelper(cx, &objv.toObject(), id,
2009 JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER,
2010 &rval)) {
2011 THROW();
2013 regs.sp[-1] = lval;
2014 regs.sp[-2] = rval;
2017 #if JS_HAS_NO_SUCH_METHOD
2018 if (JS_UNLIKELY(rval.isUndefined()) && regs.sp[-1].isObject()) {
2019 regs.sp[-2].setString(ATOM_TO_STRING(origAtom));
2020 if (!js_OnUnknownMethod(cx, regs.sp - 2))
2021 THROW();
2023 #endif
2026 void JS_FASTCALL
2027 stubs::Length(VMFrame &f)
2029 JSFrameRegs &regs = f.regs;
2030 Value *vp = &regs.sp[-1];
2032 if (vp->isString()) {
2033 vp->setInt32(vp->toString()->length());
2034 return;
2035 } else if (vp->isObject()) {
2036 JSObject *obj = &vp->toObject();
2037 if (obj->isArray()) {
2038 jsuint length = obj->getArrayLength();
2039 regs.sp[-1].setNumber(length);
2040 return;
2041 } else if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
2042 uint32 length = obj->getArgsInitialLength();
2043 JS_ASSERT(length < INT32_MAX);
2044 regs.sp[-1].setInt32(int32_t(length));
2045 return;
2049 if (!InlineGetProp(f))
2050 THROW();
2053 void JS_FASTCALL
2054 stubs::Iter(VMFrame &f, uint32 flags)
2056 if (!js_ValueToIterator(f.cx, flags, &f.regs.sp[-1]))
2057 THROW();
2058 JS_ASSERT(!f.regs.sp[-1].isPrimitive());
2061 static void
2062 InitPropOrMethod(VMFrame &f, JSAtom *atom, JSOp op)
2064 JSContext *cx = f.cx;
2065 JSRuntime *rt = cx->runtime;
2066 JSFrameRegs &regs = f.regs;
2068 /* Load the property's initial value into rval. */
2069 JS_ASSERT(regs.sp - f.fp()->base() >= 2);
2070 Value rval;
2071 rval = regs.sp[-1];
2073 /* Load the object being initialized into lval/obj. */
2074 JSObject *obj = &regs.sp[-2].toObject();
2075 JS_ASSERT(obj->isNative());
2078 * Probe the property cache.
2080 * We can not assume that the object created by JSOP_NEWINIT is still
2081 * single-threaded as the debugger can access it from other threads.
2082 * So check first.
2084 * On a hit, if the cached shape has a non-default setter, it must be
2085 * __proto__. If shape->previous() != obj->lastProperty(), there must be a
2086 * repeated property name. The fast path does not handle these two cases.
2088 PropertyCacheEntry *entry;
2089 const Shape *shape;
2090 if (JS_PROPERTY_CACHE(cx).testForInit(rt, regs.pc, obj, &shape, &entry) &&
2091 shape->hasDefaultSetter() &&
2092 shape->previous() == obj->lastProperty())
2094 /* Fast path. Property cache hit. */
2095 uint32 slot = shape->slot;
2097 JS_ASSERT(slot == obj->slotSpan());
2098 JS_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
2099 if (slot < obj->numSlots()) {
2100 JS_ASSERT(obj->getSlot(slot).isUndefined());
2101 } else {
2102 if (!obj->allocSlot(cx, &slot))
2103 THROW();
2104 JS_ASSERT(slot == shape->slot);
2107 /* A new object, or one we just extended in a recent initprop op. */
2108 JS_ASSERT(!obj->lastProperty() ||
2109 obj->shape() == obj->lastProperty()->shape);
2110 obj->extend(cx, shape);
2113 * No method change check here because here we are adding a new
2114 * property, not updating an existing slot's value that might
2115 * contain a method of a branded shape.
2117 obj->nativeSetSlot(slot, rval);
2118 } else {
2119 PCMETER(JS_PROPERTY_CACHE(cx).inipcmisses++);
2121 /* Get the immediate property name into id. */
2122 jsid id = ATOM_TO_JSID(atom);
2124 uintN defineHow = (op == JSOP_INITMETHOD)
2125 ? JSDNP_CACHE_RESULT | JSDNP_SET_METHOD
2126 : JSDNP_CACHE_RESULT;
2127 if (!(JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom)
2128 ? js_SetPropertyHelper(cx, obj, id, defineHow, &rval, false)
2129 : js_DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
2130 JSPROP_ENUMERATE, 0, 0, NULL,
2131 defineHow))) {
2132 THROW();
2137 void JS_FASTCALL
2138 stubs::InitProp(VMFrame &f, JSAtom *atom)
2140 InitPropOrMethod(f, atom, JSOP_INITPROP);
2143 void JS_FASTCALL
2144 stubs::InitMethod(VMFrame &f, JSAtom *atom)
2146 InitPropOrMethod(f, atom, JSOP_INITMETHOD);
2149 void JS_FASTCALL
2150 stubs::IterNext(VMFrame &f)
2152 JS_ASSERT(f.regs.sp - 1 >= f.fp()->base());
2153 JS_ASSERT(f.regs.sp[-1].isObject());
2155 JSObject *iterobj = &f.regs.sp[-1].toObject();
2156 f.regs.sp[0].setNull();
2157 f.regs.sp++;
2158 if (!js_IteratorNext(f.cx, iterobj, &f.regs.sp[-1]))
2159 THROW();
2162 JSBool JS_FASTCALL
2163 stubs::IterMore(VMFrame &f)
2165 JS_ASSERT(f.regs.sp - 1 >= f.fp()->base());
2166 JS_ASSERT(f.regs.sp[-1].isObject());
2168 Value v;
2169 JSObject *iterobj = &f.regs.sp[-1].toObject();
2170 if (!js_IteratorMore(f.cx, iterobj, &v))
2171 THROWV(JS_FALSE);
2173 return v.toBoolean();
2176 void JS_FASTCALL
2177 stubs::EndIter(VMFrame &f)
2179 JS_ASSERT(f.regs.sp - 1 >= f.fp()->base());
2180 if (!js_CloseIterator(f.cx, &f.regs.sp[-1].toObject()))
2181 THROW();
2184 JSString * JS_FASTCALL
2185 stubs::TypeOf(VMFrame &f)
2187 const Value &ref = f.regs.sp[-1];
2188 JSType type = JS_TypeOfValue(f.cx, Jsvalify(ref));
2189 JSAtom *atom = f.cx->runtime->atomState.typeAtoms[type];
2190 return ATOM_TO_STRING(atom);
2193 void JS_FASTCALL
2194 stubs::StrictEq(VMFrame &f)
2196 const Value &rhs = f.regs.sp[-1];
2197 const Value &lhs = f.regs.sp[-2];
2198 JSBool equal;
2199 if (!StrictlyEqual(f.cx, lhs, rhs, &equal))
2200 THROW();
2201 f.regs.sp--;
2202 f.regs.sp[-1].setBoolean(equal == JS_TRUE);
2205 void JS_FASTCALL
2206 stubs::StrictNe(VMFrame &f)
2208 const Value &rhs = f.regs.sp[-1];
2209 const Value &lhs = f.regs.sp[-2];
2210 JSBool equal;
2211 if (!StrictlyEqual(f.cx, lhs, rhs, &equal))
2212 THROW();
2213 f.regs.sp--;
2214 f.regs.sp[-1].setBoolean(equal != JS_TRUE);
2217 void JS_FASTCALL
2218 stubs::Throw(VMFrame &f)
2220 JSContext *cx = f.cx;
2222 JS_ASSERT(!cx->isExceptionPending());
2223 cx->setPendingException(f.regs.sp[-1]);
2224 THROW();
2227 JSObject * JS_FASTCALL
2228 stubs::FlatLambda(VMFrame &f, JSFunction *fun)
2230 JSObject *obj = js_NewFlatClosure(f.cx, fun, JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH);
2231 if (!obj)
2232 THROWV(NULL);
2233 return obj;
2236 void JS_FASTCALL
2237 stubs::Arguments(VMFrame &f)
2239 f.regs.sp++;
2240 if (!js_GetArgsValue(f.cx, f.fp(), &f.regs.sp[-1]))
2241 THROW();
2244 JSBool JS_FASTCALL
2245 stubs::InstanceOf(VMFrame &f)
2247 JSContext *cx = f.cx;
2248 JSFrameRegs &regs = f.regs;
2250 const Value &rref = regs.sp[-1];
2251 if (rref.isPrimitive()) {
2252 js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
2253 -1, rref, NULL);
2254 THROWV(JS_FALSE);
2256 JSObject *obj = &rref.toObject();
2257 const Value &lref = regs.sp[-2];
2258 JSBool cond = JS_FALSE;
2259 if (!HasInstance(cx, obj, &lref, &cond))
2260 THROWV(JS_FALSE);
2261 f.regs.sp[-2].setBoolean(cond);
2262 return cond;
2265 void JS_FASTCALL
2266 stubs::FastInstanceOf(VMFrame &f)
2268 const Value &lref = f.regs.sp[-1];
2270 if (lref.isPrimitive()) {
2272 * Throw a runtime error if instanceof is called on a function that
2273 * has a non-object as its .prototype value.
2275 js_ReportValueError(f.cx, JSMSG_BAD_PROTOTYPE, -1, f.regs.sp[-2], NULL);
2276 THROW();
2279 f.regs.sp[-3].setBoolean(js_IsDelegate(f.cx, &lref.toObject(), f.regs.sp[-3]));
2282 void JS_FASTCALL
2283 stubs::ArgCnt(VMFrame &f)
2285 JSContext *cx = f.cx;
2286 JSRuntime *rt = cx->runtime;
2287 JSStackFrame *fp = f.fp();
2289 jsid id = ATOM_TO_JSID(rt->atomState.lengthAtom);
2290 f.regs.sp++;
2291 if (!js_GetArgsProperty(cx, fp, id, &f.regs.sp[-1]))
2292 THROW();
2295 void JS_FASTCALL
2296 stubs::EnterBlock(VMFrame &f, JSObject *obj)
2298 JSFrameRegs &regs = f.regs;
2299 #ifdef DEBUG
2300 JSStackFrame *fp = f.fp();
2301 #endif
2303 JS_ASSERT(obj->isStaticBlock());
2304 JS_ASSERT(fp->base() + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp);
2305 Value *vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj);
2306 JS_ASSERT(regs.sp < vp);
2307 JS_ASSERT(vp <= fp->slots() + fp->script()->nslots);
2308 SetValueRangeToUndefined(regs.sp, vp);
2309 regs.sp = vp;
2311 #ifdef DEBUG
2312 JSContext *cx = f.cx;
2315 * The young end of fp->scopeChain() may omit blocks if we haven't closed
2316 * over them, but if there are any closure blocks on fp->scopeChain(), they'd
2317 * better be (clones of) ancestors of the block we're entering now;
2318 * anything else we should have popped off fp->scopeChain() when we left its
2319 * static scope.
2321 JSObject *obj2 = &fp->scopeChain();
2322 Class *clasp;
2323 while ((clasp = obj2->getClass()) == &js_WithClass)
2324 obj2 = obj2->getParent();
2325 if (clasp == &js_BlockClass &&
2326 obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, fp)) {
2327 JSObject *youngestProto = obj2->getProto();
2328 JS_ASSERT(youngestProto->isStaticBlock());
2329 JSObject *parent = obj;
2330 while ((parent = parent->getParent()) != youngestProto)
2331 JS_ASSERT(parent);
2333 #endif
2336 void JS_FASTCALL
2337 stubs::LeaveBlock(VMFrame &f, JSObject *blockChain)
2339 JSContext *cx = f.cx;
2340 JSStackFrame *fp = f.fp();
2342 #ifdef DEBUG
2343 JS_ASSERT(blockChain->isStaticBlock());
2344 uintN blockDepth = OBJ_BLOCK_DEPTH(cx, blockChain);
2346 JS_ASSERT(blockDepth <= StackDepth(fp->script()));
2347 #endif
2349 * If we're about to leave the dynamic scope of a block that has been
2350 * cloned onto fp->scopeChain(), clear its private data, move its locals from
2351 * the stack into the clone, and pop it off the chain.
2353 JSObject *obj = &fp->scopeChain();
2354 if (obj->getProto() == blockChain) {
2355 JS_ASSERT(obj->getClass() == &js_BlockClass);
2356 if (!js_PutBlockObject(cx, JS_TRUE))
2357 THROW();
2361 void * JS_FASTCALL
2362 stubs::LookupSwitch(VMFrame &f, jsbytecode *pc)
2364 jsbytecode *jpc = pc;
2365 JSScript *script = f.fp()->script();
2366 bool ctor = f.fp()->isConstructing();
2368 /* This is correct because the compiler adjusts the stack beforehand. */
2369 Value lval = f.regs.sp[-1];
2371 if (!lval.isPrimitive()) {
2372 void* native = script->nativeCodeForPC(ctor, pc + GET_JUMP_OFFSET(pc));
2373 JS_ASSERT(native);
2374 return native;
2377 JS_ASSERT(pc[0] == JSOP_LOOKUPSWITCH);
2379 pc += JUMP_OFFSET_LEN;
2380 uint32 npairs = GET_UINT16(pc);
2381 pc += UINT16_LEN;
2383 JS_ASSERT(npairs);
2385 if (lval.isString()) {
2386 JSLinearString *str = lval.toString()->ensureLinear(f.cx);
2387 if (!str)
2388 THROWV(NULL);
2389 for (uint32 i = 1; i <= npairs; i++) {
2390 Value rval = script->getConst(GET_INDEX(pc));
2391 pc += INDEX_LEN;
2392 if (rval.isString()) {
2393 JSLinearString *rhs = rval.toString()->assertIsLinear();
2394 if (rhs == str || EqualStrings(str, rhs)) {
2395 void* native = script->nativeCodeForPC(ctor,
2396 jpc + GET_JUMP_OFFSET(pc));
2397 JS_ASSERT(native);
2398 return native;
2401 pc += JUMP_OFFSET_LEN;
2403 } else if (lval.isNumber()) {
2404 double d = lval.toNumber();
2405 for (uint32 i = 1; i <= npairs; i++) {
2406 Value rval = script->getConst(GET_INDEX(pc));
2407 pc += INDEX_LEN;
2408 if (rval.isNumber() && d == rval.toNumber()) {
2409 void* native = script->nativeCodeForPC(ctor,
2410 jpc + GET_JUMP_OFFSET(pc));
2411 JS_ASSERT(native);
2412 return native;
2414 pc += JUMP_OFFSET_LEN;
2416 } else {
2417 for (uint32 i = 1; i <= npairs; i++) {
2418 Value rval = script->getConst(GET_INDEX(pc));
2419 pc += INDEX_LEN;
2420 if (lval == rval) {
2421 void* native = script->nativeCodeForPC(ctor,
2422 jpc + GET_JUMP_OFFSET(pc));
2423 JS_ASSERT(native);
2424 return native;
2426 pc += JUMP_OFFSET_LEN;
2430 void* native = script->nativeCodeForPC(ctor, jpc + GET_JUMP_OFFSET(jpc));
2431 JS_ASSERT(native);
2432 return native;
2435 void * JS_FASTCALL
2436 stubs::TableSwitch(VMFrame &f, jsbytecode *origPc)
2438 jsbytecode * const originalPC = origPc;
2439 jsbytecode *pc = originalPC;
2440 uint32 jumpOffset = GET_JUMP_OFFSET(pc);
2441 pc += JUMP_OFFSET_LEN;
2443 /* Note: compiler adjusts the stack beforehand. */
2444 Value rval = f.regs.sp[-1];
2446 jsint tableIdx;
2447 if (rval.isInt32()) {
2448 tableIdx = rval.toInt32();
2449 } else if (rval.isDouble()) {
2450 double d = rval.toDouble();
2451 if (d == 0) {
2452 /* Treat -0 (double) as 0. */
2453 tableIdx = 0;
2454 } else if (!JSDOUBLE_IS_INT32(d, (int32_t *)&tableIdx)) {
2455 goto finally;
2457 } else {
2458 goto finally;
2462 jsint low = GET_JUMP_OFFSET(pc);
2463 pc += JUMP_OFFSET_LEN;
2464 jsint high = GET_JUMP_OFFSET(pc);
2465 pc += JUMP_OFFSET_LEN;
2467 tableIdx -= low;
2468 if ((jsuint) tableIdx < (jsuint)(high - low + 1)) {
2469 pc += JUMP_OFFSET_LEN * tableIdx;
2470 uint32 candidateOffset = GET_JUMP_OFFSET(pc);
2471 if (candidateOffset)
2472 jumpOffset = candidateOffset;
2476 finally:
2477 /* Provide the native address. */
2478 JSScript* script = f.fp()->script();
2479 void* native = script->nativeCodeForPC(f.fp()->isConstructing(),
2480 originalPC + jumpOffset);
2481 JS_ASSERT(native);
2482 return native;
2485 void JS_FASTCALL
2486 stubs::Unbrand(VMFrame &f)
2488 const Value &thisv = f.regs.sp[-1];
2489 if (!thisv.isObject())
2490 return;
2491 JSObject *obj = &thisv.toObject();
2492 if (obj->isNative())
2493 obj->unbrand(f.cx);
2496 void JS_FASTCALL
2497 stubs::Pos(VMFrame &f)
2499 if (!ValueToNumber(f.cx, &f.regs.sp[-1]))
2500 THROW();
2503 void JS_FASTCALL
2504 stubs::ArgSub(VMFrame &f, uint32 n)
2506 jsid id = INT_TO_JSID(n);
2507 Value rval;
2508 if (!js_GetArgsProperty(f.cx, f.fp(), id, &rval))
2509 THROW();
2510 f.regs.sp[0] = rval;
2513 void JS_FASTCALL
2514 stubs::DelName(VMFrame &f, JSAtom *atom)
2516 jsid id = ATOM_TO_JSID(atom);
2517 JSObject *obj, *obj2;
2518 JSProperty *prop;
2519 if (!js_FindProperty(f.cx, id, &obj, &obj2, &prop))
2520 THROW();
2522 /* Strict mode code should never contain JSOP_DELNAME opcodes. */
2523 JS_ASSERT(!f.fp()->script()->strictModeCode);
2525 /* ECMA says to return true if name is undefined or inherited. */
2526 f.regs.sp++;
2527 f.regs.sp[-1] = BooleanValue(true);
2528 if (prop) {
2529 if (!obj->deleteProperty(f.cx, id, &f.regs.sp[-1], false))
2530 THROW();
2534 template<JSBool strict>
2535 void JS_FASTCALL
2536 stubs::DelProp(VMFrame &f, JSAtom *atom)
2538 JSContext *cx = f.cx;
2540 JSObject *obj = ValueToObject(cx, &f.regs.sp[-1]);
2541 if (!obj)
2542 THROW();
2544 Value rval;
2545 if (!obj->deleteProperty(cx, ATOM_TO_JSID(atom), &rval, strict))
2546 THROW();
2548 f.regs.sp[-1] = rval;
2551 template void JS_FASTCALL stubs::DelProp<true>(VMFrame &f, JSAtom *atom);
2552 template void JS_FASTCALL stubs::DelProp<false>(VMFrame &f, JSAtom *atom);
2554 template<JSBool strict>
2555 void JS_FASTCALL
2556 stubs::DelElem(VMFrame &f)
2558 JSContext *cx = f.cx;
2560 JSObject *obj = ValueToObject(cx, &f.regs.sp[-2]);
2561 if (!obj)
2562 THROW();
2564 jsid id;
2565 if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
2566 THROW();
2568 if (!obj->deleteProperty(cx, id, &f.regs.sp[-2], strict))
2569 THROW();
2572 void JS_FASTCALL
2573 stubs::DefVarOrConst(VMFrame &f, JSAtom *atom)
2575 JSContext *cx = f.cx;
2576 JSStackFrame *fp = f.fp();
2578 JSObject *obj = &fp->varobj(cx);
2579 JS_ASSERT(!obj->getOps()->defineProperty);
2580 uintN attrs = JSPROP_ENUMERATE;
2581 if (!fp->isEvalFrame())
2582 attrs |= JSPROP_PERMANENT;
2584 /* Lookup id in order to check for redeclaration problems. */
2585 jsid id = ATOM_TO_JSID(atom);
2586 bool shouldDefine;
2587 if (JSOp(*f.regs.pc) == JSOP_DEFVAR) {
2589 * Redundant declaration of a |var|, even one for a non-writable
2590 * property like |undefined| in ES5, does nothing.
2592 JSProperty *prop;
2593 JSObject *obj2;
2594 if (!obj->lookupProperty(cx, id, &obj2, &prop))
2595 THROW();
2596 shouldDefine = (!prop || obj2 != obj);
2597 } else {
2598 JS_ASSERT(JSOp(*f.regs.pc) == JSOP_DEFCONST);
2599 attrs |= JSPROP_READONLY;
2600 if (!CheckRedeclaration(cx, obj, id, attrs))
2601 THROW();
2604 * As attrs includes readonly, CheckRedeclaration can succeed only
2605 * if prop does not exist.
2607 shouldDefine = true;
2610 /* Bind a variable only if it's not yet defined. */
2611 if (shouldDefine &&
2612 !js_DefineNativeProperty(cx, obj, id, UndefinedValue(), PropertyStub, StrictPropertyStub,
2613 attrs, 0, 0, NULL)) {
2614 THROW();
2618 void JS_FASTCALL
2619 stubs::SetConst(VMFrame &f, JSAtom *atom)
2621 JSContext *cx = f.cx;
2622 JSStackFrame *fp = f.fp();
2624 JSObject *obj = &fp->varobj(cx);
2625 const Value &ref = f.regs.sp[-1];
2626 if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), ref,
2627 PropertyStub, StrictPropertyStub,
2628 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) {
2629 THROW();
2633 JSBool JS_FASTCALL
2634 stubs::In(VMFrame &f)
2636 JSContext *cx = f.cx;
2638 const Value &rref = f.regs.sp[-1];
2639 if (!rref.isObject()) {
2640 js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rref, NULL);
2641 THROWV(JS_FALSE);
2644 JSObject *obj = &rref.toObject();
2645 jsid id;
2646 if (!FetchElementId(f, obj, f.regs.sp[-2], id, &f.regs.sp[-2]))
2647 THROWV(JS_FALSE);
2649 JSObject *obj2;
2650 JSProperty *prop;
2651 if (!obj->lookupProperty(cx, id, &obj2, &prop))
2652 THROWV(JS_FALSE);
2654 return !!prop;
2657 template void JS_FASTCALL stubs::DelElem<true>(VMFrame &f);
2658 template void JS_FASTCALL stubs::DelElem<false>(VMFrame &f);
2660 void JS_FASTCALL
2661 stubs::Exception(VMFrame &f)
2663 f.regs.sp[0] = f.cx->getPendingException();
2664 f.cx->clearPendingException();
2666 template <bool Clamped>
2667 int32 JS_FASTCALL
2668 stubs::ConvertToTypedInt(JSContext *cx, Value *vp)
2670 JS_ASSERT(!vp->isInt32());
2672 if (vp->isDouble()) {
2673 if (Clamped)
2674 return js_TypedArray_uint8_clamp_double(vp->toDouble());
2675 return js_DoubleToECMAInt32(vp->toDouble());
2678 if (vp->isNull() || vp->isObject() || vp->isUndefined())
2679 return 0;
2681 if (vp->isBoolean())
2682 return vp->toBoolean() ? 1 : 0;
2684 JS_ASSERT(vp->isString());
2686 int32 i32 = 0;
2687 #ifdef DEBUG
2688 bool success =
2689 #endif
2690 StringToNumberType<jsint>(cx, vp->toString(), &i32);
2691 JS_ASSERT(success);
2693 return i32;
2696 template int32 JS_FASTCALL stubs::ConvertToTypedInt<true>(JSContext *, Value *);
2697 template int32 JS_FASTCALL stubs::ConvertToTypedInt<false>(JSContext *, Value *);
2699 void JS_FASTCALL
2700 stubs::ConvertToTypedFloat(JSContext *cx, Value *vp)
2702 JS_ASSERT(!vp->isDouble() && !vp->isInt32());
2704 if (vp->isNull()) {
2705 vp->setDouble(0);
2706 } else if (vp->isObject() || vp->isUndefined()) {
2707 vp->setDouble(js_NaN);
2708 } else if (vp->isBoolean()) {
2709 vp->setDouble(vp->toBoolean() ? 1 : 0);
2710 } else {
2711 JS_ASSERT(vp->isString());
2712 double d = 0;
2713 #ifdef DEBUG
2714 bool success =
2715 #endif
2716 StringToNumberType<double>(cx, vp->toString(), &d);
2717 JS_ASSERT(success);
2718 vp->setDouble(d);