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
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
20 * The Initial Developer of the Original Code is
21 * Brendan Eich <brendan@mozilla.org>
24 * David Anderson <danderson@mozilla.com>
25 * David Mandelin <dmandelin@mozilla.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #define __STDC_LIMIT_MACROS
46 #include "jslibmath.h"
50 #include "jsstaticcheck.h"
52 #include "assembler/assembler/MacroAssemblerCodeRef.h"
55 #include "methodjit/StubCalls.h"
57 #include "jspropertycache.h"
58 #include "jspropertycacheinlines.h"
59 #include "jsscopeinlines.h"
60 #include "jsscriptinlines.h"
61 #include "jsstrinlines.h"
62 #include "jsobjinlines.h"
63 #include "jscntxtinlines.h"
64 #include "jsatominlines.h"
66 #include "jsautooplen.h"
69 using namespace js::mjit
;
74 void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
75 f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr))); \
81 void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
82 f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr))); \
87 mjit::stubs::BindName(VMFrame
&f
)
89 PropertyCacheEntry
*entry
;
91 /* Fast-path should have caught this. See comment in interpreter. */
92 JS_ASSERT(f
.fp
->scopeChainObj()->getParent());
97 JSObject
*obj
= f
.fp
->scopeChainObj();
98 JS_PROPERTY_CACHE(cx
).test(cx
, f
.regs
.pc
, obj
, obj2
, entry
, atom
);
100 jsid id
= ATOM_TO_JSID(atom
);
101 obj
= js_FindIdentifierBase(cx
, f
.fp
->scopeChainObj(), id
);
106 f
.regs
.sp
[-1].setNonFunObj(*obj
);
110 InlineReturn(JSContext
*cx
)
114 JSStackFrame
*fp
= cx
->fp
;
116 JS_ASSERT(!fp
->blockChain
);
117 JS_ASSERT(!js_IsActiveWithOrBlock(cx
, fp
->scopeChainObj(), 0));
119 if (fp
->script
->staticLevel
< JS_DISPLAY_SIZE
)
120 cx
->display
[fp
->script
->staticLevel
] = fp
->displaySave
;
122 // Marker for debug support.
123 void *hookData
= fp
->hookData
;
124 if (JS_UNLIKELY(hookData
!= NULL
)) {
125 JSInterpreterHook hook
;
128 hook
= cx
->debugHooks
->callHook
;
131 * Do not pass &ok directly as exposing the address inhibits
132 * optimizations and uninitialised warnings.
135 hook(cx
, fp
, JS_FALSE
, &status
, hookData
);
136 ok
= (status
== JS_TRUE
);
137 // CHECK_INTERRUPT_HANDLER();
141 fp
->putActivationObjects(cx
);
143 /* :TODO: version stuff */
145 if (fp
->flags
& JSFRAME_CONSTRUCTING
&& fp
->rval
.isPrimitive())
146 fp
->rval
= fp
->thisv
;
148 cx
->stack().popInlineFrame(cx
, fp
, fp
->down
);
149 cx
->regs
->sp
[-1] = fp
->rval
;
155 mjit::stubs::Return(VMFrame
&f
)
157 if (!f
.inlineCallCount
)
160 JSContext
*cx
= f
.cx
;
161 JS_ASSERT(f
.fp
== cx
->fp
);
164 bool wasInterp
= f
.fp
->script
->ncode
== JS_UNJITTABLE_METHOD
;
167 bool ok
= InlineReturn(cx
);
170 JS_ASSERT(f
.regs
.sp
== cx
->regs
->sp
);
173 JS_ASSERT_IF(f
.inlineCallCount
> 1 && !wasInterp
,
174 f
.fp
->down
->script
->isValidJitCode(f
.fp
->ncode
));
183 FindExceptionHandler(JSContext
*cx
)
185 JSStackFrame
*fp
= cx
->fp
;
186 JSScript
*script
= fp
->script
;
189 if (cx
->throwing
&& script
->trynotesOffset
) {
190 // The PC is updated before every stub call, so we can use it here.
191 unsigned offset
= cx
->regs
->pc
- script
->main
;
193 JSTryNoteArray
*tnarray
= script
->trynotes();
194 for (unsigned i
= 0; i
< tnarray
->length
; ++i
) {
195 JSTryNote
*tn
= &tnarray
->vector
[i
];
196 if (offset
- tn
->start
>= tn
->length
)
198 if (tn
->stackDepth
> cx
->regs
->sp
- fp
->base())
201 jsbytecode
*pc
= script
->main
+ tn
->start
+ tn
->length
;
202 JSBool ok
= js_UnwindScope(cx
, tn
->stackDepth
, JS_TRUE
);
203 JS_ASSERT(cx
->regs
->sp
== fp
->base() + tn
->stackDepth
);
207 JS_ASSERT(js_GetOpcode(cx
, fp
->script
, pc
) == JSOP_ENTERBLOCK
);
209 #if JS_HAS_GENERATORS
210 /* Catch cannot intercept the closing of a generator. */
211 if (JS_UNLIKELY(cx
->exception
.isMagic(JS_GENERATOR_CLOSING
)))
216 * Don't clear cx->throwing to save cx->exception from GC
217 * until it is pushed to the stack via [exception] in the
224 * Push (true, exception) pair for finally to indicate that
225 * [retsub] should rethrow the exception.
227 cx
->regs
->sp
[0].setBoolean(true);
228 cx
->regs
->sp
[1] = cx
->exception
;
230 cx
->throwing
= JS_FALSE
;
236 * This is similar to JSOP_ENDITER in the interpreter loop,
237 * except the code now uses the stack slot normally used by
238 * JSOP_NEXTITER, namely regs.sp[-1] before the regs.sp -= 2
239 * adjustment and regs.sp[1] after, to save and restore the
242 AutoValueRooter
tvr(cx
, cx
->exception
);
243 JS_ASSERT(js_GetOpcode(cx
, fp
->script
, pc
) == JSOP_ENDITER
);
244 cx
->throwing
= JS_FALSE
;
245 ok
= !!js_CloseIterator(cx
, cx
->regs
->sp
[-1]);
249 cx
->throwing
= JS_TRUE
;
250 cx
->exception
= tvr
.value();
260 js_InternalThrow(VMFrame
&f
)
262 JSContext
*cx
= f
.cx
;
264 // Make sure sp is up to date.
265 JS_ASSERT(cx
->regs
== &f
.regs
);
267 jsbytecode
*pc
= NULL
;
269 pc
= FindExceptionHandler(cx
);
273 // If |f.inlineCallCount == 0|, then we are on the 'topmost' frame (where
274 // topmost means the first frame called into through js_Interpret). In this
275 // case, we still unwind, but we shouldn't return from a JS function, because
276 // we're not in a JS function.
277 bool lastFrame
= (f
.inlineCallCount
== 0);
278 js_UnwindScope(cx
, 0, cx
->throwing
);
282 JS_ASSERT(f
.regs
.sp
== cx
->regs
->sp
);
283 f
.scriptedReturn
= stubs::Return(f
);
286 JS_ASSERT(f
.regs
.sp
== cx
->regs
->sp
);
290 f
.cx
->setCurrentRegs(f
.oldRegs
);
294 return cx
->fp
->script
->pcToNative(pc
);
297 #define NATIVE_SET(cx,obj,sprop,entry,vp) \
299 if (sprop->hasDefaultSetter() && \
300 (sprop)->slot != SPROP_INVALID_SLOT && \
301 !obj->scope()->brandedOrHasMethodBarrier()) { \
302 /* Fast path for, e.g., plain Object instance properties. */ \
303 obj->setSlot(sprop->slot, *vp); \
305 if (!js_NativeSet(cx, obj, sprop, false, vp)) \
310 static inline JSObject
*
311 ValueToObject(JSContext
*cx
, Value
*vp
)
314 return &vp
->asObject();
315 if (!js_ValueToNonNullObject(cx
, *vp
, vp
))
317 return &vp
->asObject();
320 #define NATIVE_GET(cx,obj,pobj,sprop,getHow,vp,onerr) \
322 if (sprop->hasDefaultGetter()) { \
323 /* Fast path for Object instance properties. */ \
324 JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT || \
325 !sprop->hasDefaultSetter()); \
326 if (((sprop)->slot != SPROP_INVALID_SLOT)) \
327 *(vp) = (pobj)->lockedGetSlot((sprop)->slot); \
329 (vp)->setUndefined(); \
331 if (!js_NativeGet(cx, obj, pobj, sprop, getHow, vp)) \
337 mjit::stubs::SetName(VMFrame
&f
, JSAtom
*origAtom
)
339 JSContext
*cx
= f
.cx
;
341 Value
&rref
= f
.regs
.sp
[-1];
342 Value
&lref
= f
.regs
.sp
[-2];
343 JSObject
*obj
= ValueToObject(cx
, &lref
);
348 PropertyCache
*cache
= &JS_PROPERTY_CACHE(cx
);
351 * Probe the property cache, specializing for two important
352 * set-property cases. First:
354 * function f(a, b, c) {
355 * var o = {p:a, q:b, r:c};
359 * or similar real-world cases, which evolve a newborn native
360 * object predicatably through some bounded number of property
361 * additions. And second:
365 * in a frequently executed method or loop body, where p will
366 * (possibly after the first iteration) always exist in native
369 PropertyCacheEntry
*entry
;
372 if (cache
->testForSet(cx
, f
.regs
.pc
, obj
, &entry
, &obj2
, &atom
)) {
374 * Fast property cache hit, only partially confirmed by
375 * testForSet. We know that the entry applies to regs.pc and
376 * that obj's shape matches.
378 * The entry predicts either a new property to be added
379 * directly to obj by this set, or on an existing "own"
380 * property, or on a prototype property that has a setter.
382 JS_ASSERT(entry
->vword
.isSprop());
383 JSScopeProperty
*sprop
= entry
->vword
.toSprop();
384 JS_ASSERT_IF(sprop
->isDataDescriptor(), sprop
->writable());
385 JS_ASSERT_IF(sprop
->hasSlot(), entry
->vcapTag() == 0);
387 JSScope
*scope
= obj
->scope();
388 JS_ASSERT(!scope
->sealed());
391 * Fastest path: check whether the cached sprop is already
392 * in scope and call NATIVE_SET and break to get out of the
393 * do-while(0). But we can call NATIVE_SET only if obj owns
394 * scope or sprop is shared.
397 if (!sprop
->hasSlot()) {
398 if (entry
->vcapTag() == 0 ||
399 ((obj2
= obj
->getProto()) &&
401 obj2
->shape() == entry
->vshape())) {
402 goto fast_set_propcache_hit
;
405 /* The cache entry doesn't apply. vshape mismatch. */
407 } else if (!scope
->isSharedEmpty()) {
408 if (sprop
== scope
->lastProperty() || scope
->hasProperty(sprop
)) {
409 fast_set_propcache_hit
:
410 PCMETER(cache
->pchits
++);
411 PCMETER(cache
->setpchits
++);
412 NATIVE_SET(cx
, obj
, sprop
, entry
, &rref
);
415 checkForAdd
= sprop
->hasSlot() && sprop
->parent
== scope
->lastProperty();
418 * We check that cx own obj here and will continue to
419 * own it after js_GetMutableScope returns so we can
420 * continue to skip JS_UNLOCK_OBJ calls.
422 JS_ASSERT(CX_OWNS_OBJECT_TITLE(cx
, obj
));
423 scope
= js_GetMutableScope(cx
, obj
);
424 JS_ASSERT(CX_OWNS_OBJECT_TITLE(cx
, obj
));
427 checkForAdd
= !sprop
->parent
;
432 entry
->vshape() == cx
->runtime
->protoHazardShape
&&
433 sprop
->hasDefaultSetter() &&
434 (slot
= sprop
->slot
) == scope
->freeslot
) {
436 * Fast path: adding a plain old property that was once
437 * at the frontier of the property tree, whose slot is
438 * next to claim among the allocated slots in obj,
439 * where scope->table has not been created yet.
441 * We may want to remove hazard conditions above and
442 * inline compensation code here, depending on
443 * real-world workloads.
445 PCMETER(cache
->pchits
++);
446 PCMETER(cache
->addpchits
++);
449 * Beware classes such as Function that use the
450 * reserveSlots hook to allocate a number of reserved
451 * slots that may vary with obj.
453 if (slot
< obj
->numSlots() &&
454 !obj
->getClass()->reserveSlots
) {
457 if (!js_AllocSlot(cx
, obj
, &slot
))
462 * If this obj's number of reserved slots differed, or
463 * if something created a hash table for scope, we must
464 * pay the price of JSScope::putProperty.
466 * (A reserveSlots hook can cause scopes of the same
467 * shape to have different freeslot values. This is
468 * what causes the slot != sprop->slot case. See
469 * js_GetMutableScope.)
471 if (slot
!= sprop
->slot
|| scope
->table
) {
472 JSScopeProperty
*sprop2
=
473 scope
->putProperty(cx
, sprop
->id
,
474 sprop
->getter(), sprop
->setter(),
475 slot
, sprop
->attributes(),
476 sprop
->getFlags(), sprop
->shortid
);
478 js_FreeSlot(cx
, obj
, slot
);
483 scope
->extend(cx
, sprop
);
487 * No method change check here because here we are
488 * adding a new property, not updating an existing
489 * slot's value that might contain a method of a
492 obj
->lockedSetSlot(slot
, rref
);
495 * Purge the property cache of the id we may have just
496 * shadowed in obj's scope and proto chains. We do this
497 * after unlocking obj's scope to avoid lock nesting.
499 js_PurgeScopeChain(cx
, obj
, sprop
->id
);
502 PCMETER(cache
->setpcmisses
++);
506 * Slower property cache hit, fully confirmed by testForSet (in
507 * the slow path, via fullTest).
509 JSScopeProperty
*sprop
= NULL
;
511 sprop
= entry
->vword
.toSprop();
512 JS_ASSERT(sprop
->writable());
513 JS_ASSERT(!obj2
->scope()->sealed());
514 NATIVE_SET(cx
, obj
, sprop
, entry
, &rref
);
522 jsid id
= ATOM_TO_JSID(atom
);
523 if (entry
&& JS_LIKELY(obj
->map
->ops
->setProperty
== js_SetProperty
)) {
525 JSOp op
= JSOp(*f
.regs
.pc
);
526 if (op
== JSOP_SETMETHOD
)
527 defineHow
= JSDNP_CACHE_RESULT
| JSDNP_SET_METHOD
;
528 else if (op
== JSOP_SETNAME
)
529 defineHow
= JSDNP_CACHE_RESULT
| JSDNP_UNQUALIFIED
;
531 defineHow
= JSDNP_CACHE_RESULT
;
532 if (!js_SetPropertyHelper(cx
, obj
, id
, defineHow
, &rref
))
535 if (!obj
->setProperty(cx
, id
, &rref
))
540 f
.regs
.sp
[-2] = f
.regs
.sp
[-1];
544 stubs::SetGlobalName(VMFrame
&f
, JSAtom
*atom
)
550 ReportAtomNotDefined(JSContext
*cx
, JSAtom
*atom
)
552 const char *printable
= js_AtomToPrintableString(cx
, atom
);
554 js_ReportIsNotDefined(cx
, printable
);
558 NameOp(VMFrame
&f
, JSObject
*obj
)
560 JSContext
*cx
= f
.cx
;
562 JSScopeProperty
*sprop
;
565 PropertyCacheEntry
*entry
;
568 JS_PROPERTY_CACHE(cx
).test(cx
, f
.regs
.pc
, obj
, obj2
, entry
, atom
);
570 if (entry
->vword
.isFunObj()) {
572 f
.regs
.sp
[-1].setFunObj(entry
->vword
.toFunObj());
576 if (entry
->vword
.isSlot()) {
577 uintN slot
= entry
->vword
.toSlot();
578 JS_ASSERT(slot
< obj2
->scope()->freeslot
);
580 f
.regs
.sp
[-1] = obj2
->lockedGetSlot(slot
);
584 JS_ASSERT(entry
->vword
.isSprop());
585 sprop
= entry
->vword
.toSprop();
590 id
= ATOM_TO_JSID(atom
);
592 if (!js_FindPropertyHelper(cx
, id
, true, &obj
, &obj2
, &prop
))
595 /* Kludge to allow (typeof foo == "undefined") tests. */
596 JSOp op2
= js_GetOpcode(cx
, f
.fp
->script
, f
.regs
.pc
+ JSOP_NAME_LENGTH
);
597 if (op2
== JSOP_TYPEOF
) {
599 f
.regs
.sp
[-1].setUndefined();
602 ReportAtomNotDefined(cx
, atom
);
606 /* Take the slow path if prop was not found in a native object. */
607 if (!obj
->isNative() || !obj2
->isNative()) {
608 obj2
->dropProperty(cx
, prop
);
609 if (!obj
->getProperty(cx
, id
, &rval
))
612 sprop
= (JSScopeProperty
*)prop
;
614 NATIVE_GET(cx
, obj
, obj2
, sprop
, JSGET_METHOD_BARRIER
, &rval
, return NULL
);
615 obj2
->dropProperty(cx
, (JSProperty
*) sprop
);
619 f
.regs
.sp
[-1] = rval
;
624 stubs::Name(VMFrame
&f
)
626 if (!NameOp(f
, f
.fp
->scopeChainObj()))
631 stubs::GetGlobalName(VMFrame
&f
)
633 JSObject
*globalObj
= f
.fp
->scopeChainObj()->getGlobal();
634 if (!NameOp(f
, globalObj
))
639 IteratorNext(JSContext
*cx
, JSObject
*iterobj
, Value
*rval
)
641 if (iterobj
->getClass() == &js_IteratorClass
.base
) {
642 NativeIterator
*ni
= (NativeIterator
*) iterobj
->getPrivate();
643 JS_ASSERT(ni
->props_cursor
< ni
->props_end
);
644 *rval
= ID_TO_VALUE(*ni
->props_cursor
);
645 if (rval
->isString() || (ni
->flags
& JSITER_FOREACH
)) {
649 /* Take the slow path if we have to stringify a numeric property name. */
651 return js_IteratorNext(cx
, iterobj
, rval
);
655 stubs::ForName(VMFrame
&f
, JSAtom
*atom
)
657 JSContext
*cx
= f
.cx
;
658 JSFrameRegs
®s
= f
.regs
;
660 JS_ASSERT(regs
.sp
- 1 >= f
.fp
->base());
661 jsid id
= ATOM_TO_JSID(atom
);
662 JSObject
*obj
, *obj2
;
664 if (!js_FindProperty(cx
, id
, &obj
, &obj2
, &prop
))
667 obj2
->dropProperty(cx
, prop
);
669 AutoValueRooter
tvr(cx
);
670 JS_ASSERT(regs
.sp
[-1].isObject());
671 if (!IteratorNext(cx
, ®s
.sp
[-1].asObject(), tvr
.addr()))
673 if (!obj
->setProperty(cx
, id
, tvr
.addr()))
679 stubs::GetElem(VMFrame
&f
)
681 JSContext
*cx
= f
.cx
;
682 JSFrameRegs
®s
= f
.regs
;
684 Value lval
= regs
.sp
[-2];
685 Value rval
= regs
.sp
[-1];
686 const Value
*copyFrom
;
692 if (lval
.isString() && rval
.isInt32()) {
694 JSString
*str
= lval
.asString();
697 if ((size_t)i
>= str
->length())
700 str
= JSString::getUnitString(cx
, str
, (size_t)i
);
703 f
.regs
.sp
[-2].setString(str
);
707 obj
= ValueToObject(cx
, &lval
);
711 if (rval
.isInt32()) {
712 if (obj
->isDenseArray()) {
713 jsuint idx
= jsuint(rval
.asInt32());
715 if (idx
< obj
->getArrayLength() &&
716 idx
< obj
->getDenseArrayCapacity()) {
717 copyFrom
= obj
->addressOfDenseArrayElement(idx
);
718 if (!copyFrom
->isMagic())
720 /* Otherwise, fall to getProperty(). */
722 } else if (obj
->isArguments()
723 #if 0 /* def JS_TRACER */
724 && !GetArgsPrivateNative(obj
)
727 uint32 arg
= uint32(rval
.asInt32());
729 if (arg
< obj
->getArgsLength()) {
730 JSStackFrame
*afp
= (JSStackFrame
*) obj
->getPrivate();
732 copyFrom
= &afp
->argv
[arg
];
736 copyFrom
= obj
->addressOfArgsElement(arg
);
737 if (!copyFrom
->isMagic())
739 /* Otherwise, fall to getProperty(). */
742 id
= INT_TO_JSID(rval
.asInt32());
745 if (!js_InternNonIntElementId(cx
, obj
, rval
, &id
))
749 if (!obj
->getProperty(cx
, id
, &rval
))
754 f
.regs
.sp
[-2] = *copyFrom
;
758 FetchElementId(VMFrame
&f
, JSObject
*obj
, const Value
&idval
, jsid
&id
, Value
*vp
)
761 if (ValueFitsInInt32(idval
, &i_
)) {
762 id
= INT_TO_JSID(i_
);
765 return !!js_InternNonIntElementId(f
.cx
, obj
, idval
, &id
, vp
);
769 stubs::CallElem(VMFrame
&f
)
771 JSContext
*cx
= f
.cx
;
772 JSFrameRegs
®s
= f
.regs
;
774 /* Fetch the left part and resolve it to a non-null object. */
775 JSObject
*obj
= ValueToObject(cx
, ®s
.sp
[-2]);
779 /* Fetch index and convert it to id suitable for use with obj. */
781 if (!FetchElementId(f
, obj
, regs
.sp
[-1], id
, ®s
.sp
[-2]))
784 /* Get or set the element. */
785 if (!js_GetMethod(cx
, obj
, id
, JSGET_NO_METHOD_BARRIER
, ®s
.sp
[-2]))
788 #if JS_HAS_NO_SUCH_METHOD
789 if (JS_UNLIKELY(regs
.sp
[-2].isUndefined())) {
790 regs
.sp
[-2] = regs
.sp
[-1];
791 regs
.sp
[-1].setObject(*obj
);
792 if (!js_OnUnknownMethod(cx
, regs
.sp
- 2))
797 regs
.sp
[-1].setObject(*obj
);
802 stubs::SetElem(VMFrame
&f
)
804 JSContext
*cx
= f
.cx
;
805 JSFrameRegs
®s
= f
.regs
;
807 Value
&objval
= regs
.sp
[-3];
808 Value
&idval
= regs
.sp
[-2];
809 Value retval
= regs
.sp
[-1];
814 obj
= ValueToObject(cx
, &objval
);
818 if (!FetchElementId(f
, obj
, idval
, id
, ®s
.sp
[-2]))
821 if (obj
->isDenseArray() && JSID_IS_INT(id
)) {
822 jsuint length
= obj
->getDenseArrayCapacity();
823 jsint i
= JSID_TO_INT(id
);
825 if ((jsuint
)i
< length
) {
826 if (obj
->getDenseArrayElement(i
).isMagic(JS_ARRAY_HOLE
)) {
827 if (js_PrototypeHasIndexedProperties(cx
, obj
))
829 if ((jsuint
)i
>= obj
->getArrayLength())
830 obj
->setDenseArrayLength(i
+ 1);
831 obj
->incDenseArrayCountBy(1);
833 obj
->setDenseArrayElement(i
, regs
.sp
[-1]);
839 if (!obj
->setProperty(cx
, id
, ®s
.sp
[-1]))
843 /* :FIXME: Moving the assigned object into the lowest stack slot
844 * is a temporary hack. What we actually want is an implementation
845 * of popAfterSet() that allows popping more than one value;
846 * this logic can then be handled in Compiler.cpp. */
847 regs
.sp
[-3] = retval
;
851 stubs::CallName(VMFrame
&f
)
853 JSObject
*obj
= NameOp(f
, f
.fp
->scopeChainObj());
857 f
.regs
.sp
[-1].setNonFunObj(*obj
);
861 stubs::BitOr(VMFrame
&f
)
865 if (!ValueToECMAInt32(f
.cx
, f
.regs
.sp
[-2], &i
) ||
866 !ValueToECMAInt32(f
.cx
, f
.regs
.sp
[-1], &j
)) {
870 f
.regs
.sp
[-2].setInt32(i
);
874 stubs::BitXor(VMFrame
&f
)
878 if (!ValueToECMAInt32(f
.cx
, f
.regs
.sp
[-2], &i
) ||
879 !ValueToECMAInt32(f
.cx
, f
.regs
.sp
[-1], &j
)) {
883 f
.regs
.sp
[-2].setInt32(i
);
887 stubs::BitAnd(VMFrame
&f
)
891 if (!ValueToECMAInt32(f
.cx
, f
.regs
.sp
[-2], &i
) ||
892 !ValueToECMAInt32(f
.cx
, f
.regs
.sp
[-1], &j
)) {
896 f
.regs
.sp
[-2].setInt32(i
);
900 stubs::BitNot(VMFrame
&f
)
904 if (!ValueToECMAInt32(f
.cx
, f
.regs
.sp
[-1], &i
))
907 f
.regs
.sp
[-1].setInt32(i
);
911 stubs::Lsh(VMFrame
&f
)
914 if (!ValueToECMAInt32(f
.cx
, f
.regs
.sp
[-2], &i
))
916 if (!ValueToECMAInt32(f
.cx
, f
.regs
.sp
[-1], &j
))
919 f
.regs
.sp
[-2].setInt32(i
);
923 stubs::Rsh(VMFrame
&f
)
926 if (!ValueToECMAInt32(f
.cx
, f
.regs
.sp
[-2], &i
))
928 if (!ValueToECMAInt32(f
.cx
, f
.regs
.sp
[-1], &j
))
931 f
.regs
.sp
[-2].setInt32(i
);
935 stubs::Ursh(VMFrame
&f
)
938 if (!ValueToECMAUint32(f
.cx
, f
.regs
.sp
[-2], &u
))
941 if (!ValueToECMAInt32(f
.cx
, f
.regs
.sp
[-1], &j
))
946 f
.regs
.sp
[-2].setNumber(uint32(u
));
951 PostInc(VMFrame
&f
, Value
*vp
)
954 if (!ValueToNumber(f
.cx
, *vp
, &d
))
957 f
.regs
.sp
[-1].setDouble(d
);
965 PreInc(VMFrame
&f
, Value
*vp
)
968 if (!ValueToNumber(f
.cx
, *vp
, &d
))
973 f
.regs
.sp
[-1].setDouble(d
);
978 stubs::VpInc(VMFrame
&f
, Value
*vp
)
980 if (!PostInc
<1>(f
, vp
))
985 stubs::VpDec(VMFrame
&f
, Value
*vp
)
987 if (!PostInc
<-1>(f
, vp
))
992 stubs::DecVp(VMFrame
&f
, Value
*vp
)
994 if (!PreInc
<-1>(f
, vp
))
999 stubs::IncVp(VMFrame
&f
, Value
*vp
)
1001 if (!PreInc
<1>(f
, vp
))
1006 DoInvoke(VMFrame
&f
, uint32 argc
, Value
*vp
)
1008 JSContext
*cx
= f
.cx
;
1010 if (vp
->isFunObj()) {
1011 JSObject
*obj
= &vp
->asFunObj();
1012 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, obj
);
1014 JS_ASSERT(!FUN_INTERPRETED(fun
));
1015 if (fun
->flags
& JSFUN_FAST_NATIVE
) {
1016 JS_ASSERT(fun
->u
.n
.extra
== 0);
1017 JS_ASSERT(vp
[1].isObjectOrNull() || PrimitiveValue::test(fun
, vp
[1]));
1018 JSBool ok
= ((FastNative
)fun
->u
.n
.native
)(cx
, argc
, vp
);
1020 return ok
? true : false;
1024 JSBool ok
= Invoke(cx
, InvokeArgsGuard(vp
, argc
), 0);
1026 return ok
? true : false;
1030 InlineCall(VMFrame
&f
, uint32 flags
, void **pret
, uint32 argc
)
1032 JSContext
*cx
= f
.cx
;
1033 JSStackFrame
*fp
= f
.fp
;
1034 Value
*vp
= f
.regs
.sp
- (argc
+ 2);
1035 JSObject
*funobj
= &vp
->asFunObj();
1036 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
1038 JS_ASSERT(FUN_INTERPRETED(fun
));
1040 JSScript
*newscript
= fun
->u
.i
.script
;
1042 if (f
.inlineCallCount
>= JS_MAX_INLINE_CALL_COUNT
) {
1043 js_ReportOverRecursed(cx
);
1047 /* Allocate the frame. */
1048 StackSpace
&stack
= cx
->stack();
1049 uintN nslots
= newscript
->nslots
;
1050 uintN funargs
= fun
->nargs
;
1051 Value
*argv
= vp
+ 2;
1052 JSStackFrame
*newfp
;
1053 if (argc
< funargs
) {
1054 uintN missing
= funargs
- argc
;
1055 newfp
= stack
.getInlineFrame(cx
, f
.regs
.sp
, missing
, nslots
);
1058 for (Value
*v
= argv
+ argc
, *end
= v
+ missing
; v
!= end
; ++v
)
1061 newfp
= stack
.getInlineFrame(cx
, f
.regs
.sp
, 0, nslots
);
1066 /* Initialize the frame. */
1067 newfp
->ncode
= NULL
;
1068 newfp
->callobj
= NULL
;
1069 newfp
->argsval
.setNull();
1070 newfp
->script
= newscript
;
1073 newfp
->argv
= vp
+ 2;
1074 newfp
->rval
.setUndefined();
1075 newfp
->annotation
= NULL
;
1076 newfp
->scopeChain
.setNonFunObj(*funobj
->getParent());
1077 newfp
->flags
= flags
;
1078 newfp
->blockChain
= NULL
;
1079 JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun
->flags
));
1080 newfp
->thisv
= vp
[1];
1081 newfp
->imacpc
= NULL
;
1083 /* Push void to initialize local variables. */
1084 Value
*newslots
= newfp
->slots();
1085 Value
*newsp
= newslots
+ fun
->u
.i
.nvars
;
1086 for (Value
*v
= newslots
; v
!= newsp
; ++v
)
1089 /* Scope with a call object parented by callee's parent. */
1090 if (fun
->isHeavyweight() && !js_GetCallObject(cx
, newfp
))
1093 /* :TODO: Switch version if currentVersion wasn't overridden. */
1094 newfp
->callerVersion
= (JSVersion
)cx
->version
;
1096 // Marker for debug support.
1097 if (JSInterpreterHook hook
= cx
->debugHooks
->callHook
) {
1098 newfp
->hookData
= hook(cx
, fp
, JS_TRUE
, 0,
1099 cx
->debugHooks
->callHookData
);
1100 // CHECK_INTERRUPT_HANDLER();
1102 newfp
->hookData
= NULL
;
1105 f
.inlineCallCount
++;
1107 stack
.pushInlineFrame(cx
, fp
, cx
->regs
->pc
, newfp
);
1109 if (newscript
->staticLevel
< JS_DISPLAY_SIZE
) {
1110 JSStackFrame
**disp
= &cx
->display
[newscript
->staticLevel
];
1111 newfp
->displaySave
= *disp
;
1115 f
.regs
.pc
= newscript
->code
;
1118 if (cx
->options
& JSOPTION_METHODJIT
) {
1119 if (!newscript
->ncode
) {
1120 if (mjit::TryCompile(cx
, newscript
, fun
, newfp
->scopeChainObj()) == Compile_Error
)
1123 JS_ASSERT(newscript
->ncode
);
1124 if (newscript
->ncode
!= JS_UNJITTABLE_METHOD
) {
1125 fp
->ncode
= f
.scriptedReturn
;
1126 *pret
= newscript
->ncode
;
1131 bool ok
= !!Interpret(cx
); //, newfp, f.inlineCallCount);
1139 stubs::Call(VMFrame
&f
, uint32 argc
)
1141 Value
*vp
= f
.regs
.sp
- (argc
+ 2);
1143 if (vp
->isFunObj()) {
1144 JSObject
*obj
= &vp
->asFunObj();
1145 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, obj
);
1146 if (FUN_INTERPRETED(fun
)) {
1147 if (fun
->u
.i
.script
->isEmpty()) {
1154 if (!InlineCall(f
, 0, &ret
, argc
))
1157 f
.cx
->regs
->pc
= f
.fp
->script
->code
;
1159 #if 0 /* def JS_TRACER */
1160 if (ret
&& f
.cx
->jitEnabled
&& IsTraceableRecursion(f
.cx
)) {
1161 /* Top of script should always have traceId 0. */
1162 f
.u
.tracer
.traceId
= 0;
1163 f
.u
.tracer
.offs
= 0;
1165 /* cx.regs.sp is only set in InlineCall() if non-jittable. */
1166 JS_ASSERT(f
.cx
->regs
== &f
.regs
);
1169 * NB: Normally, the function address is returned, and the
1170 * caller's JIT'd code will set f.scriptedReturn and jump.
1171 * Invoking the tracer breaks this in two ways:
1172 * 1) f.scriptedReturn is not yet set, so when pushing new
1173 * inline frames, the call stack would get corrupted.
1174 * 2) If the tracer does not push new frames, but runs some
1175 * code, the JIT'd code to set f.scriptedReturn will not
1178 * So, a simple hack: set f.scriptedReturn now.
1180 f
.scriptedReturn
= GetReturnAddress(f
, f
.fp
);
1182 void *newRet
= InvokeTracer(f
, Record_Recursion
);
1185 * The tracer could have dropped us off anywhere. Hijack the
1186 * stub return address to JaegerFromTracer, which will restore
1190 void *ptr
= JS_FUNC_TO_DATA_PTR(void *, JaegerFromTracer
);
1191 f
.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr
)));
1201 if (!DoInvoke(f
, argc
, vp
))
1208 stubs::New(VMFrame
&f
, uint32 argc
)
1210 JSContext
*cx
= f
.cx
;
1211 Value
*vp
= f
.regs
.sp
- (argc
+ 2);
1213 if (vp
[0].isFunObj()) {
1214 JSObject
*funobj
= &vp
[0].asFunObj();
1215 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
1216 if (fun
->isInterpreted()) {
1217 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
);
1218 if (!funobj
->getProperty(cx
, id
, &vp
[1]))
1221 JSObject
*proto
= vp
[1].isObject() ? &vp
[1].asObject() : NULL
;
1222 JSObject
*obj2
= NewObject(cx
, &js_ObjectClass
, proto
, funobj
->getParent());
1226 if (fun
->u
.i
.script
->isEmpty()) {
1227 vp
[0].setNonFunObj(*obj2
);
1232 vp
[1].setNonFunObj(*obj2
);
1234 if (!InlineCall(f
, JSFRAME_CONSTRUCTING
, &pret
, argc
))
1240 if (!InvokeConstructor(cx
, InvokeArgsGuard(vp
, argc
), JS_FALSE
))
1248 stubs::DefFun(VMFrame
&f
, uint32 index
)
1251 JSObject
*pobj
, *obj2
;
1255 JSContext
*cx
= f
.cx
;
1256 JSStackFrame
*fp
= f
.fp
;
1257 JSScript
*script
= fp
->script
;
1260 * A top-level function defined in Global or Eval code (see ECMA-262
1261 * Ed. 3), or else a SpiderMonkey extension: a named function statement in
1262 * a compound statement (not at the top statement level of global code, or
1263 * at the top level of a function body).
1265 JSFunction
*fun
= script
->getFunction(index
);
1266 JSObject
*obj
= FUN_OBJECT(fun
);
1268 if (FUN_NULL_CLOSURE(fun
)) {
1270 * Even a null closure needs a parent for principals finding.
1271 * FIXME: bug 476950, although debugger users may also demand some kind
1272 * of scope link for debugger-assisted eval-in-frame.
1274 obj2
= fp
->scopeChainObj();
1276 JS_ASSERT(!FUN_FLAT_CLOSURE(fun
));
1279 * Inline js_GetScopeChain a bit to optimize for the case of a
1280 * top-level function.
1282 if (!fp
->blockChain
) {
1283 obj2
= fp
->scopeChainObj();
1285 obj2
= js_GetScopeChain(cx
, fp
);
1292 * If static link is not current scope, clone fun's object to link to the
1293 * current scope via parent. We do this to enable sharing of compiled
1294 * functions among multiple equivalent scopes, amortizing the cost of
1295 * compilation over a number of executions. Examples include XUL scripts
1296 * and event handlers shared among Firefox or other Mozilla app chrome
1297 * windows, and user-defined JS functions precompiled and then shared among
1298 * requests in server-side JS.
1300 if (obj
->getParent() != obj2
) {
1301 obj
= CloneFunctionObject(cx
, fun
, obj2
);
1307 * Protect obj from any GC hiding below JSObject::setProperty or
1308 * JSObject::defineProperty. All paths from here must flow through the
1309 * fp->scopeChain code below the parent->defineProperty call.
1311 MUST_FLOW_THROUGH("restore_scope");
1312 fp
->setScopeChainObj(obj
);
1315 rval
.setFunObj(*obj
);
1318 * ECMA requires functions defined when entering Eval code to be
1322 attrs
= (fp
->flags
& JSFRAME_EVAL
)
1324 : JSPROP_ENUMERATE
| JSPROP_PERMANENT
;
1327 * Load function flags that are also property attributes. Getters and
1328 * setters do not need a slot, their value is stored elsewhere in the
1329 * property itself, not in obj slots.
1331 PropertyOp getter
, setter
;
1334 getter
= setter
= PropertyStub
;
1335 flags
= JSFUN_GSFLAG2ATTR(fun
->flags
);
1337 /* Function cannot be both getter a setter. */
1338 JS_ASSERT(flags
== JSPROP_GETTER
|| flags
== JSPROP_SETTER
);
1339 attrs
|= flags
| JSPROP_SHARED
;
1340 rval
.setUndefined();
1341 if (flags
== JSPROP_GETTER
)
1342 getter
= CastAsPropertyOp(obj
);
1344 setter
= CastAsPropertyOp(obj
);
1352 * We define the function as a property of the variable object and not the
1353 * current scope chain even for the case of function expression statements
1354 * and functions defined by eval inside let or with blocks.
1356 parent
= fp
->varobj(cx
);
1360 * Check for a const property of the same name -- or any kind of property
1361 * if executing with the strict option. We check here at runtime as well
1362 * as at compile-time, to handle eval as well as multiple HTML script tags.
1364 id
= ATOM_TO_JSID(fun
->atom
);
1366 ok
= CheckRedeclaration(cx
, parent
, id
, attrs
, &pobj
, &prop
);
1371 * We deviate from 10.1.2 in ECMA 262 v3 and under eval use for function
1372 * declarations JSObject::setProperty, not JSObject::defineProperty, to
1373 * preserve the JSOP_PERMANENT attribute of existing properties and make
1374 * sure that such properties cannot be deleted.
1376 * We also use JSObject::setProperty for the existing properties of Call
1377 * objects with matching attributes to preserve the native getters and
1378 * setters that store the value of the property in the interpreter frame,
1381 doSet
= (attrs
== JSPROP_ENUMERATE
);
1382 JS_ASSERT_IF(doSet
, fp
->flags
& JSFRAME_EVAL
);
1384 if (parent
== pobj
&&
1385 parent
->getClass() == &js_CallClass
&&
1386 (old
= ((JSScopeProperty
*) prop
)->attributes(),
1387 !(old
& (JSPROP_GETTER
|JSPROP_SETTER
)) &&
1388 (old
& (JSPROP_ENUMERATE
|JSPROP_PERMANENT
)) == attrs
)) {
1390 * js_CheckRedeclaration must reject attempts to add a getter or
1391 * setter to an existing property without a getter or setter.
1393 JS_ASSERT(!(attrs
& ~(JSPROP_ENUMERATE
|JSPROP_PERMANENT
)));
1394 JS_ASSERT(!(old
& JSPROP_READONLY
));
1397 pobj
->dropProperty(cx
, prop
);
1400 ? parent
->setProperty(cx
, id
, &rval
)
1401 : parent
->defineProperty(cx
, id
, rval
, getter
, setter
, attrs
);
1404 /* Restore fp->scopeChain now that obj is defined in fp->callobj. */
1405 fp
->setScopeChainObj(obj2
);
1410 #define DEFAULT_VALUE(cx, n, hint, v) \
1412 JS_ASSERT(v.isObject()); \
1413 JS_ASSERT(v == regs.sp[n]); \
1414 if (!v.asObject().defaultValue(cx, hint, ®s.sp[n])) \
1419 #define RELATIONAL(OP) \
1421 JSContext *cx = f.cx; \
1422 JSFrameRegs ®s = f.regs; \
1423 Value rval = regs.sp[-1]; \
1424 Value lval = regs.sp[-2]; \
1426 if (lval.isObject()) \
1427 DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \
1428 if (rval.isObject()) \
1429 DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \
1430 if (BothString(lval, rval)) { \
1431 JSString *l = lval.asString(), *r = rval.asString(); \
1432 cond = js_CompareStrings(l, r) OP 0; \
1435 if (!ValueToNumber(cx, lval, &l) || \
1436 !ValueToNumber(cx, rval, &r)) { \
1439 cond = JSDOUBLE_COMPARE(l, OP, r, false); \
1441 regs.sp[-2].setBoolean(cond); \
1446 stubs::LessThan(VMFrame
&f
)
1452 stubs::LessEqual(VMFrame
&f
)
1458 stubs::GreaterThan(VMFrame
&f
)
1464 stubs::GreaterEqual(VMFrame
&f
)
1470 stubs::ValueToBoolean(VMFrame
&f
)
1472 return js_ValueToBoolean(f
.regs
.sp
[-1]);
1476 stubs::Not(VMFrame
&f
)
1478 JSBool b
= !js_ValueToBoolean(f
.regs
.sp
[-1]);
1479 f
.regs
.sp
[-1].setBoolean(b
);
1483 * Inline copy of jsops.cpp:EQUALITY_OP().
1484 * @param op true if for JSOP_EQ; false for JSOP_NE.
1485 * @param ifnan return value upon NaN comparison.
1488 InlineEqualityOp(VMFrame
&f
, bool op
, bool ifnan
)
1491 JSContext
*cx
= f
.cx
;
1492 JSFrameRegs
®s
= f
.regs
;
1494 Value rval
= regs
.sp
[-1];
1495 Value lval
= regs
.sp
[-2];
1500 #if JS_HAS_XML_SUPPORT
1501 /* Inline copy of jsops.cpp:XML_EQUALITY_OP() */
1502 if ((lval
.isNonFunObj() && lval
.asObject().isXML()) ||
1503 (rval
.isNonFunObj() && rval
.asObject().isXML())) {
1504 if (!js_TestXMLEquality(cx
, lval
, rval
, &jscond
))
1506 cond
= (jscond
== JS_TRUE
) == op
;
1508 #endif /* JS_HAS_XML_SUPPORT */
1510 if (SamePrimitiveTypeOrBothObjects(lval
, rval
)) {
1511 if (lval
.isString()) {
1512 JSString
*l
= lval
.asString();
1513 JSString
*r
= rval
.asString();
1514 cond
= js_EqualStrings(l
, r
) == op
;
1515 } else if (lval
.isDouble()) {
1516 double l
= lval
.asDouble();
1517 double r
= rval
.asDouble();
1519 cond
= JSDOUBLE_COMPARE(l
, ==, r
, ifnan
);
1521 cond
= JSDOUBLE_COMPARE(l
, !=, r
, ifnan
);
1524 /* jsops.cpp:EXTENDED_EQUALITY_OP() */
1526 if (lval
.isObject() &&
1527 (lobj
= &lval
.asObject()) &&
1528 ((clasp
= lobj
->getClass())->flags
& JSCLASS_IS_EXTENDED
) &&
1529 ((ExtendedClass
*)clasp
)->equality
) {
1530 if (!((ExtendedClass
*)clasp
)->equality(cx
, lobj
, lval
, &jscond
))
1532 cond
= (jscond
== JS_TRUE
) == op
;
1534 cond
= (lval
.asRawUint32() == rval
.asRawUint32()) == op
;
1538 if (lval
.isNullOrUndefined()) {
1539 cond
= rval
.isNullOrUndefined() == op
;
1540 } else if (rval
.isNullOrUndefined()) {
1543 if (lval
.isObject()) {
1544 if (!lval
.asObject().defaultValue(cx
, JSTYPE_VOID
, ®s
.sp
[-2]))
1549 if (rval
.isObject()) {
1550 if (!rval
.asObject().defaultValue(cx
, JSTYPE_VOID
, ®s
.sp
[-1]))
1555 if (BothString(lval
, rval
)) {
1556 JSString
*l
= lval
.asString();
1557 JSString
*r
= rval
.asString();
1558 cond
= js_EqualStrings(l
, r
) == op
;
1561 if (!ValueToNumber(cx
, lval
, &l
) ||
1562 !ValueToNumber(cx
, rval
, &r
)) {
1567 cond
= JSDOUBLE_COMPARE(l
, ==, r
, ifnan
);
1569 cond
= JSDOUBLE_COMPARE(l
, !=, r
, ifnan
);
1574 regs
.sp
[-2].setBoolean(cond
);
1579 stubs::Equal(VMFrame
&f
)
1581 return InlineEqualityOp(f
, true, false);
1585 stubs::NotEqual(VMFrame
&f
)
1587 return InlineEqualityOp(f
, false, true);
1591 DefaultValue(VMFrame
&f
, JSType hint
, Value
&v
, int n
)
1593 JS_ASSERT(v
.isObject());
1594 if (!v
.asObject().defaultValue(f
.cx
, hint
, &f
.regs
.sp
[n
]))
1601 stubs::Add(VMFrame
&f
)
1603 JSContext
*cx
= f
.cx
;
1604 JSFrameRegs
®s
= f
.regs
;
1605 Value rval
= regs
.sp
[-1];
1606 Value lval
= regs
.sp
[-2];
1608 if (BothInt32(lval
, rval
)) {
1609 int32_t l
= lval
.asInt32(), r
= rval
.asInt32();
1610 int32_t sum
= l
+ r
;
1612 if (JS_UNLIKELY(bool((l
^ sum
) & (r
^ sum
) & 0x80000000)))
1613 regs
.sp
[-1].setDouble(double(l
) + double(r
));
1615 regs
.sp
[-1].setInt32(sum
);
1617 #if JS_HAS_XML_SUPPORT
1618 if (lval
.isNonFunObj() && lval
.asObject().isXML() &&
1619 rval
.isNonFunObj() && rval
.asObject().isXML()) {
1620 if (!js_ConcatenateXML(cx
, &lval
.asObject(), &rval
.asObject(), &rval
))
1627 if (lval
.isObject() && !DefaultValue(f
, JSTYPE_VOID
, lval
, -2))
1629 if (rval
.isObject() && !DefaultValue(f
, JSTYPE_VOID
, rval
, -1))
1631 bool lIsString
, rIsString
;
1632 if ((lIsString
= lval
.isString()) | (rIsString
= rval
.isString())) {
1633 JSString
*lstr
, *rstr
;
1635 lstr
= lval
.asString();
1637 lstr
= js_ValueToString(cx
, lval
);
1640 regs
.sp
[-2].setString(lstr
);
1643 rstr
= rval
.asString();
1645 rstr
= js_ValueToString(cx
, rval
);
1648 regs
.sp
[-1].setString(rstr
);
1650 JSString
*str
= js_ConcatStrings(cx
, lstr
, rstr
);
1654 regs
.sp
[-1].setString(str
);
1657 if (!ValueToNumber(cx
, lval
, &l
) || !ValueToNumber(cx
, rval
, &r
))
1661 regs
.sp
[-1].setNumber(l
);
1668 stubs::Sub(VMFrame
&f
)
1670 JSContext
*cx
= f
.cx
;
1671 JSFrameRegs
®s
= f
.regs
;
1673 if (!ValueToNumber(cx
, regs
.sp
[-2], &d1
) ||
1674 !ValueToNumber(cx
, regs
.sp
[-1], &d2
)) {
1678 regs
.sp
[-2].setNumber(d
);
1682 stubs::Mul(VMFrame
&f
)
1684 JSContext
*cx
= f
.cx
;
1685 JSFrameRegs
®s
= f
.regs
;
1687 if (!ValueToNumber(cx
, regs
.sp
[-2], &d1
) ||
1688 !ValueToNumber(cx
, regs
.sp
[-1], &d2
)) {
1692 regs
.sp
[-2].setNumber(d
);
1696 stubs::Div(VMFrame
&f
)
1698 JSContext
*cx
= f
.cx
;
1699 JSRuntime
*rt
= cx
->runtime
;
1700 JSFrameRegs
®s
= f
.regs
;
1703 if (!ValueToNumber(cx
, regs
.sp
[-2], &d1
) ||
1704 !ValueToNumber(cx
, regs
.sp
[-1], &d2
)) {
1710 /* XXX MSVC miscompiles such that (NaN == 0) */
1711 if (JSDOUBLE_IS_NaN(d2
))
1715 if (d1
== 0 || JSDOUBLE_IS_NaN(d1
))
1717 else if (JSDOUBLE_IS_NEG(d1
) != JSDOUBLE_IS_NEG(d2
))
1718 vp
= &rt
->negativeInfinityValue
;
1720 vp
= &rt
->positiveInfinityValue
;
1724 regs
.sp
[-2].setNumber(d1
);
1729 stubs::Mod(VMFrame
&f
)
1731 JSContext
*cx
= f
.cx
;
1732 JSFrameRegs
®s
= f
.regs
;
1734 Value
&lref
= regs
.sp
[-2];
1735 Value
&rref
= regs
.sp
[-1];
1737 if (lref
.isInt32() && rref
.isInt32() &&
1738 (l
= lref
.asInt32()) >= 0 && (r
= rref
.asInt32()) > 0) {
1739 int32_t mod
= l
% r
;
1740 regs
.sp
[-2].setInt32(mod
);
1743 if (!ValueToNumber(cx
, regs
.sp
[-2], &d1
) ||
1744 !ValueToNumber(cx
, regs
.sp
[-1], &d2
)) {
1748 regs
.sp
[-2].setDouble(js_NaN
);
1750 d1
= js_fmod(d1
, d2
);
1751 regs
.sp
[-2].setDouble(d1
);
1756 JSObject
*JS_FASTCALL
1757 stubs::NewArray(VMFrame
&f
, uint32 len
)
1759 JSObject
*obj
= js_NewArrayObject(f
.cx
, len
, f
.regs
.sp
- len
, JS_TRUE
);
1766 stubs::Interrupt(VMFrame
&f
)
1768 if (!js_HandleExecutionInterrupt(f
.cx
)) {
1774 stubs::This(VMFrame
&f
)
1776 if (!f
.fp
->getThisObject(f
.cx
))
1778 f
.regs
.sp
[0] = f
.fp
->thisv
;
1782 stubs::Neg(VMFrame
&f
)
1785 if (!ValueToNumber(f
.cx
, f
.regs
.sp
[-1], &d
))
1788 f
.regs
.sp
[-1].setNumber(d
);
1792 stubs::ObjToStr(VMFrame
&f
)
1794 const Value
&ref
= f
.regs
.sp
[-1];
1795 if (ref
.isObject()) {
1796 JSString
*str
= js_ValueToString(f
.cx
, ref
);
1799 f
.regs
.sp
[-1].setString(str
);
1803 JSObject
* JS_FASTCALL
1804 stubs::NewInitArray(VMFrame
&f
)
1806 JSObject
*obj
= js_NewArrayObject(f
.cx
, 0, NULL
);
1812 JSObject
* JS_FASTCALL
1813 stubs::NewInitObject(VMFrame
&f
, uint32 empty
)
1815 JSContext
*cx
= f
.cx
;
1817 JSObject
*obj
= NewObject(cx
, &js_ObjectClass
, NULL
, NULL
);
1822 JS_LOCK_OBJ(cx
, obj
);
1823 JSScope
*scope
= js_GetMutableScope(cx
, obj
);
1825 JS_UNLOCK_OBJ(cx
, obj
);
1830 * We cannot assume that js_GetMutableScope above creates a scope
1831 * owned by cx and skip JS_UNLOCK_SCOPE. A new object debugger
1832 * hook may add properties to the newly created object, suspend
1833 * the current request and share the object with other threads.
1835 JS_UNLOCK_SCOPE(cx
, scope
);
1842 stubs::EndInit(VMFrame
&f
)
1844 JS_ASSERT(f
.regs
.sp
- f
.fp
->base() >= 1);
1845 const Value
&lref
= f
.regs
.sp
[-1];
1846 JS_ASSERT(lref
.isObject());
1847 f
.cx
->weakRoots
.finalizableNewborns
[FINALIZE_OBJECT
] = &lref
.asObject();
1851 stubs::InitElem(VMFrame
&f
, uint32 last
)
1853 JSContext
*cx
= f
.cx
;
1854 JSFrameRegs
®s
= f
.regs
;
1856 /* Pop the element's value into rval. */
1857 JS_ASSERT(regs
.sp
- f
.fp
->base() >= 3);
1858 const Value
&rref
= regs
.sp
[-1];
1860 /* Find the object being initialized at top of stack. */
1861 const Value
&lref
= regs
.sp
[-3];
1862 JS_ASSERT(lref
.isObject());
1863 JSObject
*obj
= &lref
.asObject();
1865 /* Fetch id now that we have obj. */
1867 const Value
&idval
= regs
.sp
[-2];
1868 if (!FetchElementId(f
, obj
, idval
, id
, ®s
.sp
[-2]))
1872 * Check for property redeclaration strict warning (we may be in an object
1873 * initialiser, not an array initialiser).
1875 if (!CheckRedeclaration(cx
, obj
, id
, JSPROP_INITIALIZER
, NULL
, NULL
))
1879 * If rref is a hole, do not call JSObject::defineProperty. In this case,
1880 * obj must be an array, so if the current op is the last element
1881 * initialiser, set the array length to one greater than id.
1883 if (rref
.isMagic(JS_ARRAY_HOLE
)) {
1884 JS_ASSERT(obj
->isArray());
1885 JS_ASSERT(JSID_IS_INT(id
));
1886 JS_ASSERT(jsuint(JSID_TO_INT(id
)) < JS_ARGS_LENGTH_MAX
);
1887 if (last
&& !js_SetLengthProperty(cx
, obj
, (jsuint
) (JSID_TO_INT(id
) + 1)))
1890 if (!obj
->defineProperty(cx
, id
, rref
, NULL
, NULL
, JSPROP_ENUMERATE
))
1896 stubs::GetUpvar(VMFrame
&f
, uint32 cookie
)
1898 /* :FIXME: We can do better, this stub isn't needed. */
1899 uint32 staticLevel
= f
.fp
->script
->staticLevel
;
1900 f
.regs
.sp
[0] = js_GetUpvar(f
.cx
, staticLevel
, cookie
);
1903 JSObject
* JS_FASTCALL
1904 stubs::DefLocalFun(VMFrame
&f
, JSFunction
*fun
)
1907 * Define a local function (i.e., one nested at the top level of another
1908 * function), parented by the current scope chain, stored in a local
1909 * variable slot that the compiler allocated. This is an optimization over
1910 * JSOP_DEFFUN that avoids requiring a call object for the outer function's
1913 JS_ASSERT(fun
->isInterpreted());
1914 JS_ASSERT(!FUN_FLAT_CLOSURE(fun
));
1915 JSObject
*obj
= FUN_OBJECT(fun
);
1917 if (FUN_NULL_CLOSURE(fun
)) {
1918 obj
= CloneFunctionObject(f
.cx
, fun
, f
.fp
->scopeChainObj());
1922 JSObject
*parent
= js_GetScopeChain(f
.cx
, f
.fp
);
1926 if (obj
->getParent() != parent
) {
1927 obj
= CloneFunctionObject(f
.cx
, fun
, parent
);
1936 JSObject
* JS_FASTCALL
1937 stubs::RegExp(VMFrame
&f
, JSObject
*regex
)
1940 * Push a regexp object cloned from the regexp literal object mapped by the
1941 * bytecode at pc. ES5 finally fixed this bad old ES3 design flaw which was
1942 * flouted by many browser-based implementations.
1944 * We avoid the js_GetScopeChain call here and pass fp->scopeChain as
1945 * js_GetClassPrototype uses the latter only to locate the global.
1948 if (!js_GetClassPrototype(f
.cx
, f
.fp
->scopeChainObj(), JSProto_RegExp
, &proto
))
1951 JSObject
*obj
= js_CloneRegExpObject(f
.cx
, regex
, proto
);
1957 JSObject
* JS_FASTCALL
1958 stubs::Lambda(VMFrame
&f
, JSFunction
*fun
)
1960 JSObject
*obj
= FUN_OBJECT(fun
);
1963 if (FUN_NULL_CLOSURE(fun
)) {
1964 parent
= f
.fp
->scopeChainObj();
1966 parent
= js_GetScopeChain(f
.cx
, f
.fp
);
1971 obj
= CloneFunctionObject(f
.cx
, fun
, parent
);
1978 /* Test whether v is an int in the range [-2^31 + 1, 2^31 - 2] */
1979 static JS_ALWAYS_INLINE
bool
1980 CanIncDecWithoutOverflow(int32_t i
)
1982 return (i
> JSVAL_INT_MIN
) && (i
< JSVAL_INT_MAX
);
1985 template <int32 N
, bool POST
>
1987 ObjIncOp(VMFrame
&f
, JSObject
*obj
, jsid id
)
1989 JSContext
*cx
= f
.cx
;
1990 JSStackFrame
*fp
= f
.fp
;
1992 f
.regs
.sp
[0].setNull();
1994 if (!obj
->getProperty(cx
, id
, &f
.regs
.sp
[-1]))
1997 Value
&ref
= f
.regs
.sp
[-1];
1999 if (JS_LIKELY(ref
.isInt32() && CanIncDecWithoutOverflow(tmp
= ref
.asInt32()))) {
2001 ref
.asInt32Ref() = tmp
+ N
;
2003 ref
.asInt32Ref() = tmp
+= N
;
2004 fp
->flags
|= JSFRAME_ASSIGNING
;
2005 JSBool ok
= obj
->setProperty(cx
, id
, &ref
);
2006 fp
->flags
&= ~JSFRAME_ASSIGNING
;
2011 * We must set regs.sp[-1] to tmp for both post and pre increments
2012 * as the setter overwrites regs.sp[-1].
2018 if (!ValueToNumber(cx
, ref
, &d
))
2028 fp
->flags
|= JSFRAME_ASSIGNING
;
2029 JSBool ok
= obj
->setProperty(cx
, id
, &v
);
2030 fp
->flags
&= ~JSFRAME_ASSIGNING
;
2038 template <int32 N
, bool POST
>
2040 NameIncDec(VMFrame
&f
, JSObject
*obj
, JSAtom
*origAtom
)
2042 JSContext
*cx
= f
.cx
;
2047 PropertyCacheEntry
*entry
;
2048 JS_PROPERTY_CACHE(cx
).test(cx
, f
.regs
.pc
, obj
, obj2
, entry
, atom
);
2050 if (obj
== obj2
&& entry
->vword
.isSlot()) {
2051 uint32 slot
= entry
->vword
.toSlot();
2052 JS_ASSERT(slot
< obj
->scope()->freeslot
);
2053 Value
&rref
= obj
->getSlotRef(slot
);
2055 if (JS_LIKELY(rref
.isInt32() && CanIncDecWithoutOverflow(tmp
= rref
.asInt32()))) {
2056 int32_t inc
= tmp
+ N
;
2059 rref
.asInt32Ref() = inc
;
2060 f
.regs
.sp
[0].setInt32(tmp
);
2067 jsid id
= ATOM_TO_JSID(atom
);
2068 if (!js_FindPropertyHelper(cx
, id
, true, &obj
, &obj2
, &prop
))
2071 ReportAtomNotDefined(cx
, atom
);
2074 obj2
->dropProperty(cx
, prop
);
2075 return ObjIncOp
<N
, POST
>(f
, obj
, id
);
2079 stubs::PropInc(VMFrame
&f
, JSAtom
*atom
)
2081 JSObject
*obj
= ValueToObject(f
.cx
, &f
.regs
.sp
[-1]);
2084 if (!ObjIncOp
<1, true>(f
, obj
, ATOM_TO_JSID(atom
)))
2086 f
.regs
.sp
[-2] = f
.regs
.sp
[-1];
2090 stubs::PropDec(VMFrame
&f
, JSAtom
*atom
)
2092 JSObject
*obj
= ValueToObject(f
.cx
, &f
.regs
.sp
[-1]);
2095 if (!ObjIncOp
<-1, true>(f
, obj
, ATOM_TO_JSID(atom
)))
2097 f
.regs
.sp
[-2] = f
.regs
.sp
[-1];
2101 stubs::IncProp(VMFrame
&f
, JSAtom
*atom
)
2103 JSObject
*obj
= ValueToObject(f
.cx
, &f
.regs
.sp
[-1]);
2106 if (!ObjIncOp
<1, false>(f
, obj
, ATOM_TO_JSID(atom
)))
2108 f
.regs
.sp
[-2] = f
.regs
.sp
[-1];
2112 stubs::DecProp(VMFrame
&f
, JSAtom
*atom
)
2114 JSObject
*obj
= ValueToObject(f
.cx
, &f
.regs
.sp
[-1]);
2117 if (!ObjIncOp
<-1, false>(f
, obj
, ATOM_TO_JSID(atom
)))
2119 f
.regs
.sp
[-2] = f
.regs
.sp
[-1];
2123 stubs::ElemInc(VMFrame
&f
)
2125 JSObject
*obj
= ValueToObject(f
.cx
, &f
.regs
.sp
[-2]);
2129 if (!FetchElementId(f
, obj
, f
.regs
.sp
[-1], id
, &f
.regs
.sp
[-1]))
2131 if (!ObjIncOp
<1, true>(f
, obj
, id
))
2133 f
.regs
.sp
[-3] = f
.regs
.sp
[-1];
2137 stubs::ElemDec(VMFrame
&f
)
2139 JSObject
*obj
= ValueToObject(f
.cx
, &f
.regs
.sp
[-2]);
2143 if (!FetchElementId(f
, obj
, f
.regs
.sp
[-1], id
, &f
.regs
.sp
[-1]))
2145 if (!ObjIncOp
<-1, true>(f
, obj
, id
))
2147 f
.regs
.sp
[-3] = f
.regs
.sp
[-1];
2151 stubs::IncElem(VMFrame
&f
)
2153 JSObject
*obj
= ValueToObject(f
.cx
, &f
.regs
.sp
[-2]);
2157 if (!FetchElementId(f
, obj
, f
.regs
.sp
[-1], id
, &f
.regs
.sp
[-1]))
2159 if (!ObjIncOp
<1, false>(f
, obj
, id
))
2161 f
.regs
.sp
[-3] = f
.regs
.sp
[-1];
2165 stubs::DecElem(VMFrame
&f
)
2167 JSObject
*obj
= ValueToObject(f
.cx
, &f
.regs
.sp
[-2]);
2171 if (!FetchElementId(f
, obj
, f
.regs
.sp
[-1], id
, &f
.regs
.sp
[-1]))
2173 if (!ObjIncOp
<-1, false>(f
, obj
, id
))
2175 f
.regs
.sp
[-3] = f
.regs
.sp
[-1];
2179 stubs::NameInc(VMFrame
&f
, JSAtom
*atom
)
2181 JSObject
*obj
= f
.fp
->scopeChainObj();
2182 if (!NameIncDec
<1, true>(f
, obj
, atom
))
2187 stubs::NameDec(VMFrame
&f
, JSAtom
*atom
)
2189 JSObject
*obj
= f
.fp
->scopeChainObj();
2190 if (!NameIncDec
<-1, true>(f
, obj
, atom
))
2195 stubs::IncName(VMFrame
&f
, JSAtom
*atom
)
2197 JSObject
*obj
= f
.fp
->scopeChainObj();
2198 if (!NameIncDec
<1, false>(f
, obj
, atom
))
2203 stubs::DecName(VMFrame
&f
, JSAtom
*atom
)
2205 JSObject
*obj
= f
.fp
->scopeChainObj();
2206 if (!NameIncDec
<-1, false>(f
, obj
, atom
))
2211 stubs::GlobalNameInc(VMFrame
&f
, JSAtom
*atom
)
2213 JSObject
*obj
= f
.fp
->scopeChainObj()->getGlobal();
2214 if (!NameIncDec
<1, true>(f
, obj
, atom
))
2219 stubs::GlobalNameDec(VMFrame
&f
, JSAtom
*atom
)
2221 JSObject
*obj
= f
.fp
->scopeChainObj()->getGlobal();
2222 if (!NameIncDec
<-1, true>(f
, obj
, atom
))
2227 stubs::IncGlobalName(VMFrame
&f
, JSAtom
*atom
)
2229 JSObject
*obj
= f
.fp
->scopeChainObj()->getGlobal();
2230 if (!NameIncDec
<1, false>(f
, obj
, atom
))
2235 stubs::DecGlobalName(VMFrame
&f
, JSAtom
*atom
)
2237 JSObject
*obj
= f
.fp
->scopeChainObj()->getGlobal();
2238 if (!NameIncDec
<-1, false>(f
, obj
, atom
))
2242 static bool JS_FASTCALL
2243 InlineGetProp(VMFrame
&f
)
2245 JSContext
*cx
= f
.cx
;
2246 JSFrameRegs
®s
= f
.regs
;
2248 Value
*vp
= &f
.regs
.sp
[-1];
2249 JSObject
*obj
= ValueToObject(f
.cx
, vp
);
2256 * We do not impose the method read barrier if in an imacro,
2257 * assuming any property gets it does (e.g., for 'toString'
2258 * from JSOP_NEW) will not be leaked to the calling script.
2260 JSObject
*aobj
= js_GetProtoIfDenseArray(obj
);
2262 PropertyCacheEntry
*entry
;
2265 JS_PROPERTY_CACHE(cx
).test(cx
, regs
.pc
, aobj
, obj2
, entry
, atom
);
2267 if (entry
->vword
.isFunObj()) {
2268 rval
.setFunObj(entry
->vword
.toFunObj());
2269 } else if (entry
->vword
.isSlot()) {
2270 uint32 slot
= entry
->vword
.toSlot();
2271 JS_ASSERT(slot
< obj2
->scope()->freeslot
);
2272 rval
= obj2
->lockedGetSlot(slot
);
2274 JS_ASSERT(entry
->vword
.isSprop());
2275 JSScopeProperty
*sprop
= entry
->vword
.toSprop();
2276 NATIVE_GET(cx
, obj
, obj2
, sprop
,
2277 f
.fp
->imacpc
? JSGET_NO_METHOD_BARRIER
: JSGET_METHOD_BARRIER
,
2278 &rval
, return false);
2283 jsid id
= ATOM_TO_JSID(atom
);
2284 if (JS_LIKELY(aobj
->map
->ops
->getProperty
== js_GetProperty
)
2285 ? !js_GetPropertyHelper(cx
, obj
, id
,
2287 ? JSGET_CACHE_RESULT
| JSGET_NO_METHOD_BARRIER
2288 : JSGET_CACHE_RESULT
| JSGET_METHOD_BARRIER
,
2290 : !obj
->getProperty(cx
, id
, &rval
)) {
2300 stubs::GetProp(VMFrame
&f
)
2302 if (!InlineGetProp(f
))
2307 stubs::CallProp(VMFrame
&f
, JSAtom
*origAtom
)
2309 JSContext
*cx
= f
.cx
;
2310 JSFrameRegs
®s
= f
.regs
;
2316 if (lval
.isObject()) {
2319 JSProtoKey protoKey
;
2320 if (lval
.isString()) {
2321 protoKey
= JSProto_String
;
2322 } else if (lval
.isNumber()) {
2323 protoKey
= JSProto_Number
;
2324 } else if (lval
.isBoolean()) {
2325 protoKey
= JSProto_Boolean
;
2327 JS_ASSERT(lval
.isNull() || lval
.isUndefined());
2328 js_ReportIsNullOrUndefined(cx
, -1, lval
, NULL
);
2332 if (!js_GetClassPrototype(cx
, NULL
, protoKey
, &pobj
))
2334 objv
.setNonFunObj(*pobj
);
2337 JSObject
*aobj
= js_GetProtoIfDenseArray(&objv
.asObject());
2340 PropertyCacheEntry
*entry
;
2343 JS_PROPERTY_CACHE(cx
).test(cx
, regs
.pc
, aobj
, obj2
, entry
, atom
);
2345 if (entry
->vword
.isFunObj()) {
2346 rval
.setFunObj(entry
->vword
.toFunObj());
2347 } else if (entry
->vword
.isSlot()) {
2348 uint32 slot
= entry
->vword
.toSlot();
2349 JS_ASSERT(slot
< obj2
->scope()->freeslot
);
2350 rval
= obj2
->lockedGetSlot(slot
);
2352 JS_ASSERT(entry
->vword
.isSprop());
2353 JSScopeProperty
*sprop
= entry
->vword
.toSprop();
2354 NATIVE_GET(cx
, &objv
.asObject(), obj2
, sprop
, JSGET_NO_METHOD_BARRIER
, &rval
,
2364 * Cache miss: use the immediate atom that was loaded for us under
2365 * PropertyCache::test.
2368 id
= ATOM_TO_JSID(origAtom
);
2371 regs
.sp
[-1].setNull();
2372 if (lval
.isObject()) {
2373 if (!js_GetMethod(cx
, &objv
.asObject(), id
,
2374 JS_LIKELY(aobj
->map
->ops
->getProperty
== js_GetProperty
)
2375 ? JSGET_CACHE_RESULT
| JSGET_NO_METHOD_BARRIER
2376 : JSGET_NO_METHOD_BARRIER
,
2383 JS_ASSERT(objv
.asObject().map
->ops
->getProperty
== js_GetProperty
);
2384 if (!js_GetPropertyHelper(cx
, &objv
.asObject(), id
,
2385 JSGET_CACHE_RESULT
| JSGET_NO_METHOD_BARRIER
,
2394 /* Wrap primitive lval in object clothing if necessary. */
2395 if (lval
.isPrimitive()) {
2396 /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */
2397 if (!rval
.isFunObj() ||
2398 !PrimitiveValue::test(GET_FUNCTION_PRIVATE(cx
, &rval
.asFunObj()),
2400 if (!js_PrimitiveToObject(cx
, ®s
.sp
[-1]))
2404 #if JS_HAS_NO_SUCH_METHOD
2405 if (JS_UNLIKELY(rval
.isUndefined())) {
2406 regs
.sp
[-2].setString(ATOM_TO_STRING(origAtom
));
2407 if (!js_OnUnknownMethod(cx
, regs
.sp
- 2))
2414 stubs::Length(VMFrame
&f
)
2416 JSFrameRegs
®s
= f
.regs
;
2417 Value
*vp
= ®s
.sp
[-1];
2419 if (vp
->isString()) {
2420 vp
->setInt32(vp
->asString()->length());
2422 } else if (vp
->isObject()) {
2423 JSObject
*obj
= &vp
->asObject();
2424 if (obj
->isArray()) {
2425 jsuint length
= obj
->getArrayLength();
2426 regs
.sp
[-1].setDouble(length
);
2428 } else if (obj
->isArguments() && !obj
->isArgsLengthOverridden()) {
2429 uint32 length
= obj
->getArgsLength();
2430 JS_ASSERT(length
< INT32_MAX
);
2431 regs
.sp
[-1].setInt32(int32_t(length
));
2436 if (!InlineGetProp(f
))
2441 stubs::Iter(VMFrame
&f
, uint32 flags
)
2443 if (!js_ValueToIterator(f
.cx
, flags
, &f
.regs
.sp
[-1]))
2445 JS_ASSERT(!f
.regs
.sp
[-1].isPrimitive());
2449 InitPropOrMethod(VMFrame
&f
, JSAtom
*atom
, JSOp op
)
2451 JSContext
*cx
= f
.cx
;
2452 JSRuntime
*rt
= cx
->runtime
;
2453 JSFrameRegs
®s
= f
.regs
;
2455 /* Load the property's initial value into rval. */
2456 JS_ASSERT(regs
.sp
- f
.fp
->base() >= 2);
2460 /* Load the object being initialized into lval/obj. */
2461 JSObject
*obj
= ®s
.sp
[-2].asObject();
2462 JS_ASSERT(obj
->isNative());
2463 JS_ASSERT(!obj
->getClass()->reserveSlots
);
2465 JSScope
*scope
= obj
->scope();
2468 * Probe the property cache.
2470 * We can not assume that the object created by JSOP_NEWINIT is still
2471 * single-threaded as the debugger can access it from other threads.
2474 * On a hit, if the cached sprop has a non-default setter, it must be
2475 * __proto__. If sprop->parent != scope->lastProperty(), there is a
2476 * repeated property name. The fast path does not handle these two cases.
2478 PropertyCacheEntry
*entry
;
2479 JSScopeProperty
*sprop
;
2480 if (CX_OWNS_OBJECT_TITLE(cx
, obj
) &&
2481 JS_PROPERTY_CACHE(cx
).testForInit(rt
, regs
.pc
, obj
, scope
, &sprop
, &entry
) &&
2482 sprop
->hasDefaultSetter() &&
2483 sprop
->parent
== scope
->lastProperty())
2485 /* Fast path. Property cache hit. */
2486 uint32 slot
= sprop
->slot
;
2487 JS_ASSERT(slot
== scope
->freeslot
);
2488 if (slot
< obj
->numSlots()) {
2491 if (!js_AllocSlot(cx
, obj
, &slot
))
2493 JS_ASSERT(slot
== sprop
->slot
);
2496 JS_ASSERT(!scope
->lastProperty() ||
2497 scope
->shape
== scope
->lastProperty()->shape
);
2499 JSScopeProperty
*sprop2
=
2500 scope
->addProperty(cx
, sprop
->id
, sprop
->getter(), sprop
->setter(), slot
,
2501 sprop
->attributes(), sprop
->getFlags(), sprop
->shortid
);
2503 js_FreeSlot(cx
, obj
, slot
);
2506 JS_ASSERT(sprop2
== sprop
);
2508 JS_ASSERT(!scope
->isSharedEmpty());
2509 scope
->extend(cx
, sprop
);
2513 * No method change check here because here we are adding a new
2514 * property, not updating an existing slot's value that might
2515 * contain a method of a branded scope.
2517 obj
->lockedSetSlot(slot
, rval
);
2519 PCMETER(JS_PROPERTY_CACHE(cx
).inipcmisses
++);
2521 /* Get the immediate property name into id. */
2522 jsid id
= ATOM_TO_JSID(atom
);
2524 /* Set the property named by obj[id] to rval. */
2525 if (!CheckRedeclaration(cx
, obj
, id
, JSPROP_INITIALIZER
, NULL
, NULL
))
2528 uintN defineHow
= (op
== JSOP_INITMETHOD
)
2529 ? JSDNP_CACHE_RESULT
| JSDNP_SET_METHOD
2530 : JSDNP_CACHE_RESULT
;
2531 if (!(JS_UNLIKELY(atom
== cx
->runtime
->atomState
.protoAtom
)
2532 ? js_SetPropertyHelper(cx
, obj
, id
, defineHow
, &rval
)
2533 : js_DefineNativeProperty(cx
, obj
, id
, rval
, NULL
, NULL
,
2534 JSPROP_ENUMERATE
, 0, 0, NULL
,
2542 stubs::InitProp(VMFrame
&f
, JSAtom
*atom
)
2544 InitPropOrMethod(f
, atom
, JSOP_INITPROP
);
2548 stubs::InitMethod(VMFrame
&f
, JSAtom
*atom
)
2550 InitPropOrMethod(f
, atom
, JSOP_INITMETHOD
);
2554 stubs::IterNext(VMFrame
&f
)
2556 JS_ASSERT(f
.regs
.sp
- 1 >= f
.fp
->base());
2557 JS_ASSERT(f
.regs
.sp
[-1].isObject());
2559 JSObject
*iterobj
= &f
.regs
.sp
[-1].asObject();
2560 f
.regs
.sp
[0].setNull();
2562 if (!js_IteratorNext(f
.cx
, iterobj
, &f
.regs
.sp
[-1]))
2567 stubs::IterMore(VMFrame
&f
)
2569 JS_ASSERT(f
.regs
.sp
- 1 >= f
.fp
->base());
2570 JS_ASSERT(f
.regs
.sp
[-1].isObject());
2573 JSObject
*iterobj
= &f
.regs
.sp
[-1].asObject();
2574 if (!js_IteratorMore(f
.cx
, iterobj
, &v
))
2577 return v
.asBoolean();
2581 stubs::EndIter(VMFrame
&f
)
2583 JS_ASSERT(f
.regs
.sp
- 1 >= f
.fp
->base());
2584 if (!js_CloseIterator(f
.cx
, f
.regs
.sp
[-1]))
2588 JSString
* JS_FASTCALL
2589 stubs::TypeOf(VMFrame
&f
)
2591 const Value
&ref
= f
.regs
.sp
[-1];
2592 JSType type
= JS_TypeOfValue(f
.cx
, Jsvalify(ref
));
2593 JSAtom
*atom
= f
.cx
->runtime
->atomState
.typeAtoms
[type
];
2594 return ATOM_TO_STRING(atom
);
2598 stubs::StrictEq(VMFrame
&f
)
2600 const Value
&rhs
= f
.regs
.sp
[-1];
2601 const Value
&lhs
= f
.regs
.sp
[-2];
2602 return StrictlyEqual(f
.cx
, lhs
, rhs
) == true;
2606 stubs::StrictNe(VMFrame
&f
)
2608 const Value
&rhs
= f
.regs
.sp
[-1];
2609 const Value
&lhs
= f
.regs
.sp
[-2];
2610 return StrictlyEqual(f
.cx
, lhs
, rhs
) != true;
2613 JSString
* JS_FASTCALL
2614 stubs::ConcatN(VMFrame
&f
, uint32 argc
)
2616 JSCharBuffer
buf(f
.cx
);
2617 for (Value
*vp
= f
.regs
.sp
- argc
; vp
< f
.regs
.sp
; vp
++) {
2618 JS_ASSERT(vp
->isPrimitive());
2619 if (!js_ValueToCharBuffer(f
.cx
, *vp
, buf
))
2622 JSString
*str
= js_NewStringFromCharBuffer(f
.cx
, buf
);
2629 stubs::Throw(VMFrame
&f
)
2631 JSContext
*cx
= f
.cx
;
2633 JS_ASSERT(!cx
->throwing
);
2634 cx
->throwing
= JS_TRUE
;
2635 cx
->exception
= f
.regs
.sp
[-1];
2639 JSObject
* JS_FASTCALL
2640 stubs::FlatLambda(VMFrame
&f
, JSFunction
*fun
)
2642 JSObject
*obj
= js_NewFlatClosure(f
.cx
, fun
);
2649 stubs::Arguments(VMFrame
&f
)
2652 if (!js_GetArgsValue(f
.cx
, f
.fp
, &f
.regs
.sp
[-1]))
2657 stubs::InstanceOf(VMFrame
&f
)
2659 JSContext
*cx
= f
.cx
;
2660 JSFrameRegs
®s
= f
.regs
;
2662 const Value
&rref
= regs
.sp
[-1];
2664 if (rref
.isPrimitive() ||
2665 !(obj
= &rref
.asObject())->map
->ops
->hasInstance
) {
2666 js_ReportValueError(cx
, JSMSG_BAD_INSTANCEOF_RHS
,
2670 const Value
&lref
= regs
.sp
[-2];
2671 JSBool cond
= JS_FALSE
;
2672 if (!obj
->map
->ops
->hasInstance(cx
, obj
, lref
, &cond
))
2679 stubs::ArgCnt(VMFrame
&f
)
2681 JSContext
*cx
= f
.cx
;
2682 JSRuntime
*rt
= cx
->runtime
;
2683 JSStackFrame
*fp
= f
.fp
;
2685 jsid id
= ATOM_TO_JSID(rt
->atomState
.lengthAtom
);
2687 if (!js_GetArgsProperty(cx
, fp
, id
, &f
.regs
.sp
[-1]))
2692 stubs::EnterBlock(VMFrame
&f
, JSObject
*obj
)
2694 JSFrameRegs
®s
= f
.regs
;
2695 JSStackFrame
*fp
= f
.fp
;
2697 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj
));
2698 JS_ASSERT(fp
->base() + OBJ_BLOCK_DEPTH(cx
, obj
) == regs
.sp
);
2699 Value
*vp
= regs
.sp
+ OBJ_BLOCK_COUNT(cx
, obj
);
2700 JS_ASSERT(regs
.sp
< vp
);
2701 JS_ASSERT(vp
<= fp
->slots() + fp
->script
->nslots
);
2702 SetValueRangeToUndefined(regs
.sp
, vp
);
2706 JSContext
*cx
= f
.cx
;
2707 JS_ASSERT(fp
->blockChain
== obj
->getParent());
2710 * The young end of fp->scopeChain may omit blocks if we haven't closed
2711 * over them, but if there are any closure blocks on fp->scopeChain, they'd
2712 * better be (clones of) ancestors of the block we're entering now;
2713 * anything else we should have popped off fp->scopeChain when we left its
2716 JSObject
*obj2
= fp
->scopeChainObj();
2718 while ((clasp
= obj2
->getClass()) == &js_WithClass
)
2719 obj2
= obj2
->getParent();
2720 if (clasp
== &js_BlockClass
&&
2721 obj2
->getPrivate() == js_FloatingFrameIfGenerator(cx
, fp
)) {
2722 JSObject
*youngestProto
= obj2
->getProto();
2723 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(youngestProto
));
2724 JSObject
*parent
= obj
;
2725 while ((parent
= parent
->getParent()) != youngestProto
)
2730 fp
->blockChain
= obj
;
2734 stubs::LeaveBlock(VMFrame
&f
)
2736 JSContext
*cx
= f
.cx
;
2737 JSStackFrame
*fp
= f
.fp
;
2740 JS_ASSERT(fp
->blockChain
->getClass() == &js_BlockClass
);
2741 uintN blockDepth
= OBJ_BLOCK_DEPTH(cx
, fp
->blockChain
);
2743 JS_ASSERT(blockDepth
<= StackDepth(fp
->script
));
2746 * If we're about to leave the dynamic scope of a block that has been
2747 * cloned onto fp->scopeChain, clear its private data, move its locals from
2748 * the stack into the clone, and pop it off the chain.
2750 JSObject
*obj
= fp
->scopeChainObj();
2751 if (obj
->getProto() == fp
->blockChain
) {
2752 JS_ASSERT(obj
->getClass() == &js_BlockClass
);
2753 if (!js_PutBlockObject(cx
, JS_TRUE
))
2757 /* Pop the block chain, too. */
2758 fp
->blockChain
= fp
->blockChain
->getParent();
2762 stubs::LookupSwitch(VMFrame
&f
, jsbytecode
*pc
)
2764 jsbytecode
*jpc
= pc
;
2765 JSScript
*script
= f
.fp
->script
;
2767 /* This is correct because the compiler adjusts the stack beforehand. */
2768 Value lval
= f
.regs
.sp
[-1];
2770 if (!lval
.isPrimitive()) {
2771 ptrdiff_t offs
= (pc
+ GET_JUMP_OFFSET(pc
)) - script
->code
;
2772 JS_ASSERT(script
->nmap
[offs
]);
2773 return script
->nmap
[offs
];
2776 JS_ASSERT(pc
[0] == JSOP_LOOKUPSWITCH
);
2778 pc
+= JUMP_OFFSET_LEN
;
2779 uint32 npairs
= GET_UINT16(pc
);
2784 if (lval
.isString()) {
2785 JSString
*str
= lval
.asString();
2786 for (uint32 i
= 1; i
<= npairs
; i
++) {
2787 Value rval
= script
->getConst(GET_INDEX(pc
));
2789 if (rval
.isString()) {
2790 JSString
*rhs
= rval
.asString();
2791 if (rhs
== str
|| js_EqualStrings(str
, rhs
)) {
2792 ptrdiff_t offs
= (jpc
+ GET_JUMP_OFFSET(pc
)) - script
->code
;
2793 JS_ASSERT(script
->nmap
[offs
]);
2794 return script
->nmap
[offs
];
2797 pc
+= JUMP_OFFSET_LEN
;
2799 } else if (lval
.isNumber()) {
2800 double d
= lval
.asNumber();
2801 for (uint32 i
= 1; i
<= npairs
; i
++) {
2802 Value rval
= script
->getConst(GET_INDEX(pc
));
2804 if (rval
.isNumber() && d
== rval
.asNumber()) {
2805 ptrdiff_t offs
= (jpc
+ GET_JUMP_OFFSET(pc
)) - script
->code
;
2806 JS_ASSERT(script
->nmap
[offs
]);
2807 return script
->nmap
[offs
];
2809 pc
+= JUMP_OFFSET_LEN
;
2812 for (uint32 i
= 1; i
<= npairs
; i
++) {
2813 Value rval
= script
->getConst(GET_INDEX(pc
));
2816 ptrdiff_t offs
= (jpc
+ GET_JUMP_OFFSET(pc
)) - script
->code
;
2817 JS_ASSERT(script
->nmap
[offs
]);
2818 return script
->nmap
[offs
];
2820 pc
+= JUMP_OFFSET_LEN
;
2824 ptrdiff_t offs
= (jpc
+ GET_JUMP_OFFSET(jpc
)) - script
->code
;
2825 JS_ASSERT(script
->nmap
[offs
]);
2826 return script
->nmap
[offs
];
2830 stubs::TableSwitch(VMFrame
&f
, jsbytecode
*origPc
)
2832 jsbytecode
* const originalPC
= origPc
;
2833 jsbytecode
*pc
= originalPC
;
2834 JSScript
*script
= f
.fp
->script
;
2835 uint32 jumpOffset
= GET_JUMP_OFFSET(pc
);
2836 pc
+= JUMP_OFFSET_LEN
;
2838 /* Note: compiler adjusts the stack beforehand. */
2839 Value rval
= f
.regs
.sp
[-1];
2842 if (rval
.isInt32()) {
2843 tableIdx
= rval
.asInt32();
2844 } else if (rval
.isDouble()) {
2845 double d
= rval
.asDouble();
2847 /* Treat -0 (double) as 0. */
2849 } else if (!JSDOUBLE_IS_INT32(d
, (int32_t&)tableIdx
)) {
2857 uint32 low
= GET_JUMP_OFFSET(pc
);
2858 pc
+= JUMP_OFFSET_LEN
;
2859 uint32 high
= GET_JUMP_OFFSET(pc
);
2860 pc
+= JUMP_OFFSET_LEN
;
2863 if ((jsuint
) tableIdx
< (jsuint
)(high
- low
+ 1)) {
2864 pc
+= JUMP_OFFSET_LEN
* tableIdx
;
2865 uint32 candidateOffset
= GET_JUMP_OFFSET(pc
);
2866 if (candidateOffset
)
2867 jumpOffset
= candidateOffset
;
2872 /* Provide the native address. */
2873 ptrdiff_t offset
= (originalPC
+ jumpOffset
) - script
->code
;
2874 JS_ASSERT(script
->nmap
[offset
]);
2875 return script
->nmap
[offset
];