1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 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 Communicator client code, released
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
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 ***** */
42 * JavaScript bytecode interpreter.
58 #include "jsversion.h"
68 #include "jspropertycache.h"
74 #include "jsstaticcheck.h"
76 #include "jslibmath.h"
78 #include "methodjit/MethodJIT.h"
79 #include "methodjit/Logging.h"
81 #include "jsatominlines.h"
82 #include "jscntxtinlines.h"
83 #include "jsinterpinlines.h"
84 #include "jsobjinlines.h"
86 #include "jspropertycacheinlines.h"
87 #include "jsscopeinlines.h"
88 #include "jsscriptinlines.h"
89 #include "jsstrinlines.h"
90 #include "jsopcodeinlines.h"
92 #if JS_HAS_XML_SUPPORT
96 #include "jsautooplen.h"
98 #if defined(JS_METHODJIT) && defined(JS_MONOIC)
99 #include "methodjit/MonoIC.h"
103 using namespace js::gc
;
105 /* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */
106 #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___
109 JSObject
*const JSStackFrame::sInvalidScopeChain
= (JSObject
*)0xbeef;
113 JSStackFrame::pc(JSContext
*cx
, JSStackFrame
*next
)
115 JS_ASSERT_IF(next
, next
->prev_
== this);
116 JS_ASSERT(cx
->containingSegment(this) != NULL
);
122 StackSegment
*segment
= cx
->getCurrentSegment();
123 regs
= segment
->getSuspendedRegs();
126 if (this == regs
->fp
)
130 next
= cx
->computeNextFrame(this);
132 if (next
->flags_
& JSFRAME_HAS_PREVPC
)
133 return next
->prevpc_
;
135 #if defined(JS_METHODJIT) && defined(JS_MONOIC)
136 JSScript
*script
= this->script();
137 js::mjit::JITScript
*jit
= script
->getJIT(isConstructing());
139 size_t high
= jit
->nCallICs
;
140 while (high
> low
+ 1) {
141 /* Could overflow here on a script with 2 billion calls. Oh well. */
142 size_t mid
= (high
+ low
) / 2;
143 void *entry
= jit
->callICs
[mid
].funGuard
.executableAddress();
146 * Use >= here as the return address of the call is likely to be
147 * the start address of the next (possibly IC'ed) operation.
149 if (entry
>= next
->ncode_
)
155 js::mjit::ic::CallICInfo
&callIC
= jit
->callICs
[low
];
157 JS_ASSERT((uint8
*)callIC
.funGuard
.executableAddress() + callIC
.joinPointOffset
== next
->ncode_
);
160 JS_NOT_REACHED("Unknown PC for frame");
166 js::GetScopeChain(JSContext
*cx
)
168 JSStackFrame
*fp
= js_GetTopStackFrame(cx
);
171 * There is no code active on this context. In place of an actual
172 * scope chain, use the context's global object, which is set in
173 * js_InitFunctionAndObjectClasses, and which represents the default
174 * scope chain for the embedding. See also js_FindClassObject.
176 * For embeddings that use the inner and outer object hooks, the inner
177 * object represents the ultimate global object, with the outer object
178 * acting as a stand-in.
180 JSObject
*obj
= cx
->globalObject
;
182 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_INACTIVE
);
186 OBJ_TO_INNER_OBJECT(cx
, obj
);
189 return GetScopeChain(cx
, fp
);
193 * This computes the blockChain by iterating through the bytecode
194 * of the current script until it reaches the PC. Each time it sees
195 * an ENTERBLOCK or LEAVEBLOCK instruction, it records the new
196 * blockChain. A faster variant of this function that doesn't
197 * require bytecode scanning appears below.
200 js::GetBlockChain(JSContext
*cx
, JSStackFrame
*fp
)
202 if (!fp
->isScriptFrame())
205 /* Assume that imacros don't affect blockChain */
206 jsbytecode
*target
= fp
->hasImacropc() ? fp
->imacropc() : fp
->pc(cx
);
208 JSScript
*script
= fp
->script();
209 jsbytecode
*start
= script
->code
;
210 JS_ASSERT(target
>= start
&& target
< start
+ script
->length
);
212 JSObject
*blockChain
= NULL
;
215 for (jsbytecode
*pc
= start
; pc
< target
; pc
+= oplen
) {
216 JSOp op
= js_GetOpcode(cx
, script
, pc
);
217 const JSCodeSpec
*cs
= &js_CodeSpec
[op
];
220 oplen
= js_GetVariableBytecodeLength(pc
);
222 if (op
== JSOP_INDEXBASE
)
223 indexBase
= GET_INDEXBASE(pc
);
224 else if (op
== JSOP_INDEXBASE1
|| op
== JSOP_INDEXBASE2
|| op
== JSOP_INDEXBASE3
)
225 indexBase
= (op
- JSOP_INDEXBASE1
+ 1) << 16;
226 else if (op
== JSOP_RESETBASE
|| op
== JSOP_RESETBASE0
)
228 else if (op
== JSOP_ENTERBLOCK
)
229 blockChain
= script
->getObject(indexBase
+ GET_INDEX(pc
));
230 else if (op
== JSOP_LEAVEBLOCK
|| op
== JSOP_LEAVEBLOCKEXPR
)
231 blockChain
= blockChain
->getParent();
232 else if (op
== JSOP_BLOCKCHAIN
)
233 blockChain
= script
->getObject(indexBase
+ GET_INDEX(pc
));
234 else if (op
== JSOP_NULLBLOCKCHAIN
)
242 * This function computes the current blockChain, but only in
243 * the special case where a BLOCKCHAIN or NULLBLOCKCHAIN
244 * instruction appears immediately after the current PC.
245 * We ensure this happens for a few important ops like DEFFUN.
246 * |oplen| is the length of opcode at the current PC.
249 js::GetBlockChainFast(JSContext
*cx
, JSStackFrame
*fp
, JSOp op
, size_t oplen
)
251 /* Assume that we're in a script frame. */
252 jsbytecode
*pc
= fp
->pc(cx
);
253 JS_ASSERT(js_GetOpcode(cx
, fp
->script(), pc
) == op
);
257 JS_ASSERT(js_GetOpcode(cx
, fp
->script(), pc
) == op
);
259 /* The fast paths assume no JSOP_RESETBASE/INDEXBASE noise. */
260 if (op
== JSOP_NULLBLOCKCHAIN
)
262 if (op
== JSOP_BLOCKCHAIN
)
263 return fp
->script()->getObject(GET_INDEX(pc
));
265 return GetBlockChain(cx
, fp
);
269 * We can't determine in advance which local variables can live on the stack and
270 * be freed when their dynamic scope ends, and which will be closed over and
271 * need to live in the heap. So we place variables on the stack initially, note
272 * when they are closed over, and copy those that are out to the heap when we
273 * leave their dynamic scope.
275 * The bytecode compiler produces a tree of block objects accompanying each
276 * JSScript representing those lexical blocks in the script that have let-bound
277 * variables associated with them. These block objects are never modified, and
278 * never become part of any function's scope chain. Their parent slots point to
279 * the innermost block that encloses them, or are NULL in the outermost blocks
280 * within a function or in eval or global code.
282 * When we are in the static scope of such a block, blockChain points to its
283 * compiler-allocated block object; otherwise, it is NULL.
285 * scopeChain is the current scope chain, including 'call' and 'block' objects
286 * for those function calls and lexical blocks whose static scope we are
287 * currently executing in, and 'with' objects for with statements; the chain is
288 * typically terminated by a global object. However, as an optimization, the
289 * young end of the chain omits block objects we have not yet cloned. To create
290 * a closure, we clone the missing blocks from blockChain (which is always
291 * current), place them at the head of scopeChain, and use that for the
292 * closure's scope chain. If we never close over a lexical block, we never
293 * place a mutable clone of it on scopeChain.
295 * This lazy cloning is implemented in GetScopeChain, which is also used in
296 * some other cases --- entering 'with' blocks, for example.
299 GetScopeChainFull(JSContext
*cx
, JSStackFrame
*fp
, JSObject
*blockChain
)
301 JSObject
*sharedBlock
= blockChain
;
305 * Don't force a call object for a lightweight function call, but do
306 * insist that there is a call object for a heavyweight function call.
308 JS_ASSERT_IF(fp
->isFunctionFrame() && fp
->fun()->isHeavyweight(),
310 return &fp
->scopeChain();
313 /* We don't handle cloning blocks on trace. */
317 * We have one or more lexical scopes to reflect into fp->scopeChain, so
318 * make sure there's a call object at the current head of the scope chain,
319 * if this frame is a call frame.
321 * Also, identify the innermost compiler-allocated block we needn't clone.
323 JSObject
*limitBlock
, *limitClone
;
324 if (fp
->isFunctionFrame() && !fp
->hasCallObj()) {
325 JS_ASSERT_IF(fp
->scopeChain().isClonedBlock(),
326 fp
->scopeChain().getPrivate() != js_FloatingFrameIfGenerator(cx
, fp
));
327 if (!js_GetCallObject(cx
, fp
))
330 /* We know we must clone everything on blockChain. */
331 limitBlock
= limitClone
= NULL
;
334 * scopeChain includes all blocks whose static scope we're within that
335 * have already been cloned. Find the innermost such block. Its
336 * prototype should appear on blockChain; we'll clone blockChain up
337 * to, but not including, that prototype.
339 limitClone
= &fp
->scopeChain();
340 while (limitClone
->getClass() == &js_WithClass
)
341 limitClone
= limitClone
->getParent();
342 JS_ASSERT(limitClone
);
345 * It may seem like we don't know enough about limitClone to be able
346 * to just grab its prototype as we do here, but it's actually okay.
348 * If limitClone is a block object belonging to this frame, then its
349 * prototype is the innermost entry in blockChain that we have already
350 * cloned, and is thus the place to stop when we clone below.
352 * Otherwise, there are no blocks for this frame on scopeChain, and we
353 * need to clone the whole blockChain. In this case, limitBlock can
354 * point to any object known not to be on blockChain, since we simply
355 * loop until we hit limitBlock or NULL. If limitClone is a block, it
356 * isn't a block from this function, since blocks can't be nested
357 * within themselves on scopeChain (recursion is dynamic nesting, not
358 * static nesting). If limitClone isn't a block, its prototype won't
359 * be a block either. So we can just grab limitClone's prototype here
360 * regardless of its type or which frame it belongs to.
362 limitBlock
= limitClone
->getProto();
364 /* If the innermost block has already been cloned, we are done. */
365 if (limitBlock
== sharedBlock
)
366 return &fp
->scopeChain();
370 * Special-case cloning the innermost block; this doesn't have enough in
371 * common with subsequent steps to include in the loop.
373 * js_CloneBlockObject leaves the clone's parent slot uninitialized. We
376 JSObject
*innermostNewChild
= js_CloneBlockObject(cx
, sharedBlock
, fp
);
377 if (!innermostNewChild
)
379 AutoObjectRooter
tvr(cx
, innermostNewChild
);
382 * Clone our way towards outer scopes until we reach the innermost
383 * enclosing function, or the innermost block we've already cloned.
385 JSObject
*newChild
= innermostNewChild
;
387 JS_ASSERT(newChild
->getProto() == sharedBlock
);
388 sharedBlock
= sharedBlock
->getParent();
390 /* Sometimes limitBlock will be NULL, so check that first. */
391 if (sharedBlock
== limitBlock
|| !sharedBlock
)
394 /* As in the call above, we don't know the real parent yet. */
395 JSObject
*clone
= js_CloneBlockObject(cx
, sharedBlock
, fp
);
399 newChild
->setParent(clone
);
402 newChild
->setParent(&fp
->scopeChain());
406 * If we found a limit block belonging to this frame, then we should have
407 * found it in blockChain.
409 JS_ASSERT_IF(limitBlock
&&
410 limitBlock
->isBlock() &&
411 limitClone
->getPrivate() == js_FloatingFrameIfGenerator(cx
, fp
),
414 /* Place our newly cloned blocks at the head of the scope chain. */
415 fp
->setScopeChainNoCallObj(*innermostNewChild
);
416 return innermostNewChild
;
420 js::GetScopeChain(JSContext
*cx
, JSStackFrame
*fp
)
422 return GetScopeChainFull(cx
, fp
, GetBlockChain(cx
, fp
));
426 js::GetScopeChainFast(JSContext
*cx
, JSStackFrame
*fp
, JSOp op
, size_t oplen
)
428 return GetScopeChainFull(cx
, fp
, GetBlockChainFast(cx
, fp
, op
, oplen
));
431 /* Some objects (e.g., With) delegate 'this' to another object. */
432 static inline JSObject
*
433 CallThisObjectHook(JSContext
*cx
, JSObject
*obj
, Value
*argv
)
435 JSObject
*thisp
= obj
->thisObject(cx
);
438 argv
[-1].setObject(*thisp
);
443 * ECMA requires "the global object", but in embeddings such as the browser,
444 * which have multiple top-level objects (windows, frames, etc. in the DOM),
445 * we prefer fun's parent. An example that causes this code to run:
448 * function f() { return this }
449 * function g() { return f }
455 * The alert should display "true".
457 JS_STATIC_INTERPRET
bool
458 ComputeGlobalThis(JSContext
*cx
, Value
*argv
)
460 JSObject
*thisp
= argv
[-2].toObject().getGlobal()->thisObject(cx
);
463 argv
[-1].setObject(*thisp
);
470 ReportIncompatibleMethod(JSContext
*cx
, Value
*vp
, Class
*clasp
)
472 Value
&thisv
= vp
[1];
475 if (thisv
.isObject()) {
476 JS_ASSERT(thisv
.toObject().getClass() != clasp
);
477 } else if (thisv
.isString()) {
478 JS_ASSERT(clasp
!= &js_StringClass
);
479 } else if (thisv
.isNumber()) {
480 JS_ASSERT(clasp
!= &js_NumberClass
);
481 } else if (thisv
.isBoolean()) {
482 JS_ASSERT(clasp
!= &js_BooleanClass
);
484 JS_ASSERT(thisv
.isUndefined() || thisv
.isNull());
488 if (JSFunction
*fun
= js_ValueToFunction(cx
, &vp
[0], 0)) {
489 const char *name
= thisv
.isObject()
490 ? thisv
.toObject().getClass()->name
499 : thisv
.isUndefined()
502 JSAutoByteString funNameBytes
;
503 if (const char *funName
= GetFunctionNameBytes(cx
, fun
, &funNameBytes
)) {
504 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_INCOMPATIBLE_PROTO
,
505 clasp
->name
, funName
, name
);
511 ComputeThisFromArgv(JSContext
*cx
, Value
*argv
)
514 * Check for SynthesizeFrame poisoning and fast constructors which
515 * didn't check their vp properly.
517 JS_ASSERT(!argv
[-1].isMagic());
519 if (argv
[-1].isNullOrUndefined())
520 return ComputeGlobalThis(cx
, argv
);
522 if (!argv
[-1].isObject())
523 return !!js_PrimitiveToObject(cx
, &argv
[-1]);
525 JS_ASSERT(IsSaneThisObject(argv
[-1].toObject()));
531 #if JS_HAS_NO_SUCH_METHOD
533 const uint32 JSSLOT_FOUND_FUNCTION
= 0;
534 const uint32 JSSLOT_SAVED_ID
= 1;
536 Class js_NoSuchMethodClass
= {
538 JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS
,
539 PropertyStub
, /* addProperty */
540 PropertyStub
, /* delProperty */
541 PropertyStub
, /* getProperty */
542 PropertyStub
, /* setProperty */
549 * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of
550 * the base object, we search for the __noSuchMethod__ method in the base.
551 * If it exists, we store the method and the property's id into an object of
552 * NoSuchMethod class and store this object into the callee's stack slot.
553 * Later, js_Invoke will recognise such an object and transfer control to
554 * NoSuchMethod that invokes the method like:
556 * this.__noSuchMethod__(id, args)
558 * where id is the name of the method that this invocation attempted to
559 * call by name, and args is an Array containing this invocation's actual
563 js_OnUnknownMethod(JSContext
*cx
, Value
*vp
)
565 JS_ASSERT(!vp
[1].isPrimitive());
567 JSObject
*obj
= &vp
[1].toObject();
568 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.noSuchMethodAtom
);
569 AutoValueRooter
tvr(cx
);
570 if (!js_GetMethod(cx
, obj
, id
, JSGET_NO_METHOD_BARRIER
, tvr
.addr()))
572 if (tvr
.value().isPrimitive()) {
575 #if JS_HAS_XML_SUPPORT
576 /* Extract the function name from function::name qname. */
577 if (vp
[0].isObject()) {
578 obj
= &vp
[0].toObject();
579 if (!js_IsFunctionQName(cx
, obj
, &id
))
581 if (!JSID_IS_VOID(id
))
582 vp
[0] = IdToValue(id
);
585 obj
= js_NewGCObject(cx
, FINALIZE_OBJECT2
);
590 * Null map to cause prompt and safe crash if this object were to
591 * escape due to a bug. This will make the object appear to be a
592 * stillborn instance that needs no finalization, which is sound:
593 * NoSuchMethod helper objects own no manually allocated resources.
596 obj
->init(cx
, &js_NoSuchMethodClass
, NULL
, NULL
, NULL
, false);
597 obj
->setSlot(JSSLOT_FOUND_FUNCTION
, tvr
.value());
598 obj
->setSlot(JSSLOT_SAVED_ID
, vp
[0]);
599 vp
[0].setObject(*obj
);
604 static JS_REQUIRES_STACK JSBool
605 NoSuchMethod(JSContext
*cx
, uintN argc
, Value
*vp
, uint32 flags
)
607 InvokeArgsGuard args
;
608 if (!cx
->stack().pushInvokeArgs(cx
, 2, &args
))
611 JS_ASSERT(vp
[0].isObject());
612 JS_ASSERT(vp
[1].isObject());
613 JSObject
*obj
= &vp
[0].toObject();
614 JS_ASSERT(obj
->getClass() == &js_NoSuchMethodClass
);
616 args
.callee() = obj
->getSlot(JSSLOT_FOUND_FUNCTION
);
617 args
.thisv() = vp
[1];
618 args
[0] = obj
->getSlot(JSSLOT_SAVED_ID
);
619 JSObject
*argsobj
= NewDenseCopiedArray(cx
, argc
, vp
+ 2);
622 args
[1].setObject(*argsobj
);
623 JSBool ok
= (flags
& JSINVOKE_CONSTRUCT
)
624 ? InvokeConstructor(cx
, args
)
625 : Invoke(cx
, args
, flags
);
630 #endif /* JS_HAS_NO_SUCH_METHOD */
634 JS_REQUIRES_STACK
bool
635 RunScript(JSContext
*cx
, JSScript
*script
, JSStackFrame
*fp
)
639 #ifdef JS_METHODJIT_SPEW
643 AutoInterpPreparer
prepareInterp(cx
, script
);
645 JS_ASSERT(fp
== cx
->fp());
646 JS_ASSERT(fp
->script() == script
);
649 mjit::CompileStatus status
= mjit::CanMethodJIT(cx
, script
, fp
);
650 if (status
== mjit::Compile_Error
)
653 if (status
== mjit::Compile_Okay
)
654 return mjit::JaegerShot(cx
);
657 return Interpret(cx
, fp
);
661 * Find a function reference and its 'this' value implicit first parameter
662 * under argc arguments on cx's stack, and call the function. Push missing
663 * required arguments, allocate declared local variables, and pop everything
664 * when done. Then push the return value.
666 JS_REQUIRES_STACK
bool
667 Invoke(JSContext
*cx
, const CallArgs
&argsRef
, uint32 flags
)
669 /* N.B. Must be kept in sync with InvokeSessionGuard::start/invoke */
671 CallArgs args
= argsRef
;
672 JS_ASSERT(args
.argc() <= JS_ARGS_LENGTH_MAX
);
674 if (args
.callee().isPrimitive()) {
675 js_ReportIsNotFunction(cx
, &args
.callee(), flags
& JSINVOKE_FUNFLAGS
);
679 JSObject
&callee
= args
.callee().toObject();
680 Class
*clasp
= callee
.getClass();
682 /* Invoke non-functions. */
683 if (JS_UNLIKELY(clasp
!= &js_FunctionClass
)) {
684 #if JS_HAS_NO_SUCH_METHOD
685 if (JS_UNLIKELY(clasp
== &js_NoSuchMethodClass
))
686 return NoSuchMethod(cx
, args
.argc(), args
.base(), 0);
688 JS_ASSERT_IF(flags
& JSINVOKE_CONSTRUCT
, !clasp
->construct
);
690 js_ReportIsNotFunction(cx
, &args
.callee(), flags
);
693 return CallJSNative(cx
, clasp
->call
, args
.argc(), args
.base());
696 /* Invoke native functions. */
697 JSFunction
*fun
= callee
.getFunctionPrivate();
698 JS_ASSERT_IF(flags
& JSINVOKE_CONSTRUCT
, !fun
->isConstructor());
700 return CallJSNative(cx
, fun
->u
.n
.native
, args
.argc(), args
.base());
702 /* Handle the empty-script special case. */
703 JSScript
*script
= fun
->script();
704 if (JS_UNLIKELY(script
->isEmpty())) {
705 if (flags
& JSINVOKE_CONSTRUCT
) {
706 JSObject
*obj
= js_CreateThisForFunction(cx
, &callee
);
709 args
.rval().setObject(*obj
);
711 args
.rval().setUndefined();
716 /* Get pointer to new frame/slots, prepare arguments. */
717 InvokeFrameGuard frame
;
718 if (JS_UNLIKELY(!cx
->stack().getInvokeFrame(cx
, args
, fun
, script
, &flags
, &frame
)))
721 /* Initialize frame, locals. */
722 JSStackFrame
*fp
= frame
.fp();
723 fp
->initCallFrame(cx
, callee
, fun
, args
.argc(), flags
);
724 SetValueRangeToUndefined(fp
->slots(), script
->nfixed
);
726 /* Officially push fp. frame's destructor pops. */
727 cx
->stack().pushInvokeFrame(cx
, args
, &frame
);
729 /* Now that the new frame is rooted, maybe create a call object. */
730 if (fun
->isHeavyweight() && !js_GetCallObject(cx
, fp
))
733 /* Run function until JSOP_STOP, JSOP_RETURN or error. */
736 AutoPreserveEnumerators
preserve(cx
);
737 ok
= RunScript(cx
, script
, fp
);
740 args
.rval() = fp
->returnValue();
741 JS_ASSERT_IF(ok
&& (flags
& JSINVOKE_CONSTRUCT
), !args
.rval().isPrimitive());
747 InvokeSessionGuard::start(JSContext
*cx
, const Value
&calleev
, const Value
&thisv
, uintN argc
)
750 if (TRACE_RECORDER(cx
))
751 AbortRecording(cx
, "attempt to reenter VM while recording");
753 if (TRACE_PROFILER(cx
))
759 /* Always push arguments, regardless of optimized/normal invoke. */
760 StackSpace
&stack
= cx
->stack();
761 if (!stack
.pushInvokeArgs(cx
, argc
, &args_
))
764 /* Callees may clobber 'this' or 'callee'. */
765 savedCallee_
= args_
.callee() = calleev
;
766 savedThis_
= args_
.thisv() = thisv
;
769 /* Hoist dynamic checks from scripted Invoke. */
770 if (!calleev
.isObject())
772 JSObject
&callee
= calleev
.toObject();
773 if (callee
.getClass() != &js_FunctionClass
)
775 JSFunction
*fun
= callee
.getFunctionPrivate();
778 script_
= fun
->script();
779 if (fun
->isHeavyweight() || script_
->isEmpty() || cx
->compartment
->debugMode
)
782 /* Push the stack frame once for the session. */
784 if (!stack
.getInvokeFrame(cx
, args_
, fun
, script_
, &flags
, &frame_
))
786 JSStackFrame
*fp
= frame_
.fp();
787 fp
->initCallFrame(cx
, calleev
.toObject(), fun
, argc
, flags
);
788 stack
.pushInvokeFrame(cx
, args_
, &frame_
);
791 /* Hoist dynamic checks from RunScript. */
792 mjit::CompileStatus status
= mjit::CanMethodJIT(cx
, script_
, fp
);
793 if (status
== mjit::Compile_Error
)
795 if (status
!= mjit::Compile_Okay
)
797 /* Cannot also cache the raw code pointer; it can change. */
799 /* Hoist dynamic checks from CheckStackAndEnterMethodJIT. */
800 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
801 stackLimit_
= stack
.getStackLimit(cx
);
805 stop_
= script_
->code
+ script_
->length
- 1;
806 JS_ASSERT(*stop_
== JSOP_STOP
);
809 /* Cached to avoid canonicalActualArg in InvokeSessionGuard::operator[]. */
810 nformals_
= fp
->numFormalArgs();
811 formals_
= fp
->formalArgs();
812 actuals_
= args_
.argv();
813 JS_ASSERT(actuals_
== fp
->actualArgs());
818 * Use the normal invoke path.
820 * The callee slot gets overwritten during an unoptimized Invoke, so we
821 * cache it here and restore it before every Invoke call. The 'this' value
822 * does not get overwritten, so we can fill it here once.
826 formals_
= actuals_
= args_
.argv();
827 nformals_
= (unsigned)-1;
832 ExternalInvoke(JSContext
*cx
, const Value
&thisv
, const Value
&fval
,
833 uintN argc
, Value
*argv
, Value
*rval
)
837 InvokeArgsGuard args
;
838 if (!cx
->stack().pushInvokeArgs(cx
, argc
, &args
))
841 args
.callee() = fval
;
842 args
.thisv() = thisv
;
843 memcpy(args
.argv(), argv
, argc
* sizeof(Value
));
845 if (args
.thisv().isObject()) {
847 * We must call the thisObject hook in case we are not called from the
848 * interpreter, where a prior bytecode has computed an appropriate
851 JSObject
*thisp
= args
.thisv().toObject().thisObject(cx
);
854 JS_ASSERT(IsSaneThisObject(*thisp
));
855 args
.thisv().setObject(*thisp
);
858 if (!Invoke(cx
, args
, 0))
866 ExternalInvokeConstructor(JSContext
*cx
, const Value
&fval
, uintN argc
, Value
*argv
,
871 InvokeArgsGuard args
;
872 if (!cx
->stack().pushInvokeArgs(cx
, argc
, &args
))
875 args
.callee() = fval
;
876 args
.thisv().setMagic(JS_THIS_POISON
);
877 memcpy(args
.argv(), argv
, argc
* sizeof(Value
));
879 if (!InvokeConstructor(cx
, args
))
887 ExternalGetOrSet(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
&fval
,
888 JSAccessMode mode
, uintN argc
, Value
*argv
, Value
*rval
)
893 * ExternalInvoke could result in another try to get or set the same id
894 * again, see bug 355497.
896 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
898 return ExternalInvoke(cx
, obj
, fval
, argc
, argv
, rval
);
902 Execute(JSContext
*cx
, JSObject
*chain
, JSScript
*script
,
903 JSStackFrame
*prev
, uintN flags
, Value
*result
)
906 JS_ASSERT_IF(prev
, !prev
->isDummyFrame());
908 if (script
->isEmpty()) {
910 result
->setUndefined();
917 * Get a pointer to new frame/slots. This memory is not "claimed", so the
918 * code before pushExecuteFrame must not reenter the interpreter.
920 ExecuteFrameGuard frame
;
921 if (!cx
->stack().getExecuteFrame(cx
, script
, &frame
))
924 /* Initialize fixed slots (GVAR ops expect NULL). */
925 SetValueRangeToNull(frame
.fp()->slots(), script
->nfixed
);
927 /* Initialize frame and locals. */
928 JSObject
*initialVarObj
;
930 JS_ASSERT(chain
== &prev
->scopeChain());
931 frame
.fp()->initEvalFrame(cx
, script
, prev
, flags
);
934 * We want to call |prev->varobj()|, but this requires knowing the
935 * CallStackSegment of |prev|. If |prev == cx->fp()|, the callstack is
936 * simply the context's active callstack, so we can use
937 * |prev->varobj(cx)|. When |prev != cx->fp()|, we need to do a slow
938 * linear search. Luckily, this only happens with EvaluateInFrame.
940 initialVarObj
= (prev
== cx
->maybefp())
942 : &prev
->varobj(cx
->containingSegment(prev
));
944 /* The scope chain could be anything, so innerize just in case. */
945 JSObject
*innerizedChain
= chain
;
946 OBJ_TO_INNER_OBJECT(cx
, innerizedChain
);
950 /* If we were handed a non-native object, complain bitterly. */
951 if (!innerizedChain
->isNative()) {
952 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
953 JSMSG_NON_NATIVE_SCOPE
);
957 /* Initialize frame. */
958 frame
.fp()->initGlobalFrame(script
, *innerizedChain
, flags
);
960 /* Compute 'this'. */
961 JSObject
*thisp
= chain
->thisObject(cx
);
964 frame
.fp()->globalThis().setObject(*thisp
);
966 initialVarObj
= (cx
->options
& JSOPTION_VAROBJFIX
) ? chain
->getGlobal() : chain
;
970 * Strict mode eval code receives its own, fresh lexical environment; thus
971 * strict mode eval can't mutate its calling frame's binding set.
973 if ((flags
& JSFRAME_EVAL
) && script
->strictModeCode
) {
974 AutoScriptRooter
root(cx
, script
);
975 initialVarObj
= NewCallObject(cx
, &script
->bindings
, *initialVarObj
, NULL
);
978 initialVarObj
->setPrivate(frame
.fp());
980 /* Clear the Call object propagated from the previous frame, if any. */
981 if (frame
.fp()->hasCallObj())
982 frame
.fp()->clearCallObj();
983 frame
.fp()->setScopeChainAndCallObj(*initialVarObj
);
985 JS_ASSERT(!initialVarObj
->getOps()->defineProperty
);
987 #if JS_HAS_SHARP_VARS
988 JS_STATIC_ASSERT(SHARP_NSLOTS
== 2);
989 if (script
->hasSharps
) {
990 JS_ASSERT(script
->nfixed
>= SHARP_NSLOTS
);
991 Value
*sharps
= &frame
.fp()->slots()[script
->nfixed
- SHARP_NSLOTS
];
992 if (prev
&& prev
->script()->hasSharps
) {
993 JS_ASSERT(prev
->numFixed() >= SHARP_NSLOTS
);
994 int base
= (prev
->isFunctionFrame() && !prev
->isEvalOrDebuggerFrame())
995 ? prev
->fun()->script()->bindings
.sharpSlotBase(cx
)
996 : prev
->numFixed() - SHARP_NSLOTS
;
999 sharps
[0] = prev
->slots()[base
];
1000 sharps
[1] = prev
->slots()[base
+ 1];
1002 sharps
[0].setUndefined();
1003 sharps
[1].setUndefined();
1008 /* Officially push |fp|. |frame|'s destructor pops. */
1009 cx
->stack().pushExecuteFrame(cx
, initialVarObj
, &frame
);
1011 /* Now that the frame has been pushed, we can call the thisObject hook. */
1013 JSObject
*thisp
= chain
->thisObject(cx
);
1016 frame
.fp()->globalThis().setObject(*thisp
);
1019 Probes::startExecution(cx
, script
);
1021 /* Run script until JSOP_STOP or error. */
1022 AutoPreserveEnumerators
preserve(cx
);
1023 JSBool ok
= RunScript(cx
, script
, frame
.fp());
1025 *result
= frame
.fp()->returnValue();
1027 Probes::stopExecution(cx
, script
);
1033 CheckRedeclaration(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN attrs
,
1034 JSObject
**objp
, JSProperty
**propp
)
1038 uintN oldAttrs
, report
;
1040 const char *type
, *name
;
1043 * Both objp and propp must be either null or given. When given, *propp
1044 * must be null. This way we avoid an extra "if (propp) *propp = NULL" for
1045 * the common case of a nonexistent property.
1047 JS_ASSERT(!objp
== !propp
);
1048 JS_ASSERT_IF(propp
, !*propp
);
1050 /* The JSPROP_INITIALIZER case below may generate a warning. Since we must
1051 * drop the property before reporting it, we insists on !propp to avoid
1052 * looking up the property again after the reporting is done.
1054 JS_ASSERT_IF(attrs
& JSPROP_INITIALIZER
, attrs
== JSPROP_INITIALIZER
);
1055 JS_ASSERT_IF(attrs
== JSPROP_INITIALIZER
, !propp
);
1057 if (!obj
->lookupProperty(cx
, id
, &obj2
, &prop
))
1061 if (obj2
->isNative()) {
1062 oldAttrs
= ((Shape
*) prop
)->attributes();
1064 if (!obj2
->getAttributes(cx
, id
, &oldAttrs
))
1075 if (attrs
== JSPROP_INITIALIZER
) {
1076 /* Allow the new object to override properties. */
1080 /* The property must be dropped already. */
1082 report
= JSREPORT_WARNING
| JSREPORT_STRICT
;
1085 isFunction
= false; /* suppress bogus gcc warnings */
1088 /* We allow redeclaring some non-readonly properties. */
1089 if (((oldAttrs
| attrs
) & JSPROP_READONLY
) == 0) {
1090 /* Allow redeclaration of variables and functions. */
1091 if (!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)))
1095 * Allow adding a getter only if a property already has a setter
1096 * but no getter and similarly for adding a setter. That is, we
1097 * allow only the following transitions:
1099 * no-property --> getter --> getter + setter
1100 * no-property --> setter --> getter + setter
1102 if ((~(oldAttrs
^ attrs
) & (JSPROP_GETTER
| JSPROP_SETTER
)) == 0)
1106 * Allow redeclaration of an impermanent property (in which case
1107 * anyone could delete it and redefine it, willy-nilly).
1109 if (!(oldAttrs
& JSPROP_PERMANENT
))
1113 report
= JSREPORT_ERROR
;
1114 isFunction
= (oldAttrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) != 0;
1117 if (!obj
->getProperty(cx
, id
, &value
))
1119 isFunction
= IsFunctionObject(value
);
1123 type
= (attrs
== JSPROP_INITIALIZER
)
1125 : (oldAttrs
& attrs
& JSPROP_GETTER
)
1127 : (oldAttrs
& attrs
& JSPROP_SETTER
)
1129 : (oldAttrs
& JSPROP_READONLY
)
1134 JSAutoByteString bytes
;
1135 name
= js_ValueToPrintable(cx
, IdToValue(id
), &bytes
);
1138 return !!JS_ReportErrorFlagsAndNumber(cx
, report
,
1139 js_GetErrorMessage
, NULL
,
1140 JSMSG_REDECLARED_VAR
,
1145 HasInstance(JSContext
*cx
, JSObject
*obj
, const Value
*v
, JSBool
*bp
)
1147 Class
*clasp
= obj
->getClass();
1148 if (clasp
->hasInstance
)
1149 return clasp
->hasInstance(cx
, obj
, v
, bp
);
1150 js_ReportValueError(cx
, JSMSG_BAD_INSTANCEOF_RHS
,
1151 JSDVG_SEARCH_STACK
, ObjectValue(*obj
), NULL
);
1156 StrictlyEqual(JSContext
*cx
, const Value
&lref
, const Value
&rref
, JSBool
*equal
)
1158 Value lval
= lref
, rval
= rref
;
1159 if (SameType(lval
, rval
)) {
1160 if (lval
.isString())
1161 return EqualStrings(cx
, lval
.toString(), rval
.toString(), equal
);
1162 if (lval
.isDouble()) {
1163 *equal
= JSDOUBLE_COMPARE(lval
.toDouble(), ==, rval
.toDouble(), JS_FALSE
);
1166 if (lval
.isObject()) {
1167 *equal
= &lval
.toObject() == &rval
.toObject();
1170 if (lval
.isUndefined()) {
1174 *equal
= lval
.payloadAsRawUint32() == rval
.payloadAsRawUint32();
1178 if (lval
.isDouble() && rval
.isInt32()) {
1179 double ld
= lval
.toDouble();
1180 double rd
= rval
.toInt32();
1181 *equal
= JSDOUBLE_COMPARE(ld
, ==, rd
, JS_FALSE
);
1184 if (lval
.isInt32() && rval
.isDouble()) {
1185 double ld
= lval
.toInt32();
1186 double rd
= rval
.toDouble();
1187 *equal
= JSDOUBLE_COMPARE(ld
, ==, rd
, JS_FALSE
);
1196 IsNegativeZero(const Value
&v
)
1198 return v
.isDouble() && JSDOUBLE_IS_NEGZERO(v
.toDouble());
1202 IsNaN(const Value
&v
)
1204 return v
.isDouble() && JSDOUBLE_IS_NaN(v
.toDouble());
1208 SameValue(JSContext
*cx
, const Value
&v1
, const Value
&v2
, JSBool
*same
)
1210 if (IsNegativeZero(v1
)) {
1211 *same
= IsNegativeZero(v2
);
1214 if (IsNegativeZero(v2
)) {
1218 if (IsNaN(v1
) && IsNaN(v2
)) {
1222 return StrictlyEqual(cx
, v1
, v2
, same
);
1226 TypeOfValue(JSContext
*cx
, const Value
&vref
)
1230 return JSTYPE_NUMBER
;
1232 return JSTYPE_STRING
;
1234 return JSTYPE_OBJECT
;
1235 if (v
.isUndefined())
1238 return v
.toObject().typeOf(cx
);
1239 JS_ASSERT(v
.isBoolean());
1240 return JSTYPE_BOOLEAN
;
1244 InstanceOfSlow(JSContext
*cx
, JSObject
*obj
, Class
*clasp
, Value
*argv
)
1246 JS_ASSERT(!obj
|| obj
->getClass() != clasp
);
1248 JSFunction
*fun
= js_ValueToFunction(cx
, &argv
[-2], 0);
1250 JSAutoByteString funNameBytes
;
1251 if (const char *funName
= GetFunctionNameBytes(cx
, fun
, &funNameBytes
)) {
1252 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_INCOMPATIBLE_PROTO
,
1253 clasp
->name
, funName
,
1254 obj
? obj
->getClass()->name
: js_null_str
);
1261 JS_REQUIRES_STACK
bool
1262 InvokeConstructor(JSContext
*cx
, const CallArgs
&argsRef
)
1264 JS_ASSERT(!js_FunctionClass
.construct
);
1265 CallArgs args
= argsRef
;
1268 if (args
.callee().isPrimitive() || !(callee
= &args
.callee().toObject())->getParent()) {
1269 js_ReportIsNotFunction(cx
, &args
.callee(), JSV2F_CONSTRUCT
);
1273 /* Handle the fast-constructors cases before falling into the general case . */
1274 Class
*clasp
= callee
->getClass();
1275 JSFunction
*fun
= NULL
;
1276 if (clasp
== &js_FunctionClass
) {
1277 fun
= callee
->getFunctionPrivate();
1278 if (fun
->isConstructor()) {
1279 args
.thisv().setMagicWithObjectOrNullPayload(NULL
);
1280 return CallJSNativeConstructor(cx
, fun
->u
.n
.native
, args
.argc(), args
.base());
1282 } else if (clasp
->construct
) {
1283 args
.thisv().setMagicWithObjectOrNullPayload(NULL
);
1284 return CallJSNativeConstructor(cx
, clasp
->construct
, args
.argc(), args
.base());
1287 /* Scripts create their own |this| in JSOP_BEGIN */
1288 if (!fun
|| !fun
->isInterpreted()) {
1289 JSObject
*obj
= js_CreateThis(cx
, callee
);
1292 args
.thisv().setObject(*obj
);
1295 if (!Invoke(cx
, args
, JSINVOKE_CONSTRUCT
))
1298 if (args
.rval().isPrimitive()) {
1299 if (clasp
!= &js_FunctionClass
) {
1300 /* native [[Construct]] returning primitive is error */
1301 JSAutoByteString bytes
;
1302 if (js_ValueToPrintable(cx
, args
.rval(), &bytes
)) {
1303 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1304 JSMSG_BAD_NEW_RESULT
, bytes
.ptr());
1309 /* The interpreter fixes rval for us. */
1310 JS_ASSERT(!fun
->isInterpreted());
1312 args
.rval() = args
.thisv();
1315 JS_RUNTIME_METER(cx
->runtime
, constructs
);
1320 InvokeConstructorWithGivenThis(JSContext
*cx
, JSObject
*thisobj
, const Value
&fval
,
1321 uintN argc
, Value
*argv
, Value
*rval
)
1325 InvokeArgsGuard args
;
1326 if (!cx
->stack().pushInvokeArgs(cx
, argc
, &args
))
1329 args
.callee() = fval
;
1330 /* Initialize args.thisv on all paths below. */
1331 memcpy(args
.argv(), argv
, argc
* sizeof(Value
));
1333 /* Handle the fast-constructor cases before calling the general case. */
1334 JSObject
&callee
= fval
.toObject();
1335 Class
*clasp
= callee
.getClass();
1338 if (clasp
== &js_FunctionClass
&& (fun
= callee
.getFunctionPrivate())->isConstructor()) {
1339 args
.thisv().setMagicWithObjectOrNullPayload(thisobj
);
1340 ok
= CallJSNativeConstructor(cx
, fun
->u
.n
.native
, args
.argc(), args
.base());
1341 } else if (clasp
->construct
) {
1342 args
.thisv().setMagicWithObjectOrNullPayload(thisobj
);
1343 ok
= CallJSNativeConstructor(cx
, clasp
->construct
, args
.argc(), args
.base());
1345 args
.thisv().setObjectOrNull(thisobj
);
1346 ok
= Invoke(cx
, args
, JSINVOKE_CONSTRUCT
);
1349 *rval
= args
.rval();
1354 DirectEval(JSContext
*cx
, JSFunction
*evalfun
, uint32 argc
, Value
*vp
)
1356 JS_ASSERT(vp
== cx
->regs
->sp
- argc
- 2);
1357 JS_ASSERT(vp
[0].isObject());
1358 JS_ASSERT(vp
[0].toObject().isFunction());
1359 JS_ASSERT(vp
[0].toObject().getFunctionPrivate() == evalfun
);
1360 JS_ASSERT(IsBuiltinEvalFunction(evalfun
));
1362 JSStackFrame
*caller
= cx
->fp();
1363 JS_ASSERT(caller
->isScriptFrame());
1364 AutoFunctionCallProbe
callProbe(cx
, evalfun
, caller
->script());
1366 JSObject
*scopeChain
=
1367 GetScopeChainFast(cx
, caller
, JSOP_EVAL
, JSOP_EVAL_LENGTH
+ JSOP_LINENO_LENGTH
);
1368 if (!scopeChain
|| !EvalKernel(cx
, argc
, vp
, DIRECT_EVAL
, caller
, scopeChain
))
1370 cx
->regs
->sp
= vp
+ 1;
1375 ValueToId(JSContext
*cx
, const Value
&v
, jsid
*idp
)
1378 if (ValueFitsInInt32(v
, &i
) && INT_FITS_IN_JSID(i
)) {
1379 *idp
= INT_TO_JSID(i
);
1383 #if JS_HAS_XML_SUPPORT
1385 JSObject
*obj
= &v
.toObject();
1386 if (obj
->isXMLId()) {
1387 *idp
= OBJECT_TO_JSID(obj
);
1393 return js_ValueToStringId(cx
, v
, idp
);
1396 } /* namespace js */
1399 * Enter the new with scope using an object at sp[-1] and associate the depth
1400 * of the with block with sp + stackIndex.
1402 JS_STATIC_INTERPRET JS_REQUIRES_STACK JSBool
1403 js_EnterWith(JSContext
*cx
, jsint stackIndex
, JSOp op
, size_t oplen
)
1405 JSStackFrame
*fp
= cx
->fp();
1406 Value
*sp
= cx
->regs
->sp
;
1407 JS_ASSERT(stackIndex
< 0);
1408 JS_ASSERT(fp
->base() <= sp
+ stackIndex
);
1411 if (sp
[-1].isObject()) {
1412 obj
= &sp
[-1].toObject();
1414 obj
= js_ValueToNonNullObject(cx
, sp
[-1]);
1417 sp
[-1].setObject(*obj
);
1420 JSObject
*parent
= GetScopeChainFast(cx
, fp
, op
, oplen
);
1424 OBJ_TO_INNER_OBJECT(cx
, obj
);
1428 JSObject
*withobj
= js_NewWithObject(cx
, obj
, parent
,
1429 sp
+ stackIndex
- fp
->base());
1433 fp
->setScopeChainNoCallObj(*withobj
);
1437 JS_STATIC_INTERPRET JS_REQUIRES_STACK
void
1438 js_LeaveWith(JSContext
*cx
)
1442 withobj
= &cx
->fp()->scopeChain();
1443 JS_ASSERT(withobj
->getClass() == &js_WithClass
);
1444 JS_ASSERT(withobj
->getPrivate() == js_FloatingFrameIfGenerator(cx
, cx
->fp()));
1445 JS_ASSERT(OBJ_BLOCK_DEPTH(cx
, withobj
) >= 0);
1446 withobj
->setPrivate(NULL
);
1447 cx
->fp()->setScopeChainNoCallObj(*withobj
->getParent());
1450 JS_REQUIRES_STACK Class
*
1451 js_IsActiveWithOrBlock(JSContext
*cx
, JSObject
*obj
, int stackDepth
)
1455 clasp
= obj
->getClass();
1456 if ((clasp
== &js_WithClass
|| clasp
== &js_BlockClass
) &&
1457 obj
->getPrivate() == js_FloatingFrameIfGenerator(cx
, cx
->fp()) &&
1458 OBJ_BLOCK_DEPTH(cx
, obj
) >= stackDepth
) {
1465 * Unwind block and scope chains to match the given depth. The function sets
1466 * fp->sp on return to stackDepth.
1468 JS_REQUIRES_STACK JSBool
1469 js_UnwindScope(JSContext
*cx
, jsint stackDepth
, JSBool normalUnwind
)
1473 JS_ASSERT(stackDepth
>= 0);
1474 JS_ASSERT(cx
->fp()->base() + stackDepth
<= cx
->regs
->sp
);
1476 JSStackFrame
*fp
= cx
->fp();
1478 clasp
= js_IsActiveWithOrBlock(cx
, &fp
->scopeChain(), stackDepth
);
1481 if (clasp
== &js_BlockClass
) {
1482 /* Don't fail until after we've updated all stacks. */
1483 normalUnwind
&= js_PutBlockObject(cx
, normalUnwind
);
1489 cx
->regs
->sp
= fp
->base() + stackDepth
;
1490 return normalUnwind
;
1493 JS_STATIC_INTERPRET JSBool
1494 js_DoIncDec(JSContext
*cx
, const JSCodeSpec
*cs
, Value
*vp
, Value
*vp2
)
1496 if (cs
->format
& JOF_POST
) {
1498 if (!ValueToNumber(cx
, *vp
, &d
))
1501 (cs
->format
& JOF_INC
) ? ++d
: --d
;
1507 if (!ValueToNumber(cx
, *vp
, &d
))
1509 (cs
->format
& JOF_INC
) ? ++d
: --d
;
1516 js::GetUpvar(JSContext
*cx
, uintN closureLevel
, UpvarCookie cookie
)
1518 JS_ASSERT(closureLevel
>= cookie
.level() && cookie
.level() > 0);
1519 const uintN targetLevel
= closureLevel
- cookie
.level();
1520 JS_ASSERT(targetLevel
< UpvarCookie::UPVAR_LEVEL_LIMIT
);
1522 JSStackFrame
*fp
= cx
->findFrameAtLevel(targetLevel
);
1523 uintN slot
= cookie
.slot();
1526 if (!fp
->isFunctionFrame() || fp
->isEvalFrame()) {
1527 vp
= fp
->slots() + fp
->numFixed();
1528 } else if (slot
< fp
->numFormalArgs()) {
1529 vp
= fp
->formalArgs();
1530 } else if (slot
== UpvarCookie::CALLEE_SLOT
) {
1531 vp
= &fp
->calleeValue();
1534 slot
-= fp
->numFormalArgs();
1535 JS_ASSERT(slot
< fp
->numSlots());
1544 JS_STATIC_INTERPRET JS_REQUIRES_STACK
void
1545 js_LogOpcode(JSContext
*cx
)
1550 intN ndefs
, n
, nuses
;
1553 logfp
= (FILE *) cx
->logfp
;
1559 * Operations in prologues don't produce interesting values, and
1560 * js_DecompileValueGenerator isn't set up to handle them anyway.
1562 if (cx
->logPrevPc
&& regs
->pc
>= fp
->script()->main
) {
1563 JSOp logPrevOp
= JSOp(*cx
->logPrevPc
);
1564 ndefs
= js_GetStackDefs(cx
, &js_CodeSpec
[logPrevOp
], logPrevOp
,
1565 fp
->script(), cx
->logPrevPc
);
1568 * If there aren't that many elements on the stack, then we have
1569 * probably entered a new frame, and printing output would just be
1573 ndefs
< regs
->sp
- fp
->slots()) {
1574 for (n
= -ndefs
; n
< 0; n
++) {
1575 char *bytes
= DecompileValueGenerator(cx
, n
, regs
->sp
[n
], NULL
);
1577 fprintf(logfp
, "%s %s",
1578 (n
== -ndefs
) ? " output:" : ",",
1582 JS_ClearPendingException(cx
);
1585 fprintf(logfp
, " @ %u\n", (uintN
) (regs
->sp
- fp
->base()));
1587 fprintf(logfp
, " stack: ");
1588 for (Value
*siter
= fp
->base(); siter
< regs
->sp
; siter
++) {
1589 if (siter
->isObject() && siter
->toObject().getClass() == &js_CallClass
) {
1591 * Call objects have NULL convert ops so that we catch cases
1592 * where they escape. So js_ValueToString doesn't work on them.
1594 fputs("<call>", logfp
);
1596 JSString
*str
= js_ValueToString(cx
, *siter
);
1597 JSLinearString
*linearStr
= str
? str
->ensureLinear(cx
) : NULL
;
1599 fputs("<null>", logfp
);
1600 JS_ClearPendingException(cx
);
1602 FileEscapedString(logfp
, linearStr
, 0);
1610 fprintf(logfp
, "%4u: ",
1611 js_PCToLineNumber(cx
, fp
->script(),
1612 fp
->hasImacropc() ? fp
->imacropc() : regs
->pc
));
1613 js_Disassemble1(cx
, fp
->script(), regs
->pc
,
1614 regs
->pc
- fp
->script()->code
,
1616 op
= (JSOp
) *regs
->pc
;
1617 nuses
= js_GetStackUses(&js_CodeSpec
[op
], op
, regs
->pc
);
1619 for (n
= -nuses
; n
< 0; n
++) {
1620 char *bytes
= DecompileValueGenerator(cx
, n
, regs
->sp
[n
], NULL
);
1622 fprintf(logfp
, "%s %s",
1623 (n
== -nuses
) ? " inputs:" : ",",
1627 JS_ClearPendingException(cx
);
1630 fprintf(logfp
, " @ %u\n", (uintN
) (regs
->sp
- fp
->base()));
1632 cx
->logPrevPc
= regs
->pc
;
1634 /* It's nice to have complete logs when debugging a crash. */
1642 # include <stdlib.h>
1644 # define HIST_NSLOTS 8
1647 * The second dimension is hardcoded at 256 because we know that many bits fit
1648 * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address
1649 * any particular row.
1651 static uint32 succeeds
[JSOP_LIMIT
][256];
1652 static uint32 slot_ops
[JSOP_LIMIT
][HIST_NSLOTS
];
1654 JS_STATIC_INTERPRET
void
1655 js_MeterOpcodePair(JSOp op1
, JSOp op2
)
1657 if (op1
!= JSOP_STOP
)
1658 ++succeeds
[op1
][op2
];
1661 JS_STATIC_INTERPRET
void
1662 js_MeterSlotOpcode(JSOp op
, uint32 slot
)
1664 if (slot
< HIST_NSLOTS
)
1665 ++slot_ops
[op
][slot
];
1668 typedef struct Edge
{
1675 compare_edges(const void *a
, const void *b
)
1677 const Edge
*ea
= (const Edge
*) a
;
1678 const Edge
*eb
= (const Edge
*) b
;
1680 return (int32
)eb
->count
- (int32
)ea
->count
;
1686 const char *name
, *from
, *style
;
1688 uint32 total
, count
;
1689 uint32 i
, j
, nedges
;
1692 name
= getenv("JS_OPMETER_FILE");
1694 name
= "/tmp/ops.dot";
1695 fp
= fopen(name
, "w");
1702 for (i
= 0; i
< JSOP_LIMIT
; i
++) {
1703 for (j
= 0; j
< JSOP_LIMIT
; j
++) {
1704 count
= succeeds
[i
][j
];
1712 # define SIGNIFICANT(count,total) (200. * (count) >= (total))
1714 graph
= (Edge
*) js_calloc(nedges
* sizeof graph
[0]);
1717 for (i
= nedges
= 0; i
< JSOP_LIMIT
; i
++) {
1718 from
= js_CodeName
[i
];
1719 for (j
= 0; j
< JSOP_LIMIT
; j
++) {
1720 count
= succeeds
[i
][j
];
1721 if (count
!= 0 && SIGNIFICANT(count
, total
)) {
1722 graph
[nedges
].from
= from
;
1723 graph
[nedges
].to
= js_CodeName
[j
];
1724 graph
[nedges
].count
= count
;
1729 qsort(graph
, nedges
, sizeof(Edge
), compare_edges
);
1733 fputs("digraph {\n", fp
);
1734 for (i
= 0, style
= NULL
; i
< nedges
; i
++) {
1735 JS_ASSERT(i
== 0 || graph
[i
-1].count
>= graph
[i
].count
);
1736 if (!style
|| graph
[i
-1].count
!= graph
[i
].count
) {
1737 style
= (i
> nedges
* .75) ? "dotted" :
1738 (i
> nedges
* .50) ? "dashed" :
1739 (i
> nedges
* .25) ? "solid" : "bold";
1741 fprintf(fp
, " %s -> %s [label=\"%lu\" style=%s]\n",
1742 graph
[i
].from
, graph
[i
].to
,
1743 (unsigned long)graph
[i
].count
, style
);
1749 name
= getenv("JS_OPMETER_HIST");
1751 name
= "/tmp/ops.hist";
1752 fp
= fopen(name
, "w");
1757 fputs("bytecode", fp
);
1758 for (j
= 0; j
< HIST_NSLOTS
; j
++)
1759 fprintf(fp
, " slot %1u", (unsigned)j
);
1761 fputs("========", fp
);
1762 for (j
= 0; j
< HIST_NSLOTS
; j
++)
1763 fputs(" =======", fp
);
1765 for (i
= 0; i
< JSOP_LIMIT
; i
++) {
1766 for (j
= 0; j
< HIST_NSLOTS
; j
++) {
1767 if (slot_ops
[i
][j
] != 0) {
1768 /* Reuse j in the next loop, since we break after. */
1769 fprintf(fp
, "%-8.8s", js_CodeName
[i
]);
1770 for (j
= 0; j
< HIST_NSLOTS
; j
++)
1771 fprintf(fp
, " %7lu", (unsigned long)slot_ops
[i
][j
]);
1780 #endif /* JS_OPSMETER */
1782 #endif /* !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___ */
1784 #ifndef jsinvoke_cpp___
1787 // jsval representation metering: this measures the kinds of jsvals that
1788 // are used as inputs to each JSOp.
1789 namespace reprmeter
{
1799 FUNCTION_INTERPRETED
,
1800 FUNCTION_FASTNATIVE
,
1805 // Return the |repr| value giving the representation of the given jsval.
1809 if (JSVAL_IS_INT(v
))
1811 if (JSVAL_IS_DOUBLE(v
))
1813 if (JSVAL_IS_SPECIAL(v
)) {
1814 return (v
== JSVAL_TRUE
|| v
== JSVAL_FALSE
)
1818 if (JSVAL_IS_STRING(v
))
1821 JS_ASSERT(JSVAL_IS_OBJECT(v
));
1823 JSObject
*obj
= JSVAL_TO_OBJECT(v
);
1824 if (VALUE_IS_FUNCTION(cx
, v
)) {
1825 JSFunction
*fun
= obj
->getFunctionPrivate();
1826 if (FUN_INTERPRETED(fun
))
1827 return FUNCTION_INTERPRETED
;
1828 return FUNCTION_FASTNATIVE
;
1830 // This must come before the general array test, because that
1831 // one subsumes this one.
1834 if (obj
->isDenseArray())
1838 return OBJECT_PLAIN
;
1841 static const char *reprName
[] = { "invalid", "int", "double", "bool", "special",
1842 "string", "null", "object",
1843 "fun:interp", "fun:native"
1844 "array:slow", "array:dense" };
1846 // Logically, a tuple of (JSOp, repr_1, ..., repr_n) where repr_i is
1847 // the |repr| of the ith input to the JSOp.
1849 enum { max_uses
= 16 };
1852 Repr uses
[max_uses
];
1854 OpInput() : op(JSOp(255)) {
1855 for (int i
= 0; i
< max_uses
; ++i
)
1859 OpInput(JSOp op
) : op(op
) {
1860 for (int i
= 0; i
< max_uses
; ++i
)
1865 operator uint32() const {
1867 for (int i
= 0; i
< max_uses
; ++i
)
1868 h
= h
* 7 + uses
[i
] * 13;
1872 bool operator==(const OpInput
&opinput
) const {
1873 if (op
!= opinput
.op
)
1875 for (int i
= 0; i
< max_uses
; ++i
) {
1876 if (uses
[i
] != opinput
.uses
[i
])
1882 OpInput
&operator=(const OpInput
&opinput
) {
1884 for (int i
= 0; i
< max_uses
; ++i
)
1885 uses
[i
] = opinput
.uses
[i
];
1890 typedef HashMap
<OpInput
, uint64
, DefaultHasher
<OpInput
>, SystemAllocPolicy
> OpInputHistogram
;
1892 OpInputHistogram opinputs
;
1893 bool opinputsInitialized
= false;
1895 // Record an OpInput for the current op. This should be called just
1896 // before executing the op.
1898 MeterRepr(JSContext
*cx
)
1900 // Note that we simply ignore the possibility of errors (OOMs)
1901 // using the hash map, since this is only metering code.
1903 if (!opinputsInitialized
) {
1905 opinputsInitialized
= true;
1908 JSOp op
= JSOp(*cx
->regs
->pc
);
1909 int nuses
= js_GetStackUses(&js_CodeSpec
[op
], op
, cx
->regs
->pc
);
1911 // Build the OpInput.
1912 OpInput
opinput(op
);
1913 for (int i
= 0; i
< nuses
; ++i
) {
1914 jsval v
= cx
->regs
->sp
[-nuses
+i
];
1915 opinput
.uses
[i
] = GetRepr(v
);
1918 OpInputHistogram::AddPtr p
= opinputs
.lookupForAdd(opinput
);
1922 opinputs
.add(p
, opinput
, 1);
1928 FILE *f
= fopen("/tmp/reprmeter.txt", "w");
1930 for (OpInputHistogram::Range r
= opinputs
.all(); !r
.empty(); r
.popFront()) {
1931 const OpInput
&o
= r
.front().key
;
1932 uint64 c
= r
.front().value
;
1933 fprintf(f
, "%3d,%s", o
.op
, js_CodeName
[o
.op
]);
1934 for (int i
= 0; i
< OpInput::max_uses
&& o
.uses
[i
] != NONE
; ++i
)
1935 fprintf(f
, ",%s", reprName
[o
.uses
[i
]]);
1936 fprintf(f
, ",%llu\n", c
);
1941 #endif /* JS_REPRMETER */
1943 #define PUSH_COPY(v) do { *regs.sp++ = v; assertSameCompartment(cx, regs.sp[-1]); } while (0)
1944 #define PUSH_NULL() regs.sp++->setNull()
1945 #define PUSH_UNDEFINED() regs.sp++->setUndefined()
1946 #define PUSH_BOOLEAN(b) regs.sp++->setBoolean(b)
1947 #define PUSH_DOUBLE(d) regs.sp++->setDouble(d)
1948 #define PUSH_INT32(i) regs.sp++->setInt32(i)
1949 #define PUSH_STRING(s) do { regs.sp++->setString(s); assertSameCompartment(cx, regs.sp[-1]); } while (0)
1950 #define PUSH_OBJECT(obj) do { regs.sp++->setObject(obj); assertSameCompartment(cx, regs.sp[-1]); } while (0)
1951 #define PUSH_OBJECT_OR_NULL(obj) do { regs.sp++->setObjectOrNull(obj); assertSameCompartment(cx, regs.sp[-1]); } while (0)
1952 #define PUSH_HOLE() regs.sp++->setMagic(JS_ARRAY_HOLE)
1953 #define POP_COPY_TO(v) v = *--regs.sp
1954 #define POP_RETURN_VALUE() regs.fp->setReturnValue(*--regs.sp)
1956 #define POP_BOOLEAN(cx, vp, b) \
1958 vp = ®s.sp[-1]; \
1959 if (vp->isNull()) { \
1961 } else if (vp->isBoolean()) { \
1962 b = vp->toBoolean(); \
1964 b = !!js_ValueToBoolean(*vp); \
1969 #define VALUE_TO_OBJECT(cx, vp, obj) \
1971 if ((vp)->isObject()) { \
1972 obj = &(vp)->toObject(); \
1974 obj = js_ValueToNonNullObject(cx, *(vp)); \
1977 (vp)->setObject(*obj); \
1981 #define FETCH_OBJECT(cx, n, obj) \
1983 Value *vp_ = ®s.sp[n]; \
1984 VALUE_TO_OBJECT(cx, vp_, obj); \
1987 #define DEFAULT_VALUE(cx, n, hint, v) \
1989 JS_ASSERT(v.isObject()); \
1990 JS_ASSERT(v == regs.sp[n]); \
1991 if (!DefaultValue(cx, &v.toObject(), hint, ®s.sp[n])) \
1996 /* Test whether v is an int in the range [-2^31 + 1, 2^31 - 2] */
1997 static JS_ALWAYS_INLINE
bool
1998 CanIncDecWithoutOverflow(int32_t i
)
2000 return (i
> JSVAL_INT_MIN
) && (i
< JSVAL_INT_MAX
);
2004 * Define JS_OPMETER to instrument bytecode succession, generating a .dot file
2005 * on shutdown that shows the graph of significant predecessor/successor pairs
2006 * executed, where the edge labels give the succession counts. The .dot file
2007 * is named by the JS_OPMETER_FILE envariable, and defaults to /tmp/ops.dot.
2009 * Bonus feature: JS_OPMETER also enables counters for stack-addressing ops
2010 * such as JSOP_GETLOCAL, JSOP_INCARG, via METER_SLOT_OP. The resulting counts
2011 * are written to JS_OPMETER_HIST, defaulting to /tmp/ops.hist.
2014 # define METER_OP_INIT(op) /* nothing */
2015 # define METER_OP_PAIR(op1,op2) /* nothing */
2016 # define METER_SLOT_OP(op,slot) /* nothing */
2020 * The second dimension is hardcoded at 256 because we know that many bits fit
2021 * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address
2022 * any particular row.
2024 # define METER_OP_INIT(op) ((op) = JSOP_STOP)
2025 # define METER_OP_PAIR(op1,op2) (js_MeterOpcodePair(op1, op2))
2026 # define METER_SLOT_OP(op,slot) (js_MeterSlotOpcode(op, slot))
2031 # define METER_REPR(cx) (reprmeter::MeterRepr(cx))
2033 # define METER_REPR(cx) ((void) 0)
2034 #endif /* JS_REPRMETER */
2037 * Threaded interpretation via computed goto appears to be well-supported by
2038 * GCC 3 and higher. IBM's C compiler when run with the right options (e.g.,
2039 * -qlanglvl=extended) also supports threading. Ditto the SunPro C compiler.
2040 * Currently it's broken for JS_VERSION < 160, though this isn't worth fixing.
2041 * Add your compiler support macros here.
2043 #ifndef JS_THREADED_INTERP
2044 # if JS_VERSION >= 160 && ( \
2046 (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \
2047 __SUNPRO_C >= 0x570)
2048 # define JS_THREADED_INTERP 1
2050 # define JS_THREADED_INTERP 0
2055 * Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on
2056 * single-thread DEBUG js shell testing to verify property cache hits.
2058 #if defined DEBUG && !defined JS_THREADSAFE
2060 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) \
2062 if (!AssertValidPropertyCacheHit(cx, script, regs, pcoff, obj, pobj, \
2069 AssertValidPropertyCacheHit(JSContext
*cx
, JSScript
*script
, JSFrameRegs
& regs
,
2070 ptrdiff_t pcoff
, JSObject
*start
, JSObject
*found
,
2071 PropertyCacheEntry
*entry
)
2073 uint32 sample
= cx
->runtime
->gcNumber
;
2077 GET_ATOM_FROM_BYTECODE(script
, regs
.pc
, pcoff
, atom
);
2079 atom
= cx
->runtime
->atomState
.lengthAtom
;
2081 JSObject
*obj
, *pobj
;
2085 if (JOF_OPMODE(*regs
.pc
) == JOF_NAME
) {
2086 ok
= js_FindProperty(cx
, ATOM_TO_JSID(atom
), &obj
, &pobj
, &prop
);
2089 ok
= js_LookupProperty(cx
, obj
, ATOM_TO_JSID(atom
), &pobj
, &prop
);
2093 if (cx
->runtime
->gcNumber
!= sample
|| entry
->vshape() != pobj
->shape())
2096 JS_ASSERT(pobj
== found
);
2098 const Shape
*shape
= (Shape
*) prop
;
2099 if (entry
->vword
.isSlot()) {
2100 JS_ASSERT(entry
->vword
.toSlot() == shape
->slot
);
2101 JS_ASSERT(!shape
->isMethod());
2102 } else if (entry
->vword
.isShape()) {
2103 JS_ASSERT(entry
->vword
.toShape() == shape
);
2104 JS_ASSERT_IF(shape
->isMethod(),
2105 &shape
->methodObject() == &pobj
->nativeGetSlot(shape
->slot
).toObject());
2108 JS_ASSERT(entry
->vword
.isFunObj());
2109 JS_ASSERT(!entry
->vword
.isNull());
2110 JS_ASSERT(pobj
->brandedOrHasMethodBarrier());
2111 JS_ASSERT(shape
->hasDefaultGetterOrIsMethod());
2112 JS_ASSERT(pobj
->containsSlot(shape
->slot
));
2113 v
= pobj
->nativeGetSlot(shape
->slot
);
2114 JS_ASSERT(&entry
->vword
.toFunObj() == &v
.toObject());
2116 if (shape
->isMethod()) {
2117 JS_ASSERT(js_CodeSpec
[*regs
.pc
].format
& JOF_CALLOP
);
2118 JS_ASSERT(&shape
->methodObject() == &v
.toObject());
2126 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0)
2130 * Ensure that the intrepreter switch can close call-bytecode cases in the
2131 * same way as non-call bytecodes.
2133 JS_STATIC_ASSERT(JSOP_NAME_LENGTH
== JSOP_CALLNAME_LENGTH
);
2134 JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH
== JSOP_GETFCSLOT_LENGTH
);
2135 JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH
== JSOP_CALLUPVAR_DBG_LENGTH
);
2136 JS_STATIC_ASSERT(JSOP_GETFCSLOT_LENGTH
== JSOP_CALLFCSLOT_LENGTH
);
2137 JS_STATIC_ASSERT(JSOP_GETARG_LENGTH
== JSOP_CALLARG_LENGTH
);
2138 JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH
== JSOP_CALLLOCAL_LENGTH
);
2139 JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH
== JSOP_CALLXMLNAME_LENGTH
);
2142 * Same for debuggable flat closures defined at top level in another function
2143 * or program fragment.
2145 JS_STATIC_ASSERT(JSOP_DEFFUN_FC_LENGTH
== JSOP_DEFFUN_DBGFC_LENGTH
);
2148 * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
2149 * remain distinct for the decompiler. Likewise for JSOP_INIT{PROP,METHOD}.
2151 JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH
== JSOP_SETPROP_LENGTH
);
2152 JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH
== JSOP_SETMETHOD_LENGTH
);
2153 JS_STATIC_ASSERT(JSOP_INITPROP_LENGTH
== JSOP_INITMETHOD_LENGTH
);
2155 /* See TRY_BRANCH_AFTER_COND. */
2156 JS_STATIC_ASSERT(JSOP_IFNE_LENGTH
== JSOP_IFEQ_LENGTH
);
2157 JS_STATIC_ASSERT(JSOP_IFNE
== JSOP_IFEQ
+ 1);
2159 /* For the fastest case inder JSOP_INCNAME, etc. */
2160 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH
== JSOP_DECNAME_LENGTH
);
2161 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH
== JSOP_NAMEINC_LENGTH
);
2162 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH
== JSOP_NAMEDEC_LENGTH
);
2165 # define ABORT_RECORDING(cx, reason) \
2167 if (TRACE_RECORDER(cx)) \
2168 AbortRecording(cx, reason); \
2171 # define ABORT_RECORDING(cx, reason) ((void) 0)
2175 * Inline fast paths for iteration. js_IteratorMore and js_IteratorNext handle
2176 * all cases, but we inline the most frequently taken paths here.
2179 IteratorMore(JSContext
*cx
, JSObject
*iterobj
, bool *cond
, Value
*rval
)
2181 if (iterobj
->getClass() == &js_IteratorClass
) {
2182 NativeIterator
*ni
= (NativeIterator
*) iterobj
->getPrivate();
2183 if (ni
->isKeyIter()) {
2184 *cond
= (ni
->props_cursor
< ni
->props_end
);
2188 if (!js_IteratorMore(cx
, iterobj
, rval
))
2190 *cond
= rval
->isTrue();
2195 IteratorNext(JSContext
*cx
, JSObject
*iterobj
, Value
*rval
)
2197 if (iterobj
->getClass() == &js_IteratorClass
) {
2198 NativeIterator
*ni
= (NativeIterator
*) iterobj
->getPrivate();
2199 if (ni
->isKeyIter()) {
2200 JS_ASSERT(ni
->props_cursor
< ni
->props_end
);
2201 jsid id
= *ni
->current();
2202 if (JSID_IS_ATOM(id
)) {
2203 rval
->setString(JSID_TO_STRING(id
));
2207 /* Take the slow path if we have to stringify a numeric property name. */
2210 return js_IteratorNext(cx
, iterobj
, rval
);
2214 ScriptPrologue(JSContext
*cx
, JSStackFrame
*fp
)
2216 if (fp
->isConstructing()) {
2217 JSObject
*obj
= js_CreateThisForFunction(cx
, &fp
->callee());
2220 fp
->functionThis().setObject(*obj
);
2222 if (fp
->isExecuteFrame()) {
2223 if (JSInterpreterHook hook
= cx
->debugHooks
->executeHook
)
2224 fp
->setHookData(hook(cx
, fp
, JS_TRUE
, 0, cx
->debugHooks
->executeHookData
));
2226 if (JSInterpreterHook hook
= cx
->debugHooks
->callHook
)
2227 fp
->setHookData(hook(cx
, fp
, JS_TRUE
, 0, cx
->debugHooks
->callHookData
));
2228 Probes::enterJSFun(cx
, fp
->maybeFun(), fp
->maybeScript());
2236 JS_REQUIRES_STACK JS_NEVER_INLINE
bool
2237 Interpret(JSContext
*cx
, JSStackFrame
*entryFrame
, uintN inlineCallCount
, JSInterpMode interpMode
)
2240 TraceVisStateObj
tvso(cx
, S_INTERP
);
2242 JSAutoResolveFlags
rf(cx
, JSRESOLVE_INFER
);
2246 * We call this macro from BEGIN_CASE in threaded interpreters,
2247 * and before entering the switch in non-threaded interpreters.
2248 * However, reaching such points doesn't mean we've actually
2249 * fetched an OP from the instruction stream: some opcodes use
2250 * 'op=x; DO_OP()' to let another opcode's implementation finish
2251 * their work, and many opcodes share entry points with a run of
2252 * consecutive BEGIN_CASEs.
2254 * Take care to trace OP only when it is the opcode fetched from
2255 * the instruction stream, so the trace matches what one would
2256 * expect from looking at the code. (We do omit POPs after SETs;
2257 * unfortunate, but not worth fixing.)
2259 # define LOG_OPCODE(OP) JS_BEGIN_MACRO \
2260 if (JS_UNLIKELY(cx->logfp != NULL) && \
2265 # define LOG_OPCODE(OP) ((void) 0)
2269 * Macros for threaded interpreter loop
2271 #if JS_THREADED_INTERP
2272 static void *const normalJumpTable
[] = {
2273 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2274 JS_EXTENSION &&L_##op,
2275 # include "jsopcode.tbl"
2279 static void *const interruptJumpTable
[] = {
2280 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2281 JS_EXTENSION &&interrupt,
2282 # include "jsopcode.tbl"
2286 register void * const *jumpTable
= normalJumpTable
;
2288 METER_OP_INIT(op
); /* to nullify first METER_OP_PAIR */
2290 # define ENABLE_INTERRUPTS() ((void) (jumpTable = interruptJumpTable))
2293 # define CHECK_RECORDER() \
2294 JS_ASSERT_IF(TRACE_RECORDER(cx), jumpTable == interruptJumpTable)
2296 # define CHECK_RECORDER() ((void)0)
2299 # define DO_OP() JS_BEGIN_MACRO \
2301 JS_EXTENSION_(goto *jumpTable[op]); \
2303 # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
2304 METER_OP_PAIR(op, JSOp(regs.pc[n])); \
2305 op = (JSOp) *(regs.pc += (n)); \
2310 # define BEGIN_CASE(OP) L_##OP: LOG_OPCODE(OP); CHECK_RECORDER();
2311 # define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH);
2312 # define END_VARLEN_CASE DO_NEXT_OP(len);
2313 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP) \
2314 JS_ASSERT(js_CodeSpec[OP].length == 1); \
2315 op = (JSOp) *++regs.pc; \
2318 # define END_EMPTY_CASES
2320 #else /* !JS_THREADED_INTERP */
2322 register intN switchMask
= 0;
2325 # define ENABLE_INTERRUPTS() ((void) (switchMask = -1))
2328 # define CHECK_RECORDER() \
2329 JS_ASSERT_IF(TRACE_RECORDER(cx), switchMask == -1)
2331 # define CHECK_RECORDER() ((void)0)
2334 # define DO_OP() goto do_op
2335 # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
2336 JS_ASSERT((n) == len); \
2340 # define BEGIN_CASE(OP) case OP: CHECK_RECORDER();
2341 # define END_CASE(OP) END_CASE_LEN(OP##_LENGTH)
2342 # define END_CASE_LEN(n) END_CASE_LENX(n)
2343 # define END_CASE_LENX(n) END_CASE_LEN##n
2346 * To share the code for all len == 1 cases we use the specialized label with
2347 * code that falls through to advance_pc: .
2349 # define END_CASE_LEN1 goto advance_pc_by_one;
2350 # define END_CASE_LEN2 len = 2; goto advance_pc;
2351 # define END_CASE_LEN3 len = 3; goto advance_pc;
2352 # define END_CASE_LEN4 len = 4; goto advance_pc;
2353 # define END_CASE_LEN5 len = 5; goto advance_pc;
2354 # define END_VARLEN_CASE goto advance_pc;
2355 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)
2356 # define END_EMPTY_CASES goto advance_pc_by_one;
2358 #endif /* !JS_THREADED_INTERP */
2360 #define LOAD_ATOM(PCOFF, atom) \
2362 JS_ASSERT(regs.fp->hasImacropc() \
2363 ? atoms == COMMON_ATOMS_START(&rt->atomState) && \
2364 GET_INDEX(regs.pc + PCOFF) < js_common_atom_count \
2365 : (size_t)(atoms - script->atomMap.vector) < \
2366 (size_t)(script->atomMap.length - \
2367 GET_INDEX(regs.pc + PCOFF))); \
2368 atom = atoms[GET_INDEX(regs.pc + PCOFF)]; \
2371 #define GET_FULL_INDEX(PCOFF) \
2372 (atoms - script->atomMap.vector + GET_INDEX(regs.pc + (PCOFF)))
2374 #define LOAD_OBJECT(PCOFF, obj) \
2375 (obj = script->getObject(GET_FULL_INDEX(PCOFF)))
2377 #define LOAD_FUNCTION(PCOFF) \
2378 (fun = script->getFunction(GET_FULL_INDEX(PCOFF)))
2380 #define LOAD_DOUBLE(PCOFF, dbl) \
2381 (dbl = script->getConst(GET_FULL_INDEX(PCOFF)).toDouble())
2386 #if JS_THREADED_INTERP
2387 #define MONITOR_BRANCH_TRACEVIS \
2389 if (jumpTable != interruptJumpTable) \
2390 EnterTraceVisState(cx, S_RECORD, R_NONE); \
2392 #else /* !JS_THREADED_INTERP */
2393 #define MONITOR_BRANCH_TRACEVIS \
2395 EnterTraceVisState(cx, S_RECORD, R_NONE); \
2399 #define MONITOR_BRANCH_TRACEVIS
2402 #define RESTORE_INTERP_VARS() \
2404 script = regs.fp->script(); \
2405 argv = regs.fp->maybeFormalArgs(); \
2406 atoms = FrameAtomBase(cx, regs.fp); \
2407 JS_ASSERT(cx->regs == ®s); \
2408 if (cx->isExceptionPending()) \
2412 #define MONITOR_BRANCH() \
2414 if (TRACING_ENABLED(cx)) { \
2415 MonitorResult r = MonitorLoopEdge(cx, inlineCallCount); \
2416 if (r == MONITOR_RECORDING) { \
2417 JS_ASSERT(TRACE_RECORDER(cx)); \
2418 JS_ASSERT(!TRACE_PROFILER(cx)); \
2419 MONITOR_BRANCH_TRACEVIS; \
2420 ENABLE_INTERRUPTS(); \
2421 CLEAR_LEAVE_ON_TRACE_POINT(); \
2423 RESTORE_INTERP_VARS(); \
2424 JS_ASSERT_IF(cx->isExceptionPending(), r == MONITOR_ERROR); \
2425 if (r == MONITOR_ERROR) \
2430 #else /* !JS_TRACER */
2432 #define MONITOR_BRANCH() ((void) 0)
2434 #endif /* !JS_TRACER */
2437 * Prepare to call a user-supplied branch handler, and abort the script
2438 * if it returns false.
2440 #define CHECK_BRANCH() \
2442 if (JS_THREAD_DATA(cx)->interruptFlags && !js_HandleExecutionInterrupt(cx)) \
2446 #ifndef TRACE_RECORDER
2447 #define TRACE_RECORDER(cx) (false)
2448 #define TRACE_PROFILER(cx) (false)
2451 #if defined(JS_TRACER) && defined(JS_METHODJIT)
2452 # define LEAVE_ON_SAFE_POINT() \
2454 JS_ASSERT_IF(leaveOnSafePoint, !TRACE_RECORDER(cx)); \
2455 JS_ASSERT_IF(leaveOnSafePoint, !TRACE_PROFILER(cx)); \
2456 JS_ASSERT_IF(leaveOnSafePoint, interpMode != JSINTERP_NORMAL); \
2457 if (leaveOnSafePoint && !regs.fp->hasImacropc() && \
2458 script->maybeNativeCodeForPC(regs.fp->isConstructing(), regs.pc)) { \
2459 JS_ASSERT(!TRACE_RECORDER(cx)); \
2460 interpReturnOK = true; \
2461 goto leave_on_safe_point; \
2465 # define LEAVE_ON_SAFE_POINT() /* nop */
2471 op = (JSOp) *regs.pc; \
2474 if (op == JSOP_NOTRACE) { \
2475 if (TRACE_RECORDER(cx) || TRACE_PROFILER(cx)) { \
2477 op = (JSOp) *regs.pc; \
2479 } else if (op == JSOP_TRACE) { \
2481 op = (JSOp) *regs.pc; \
2484 LEAVE_ON_SAFE_POINT(); \
2488 #define CHECK_INTERRUPT_HANDLER() \
2490 if (cx->debugHooks->interruptHook) \
2491 ENABLE_INTERRUPTS(); \
2494 JSFrameRegs regs
= *cx
->regs
;
2496 /* Repoint cx->regs to a local variable for faster access. */
2497 struct InterpExitGuard
{
2499 const JSFrameRegs
®s
;
2500 JSFrameRegs
*prevContextRegs
;
2501 InterpExitGuard(JSContext
*cx
, JSFrameRegs
®s
)
2502 : cx(cx
), regs(regs
), prevContextRegs(cx
->regs
) {
2503 cx
->setCurrentRegs(®s
);
2506 ~InterpExitGuard() {
2508 JS_ASSERT(cx
->regs
== ®s
);
2509 *prevContextRegs
= regs
;
2510 cx
->setCurrentRegs(prevContextRegs
);
2512 } interpGuard(cx
, regs
);
2514 /* Copy in hot values that change infrequently. */
2515 JSRuntime
*const rt
= cx
->runtime
;
2516 JSScript
*script
= regs
.fp
->script();
2517 Value
*argv
= regs
.fp
->maybeFormalArgs();
2518 CHECK_INTERRUPT_HANDLER();
2520 #if defined(JS_TRACER) && defined(JS_METHODJIT)
2521 bool leaveOnSafePoint
= (interpMode
== JSINTERP_SAFEPOINT
);
2522 # define CLEAR_LEAVE_ON_TRACE_POINT() ((void) (leaveOnSafePoint = false))
2524 # define CLEAR_LEAVE_ON_TRACE_POINT() ((void) 0)
2528 entryFrame
= regs
.fp
;
2531 * Initialize the index segment register used by LOAD_ATOM and
2532 * GET_FULL_INDEX macros below. As a register we use a pointer based on
2533 * the atom map to turn frequently executed LOAD_ATOM into simple array
2534 * access. For less frequent object and regexp loads we have to recover
2535 * the segment from atoms pointer first.
2537 JSAtom
**atoms
= script
->atomMap
.vector
;
2539 #if JS_HAS_GENERATORS
2540 if (JS_UNLIKELY(regs
.fp
->isGeneratorFrame())) {
2541 JS_ASSERT(interpGuard
.prevContextRegs
== &cx
->generatorFor(regs
.fp
)->regs
);
2542 JS_ASSERT((size_t) (regs
.pc
- script
->code
) <= script
->length
);
2543 JS_ASSERT((size_t) (regs
.sp
- regs
.fp
->base()) <= StackDepth(script
));
2546 * To support generator_throw and to catch ignored exceptions,
2547 * fail if cx->isExceptionPending() is true.
2549 if (cx
->isExceptionPending())
2556 * The method JIT may have already initiated a recording, in which case
2557 * there should already be a valid recorder. Otherwise...
2558 * we cannot reenter the interpreter while recording.
2560 if (interpMode
== JSINTERP_RECORD
) {
2561 JS_ASSERT(TRACE_RECORDER(cx
));
2562 JS_ASSERT(!TRACE_PROFILER(cx
));
2563 ENABLE_INTERRUPTS();
2564 } else if (interpMode
== JSINTERP_PROFILE
) {
2565 ENABLE_INTERRUPTS();
2566 } else if (TRACE_RECORDER(cx
)) {
2567 AbortRecording(cx
, "attempt to reenter interpreter while recording");
2569 } else if (TRACE_PROFILER(cx
)) {
2574 if (regs
.fp
->hasImacropc())
2575 atoms
= COMMON_ATOMS_START(&rt
->atomState
);
2578 /* Don't call the script prologue if executing between Method and Trace JIT. */
2579 if (interpMode
== JSINTERP_NORMAL
) {
2580 JS_ASSERT_IF(!regs
.fp
->isGeneratorFrame(), regs
.pc
== script
->code
);
2581 if (!ScriptPrologue(cx
, regs
.fp
))
2585 CHECK_INTERRUPT_HANDLER();
2587 /* State communicated between non-local jumps: */
2588 JSBool interpReturnOK
;
2589 JSAtom
*atomNotDefined
;
2592 * It is important that "op" be initialized before calling DO_OP because
2593 * it is possible for "op" to be specially assigned during the normal
2594 * processing of an opcode while looping. We rely on DO_NEXT_OP to manage
2595 * "op" correctly in all other cases.
2601 /* Check for too deep of a native thread stack. */
2604 JS_CHECK_RECURSION(cx
, do {
2605 if (TRACE_RECORDER(cx
))
2606 AbortRecording(cx
, "too much recursion");
2607 if (TRACE_PROFILER(cx
))
2612 JS_CHECK_RECURSION(cx
, do {
2613 if (TRACE_RECORDER(cx
))
2614 AbortRecording(cx
, "too much recursion");
2619 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
2622 #if JS_THREADED_INTERP
2628 #if JS_THREADED_INTERP
2630 * This is a loop, but it does not look like a loop. The loop-closing
2631 * jump is distributed throughout goto *jumpTable[op] inside of DO_OP.
2632 * When interrupts are enabled, jumpTable is set to interruptJumpTable
2633 * where all jumps point to the interrupt label. The latter, after
2634 * calling the interrupt handler, dispatches through normalJumpTable to
2635 * continue the normal bytecode processing.
2638 #else /* !JS_THREADED_INTERP */
2641 JS_ASSERT(js_CodeSpec
[op
].length
== 1);
2645 op
= (JSOp
) *regs
.pc
;
2650 switchOp
= intN(op
) | switchMask
;
2655 #if JS_THREADED_INTERP
2657 #else /* !JS_THREADED_INTERP */
2659 JS_ASSERT(switchMask
== -1);
2660 #endif /* !JS_THREADED_INTERP */
2662 bool moreInterrupts
= false;
2663 JSInterruptHook hook
= cx
->debugHooks
->interruptHook
;
2666 if (TRACE_RECORDER(cx
))
2667 AbortRecording(cx
, "interrupt hook");
2669 if (TRACE_PROFILER(cx
))
2674 switch (hook(cx
, script
, regs
.pc
, Jsvalify(&rval
),
2675 cx
->debugHooks
->interruptHookData
)) {
2678 case JSTRAP_CONTINUE
:
2681 regs
.fp
->setReturnValue(rval
);
2682 interpReturnOK
= JS_TRUE
;
2685 cx
->setPendingException(rval
);
2689 moreInterrupts
= true;
2694 if (LoopProfile
*prof
= TRACE_PROFILER(cx
)) {
2695 JS_ASSERT(!TRACE_RECORDER(cx
));
2696 LoopProfile::ProfileAction act
= prof
->profileOperation(cx
, op
);
2698 case LoopProfile::ProfComplete
:
2699 if (interpMode
!= JSINTERP_NORMAL
) {
2700 leaveOnSafePoint
= true;
2701 LEAVE_ON_SAFE_POINT();
2705 moreInterrupts
= true;
2710 if (TraceRecorder
* tr
= TRACE_RECORDER(cx
)) {
2711 JS_ASSERT(!TRACE_PROFILER(cx
));
2712 AbortableRecordingStatus status
= tr
->monitorRecording(op
);
2713 JS_ASSERT_IF(cx
->isExceptionPending(), status
== ARECORD_ERROR
);
2715 if (interpMode
!= JSINTERP_NORMAL
) {
2716 JS_ASSERT(interpMode
== JSINTERP_RECORD
|| JSINTERP_SAFEPOINT
);
2718 case ARECORD_IMACRO_ABORTED
:
2719 case ARECORD_ABORTED
:
2720 case ARECORD_COMPLETED
:
2723 leaveOnSafePoint
= true;
2724 LEAVE_ON_SAFE_POINT();
2733 case ARECORD_CONTINUE
:
2734 moreInterrupts
= true;
2736 case ARECORD_IMACRO
:
2737 case ARECORD_IMACRO_ABORTED
:
2738 atoms
= COMMON_ATOMS_START(&rt
->atomState
);
2739 op
= JSOp(*regs
.pc
);
2740 CLEAR_LEAVE_ON_TRACE_POINT();
2741 if (status
== ARECORD_IMACRO
)
2742 DO_OP(); /* keep interrupting for op. */
2745 // The code at 'error:' aborts the recording.
2747 case ARECORD_ABORTED
:
2748 case ARECORD_COMPLETED
:
2751 /* A 'stop' error should have already aborted recording. */
2753 JS_NOT_REACHED("Bad recording status");
2756 #endif /* !JS_TRACER */
2758 #if JS_THREADED_INTERP
2760 if (!moreInterrupts
)
2761 ExitTraceVisState(cx
, R_ABORT
);
2763 jumpTable
= moreInterrupts
? interruptJumpTable
: normalJumpTable
;
2764 JS_EXTENSION_(goto *normalJumpTable
[op
]);
2766 switchMask
= moreInterrupts
? -1 : 0;
2767 switchOp
= intN(op
);
2772 /* No-ops for ease of decompilation. */
2773 ADD_EMPTY_CASE(JSOP_NOP
)
2774 ADD_EMPTY_CASE(JSOP_CONDSWITCH
)
2775 ADD_EMPTY_CASE(JSOP_TRY
)
2776 #if JS_HAS_XML_SUPPORT
2777 ADD_EMPTY_CASE(JSOP_STARTXML
)
2778 ADD_EMPTY_CASE(JSOP_STARTXMLEXPR
)
2780 ADD_EMPTY_CASE(JSOP_NULLBLOCKCHAIN
)
2783 BEGIN_CASE(JSOP_TRACE
)
2784 BEGIN_CASE(JSOP_NOTRACE
)
2785 LEAVE_ON_SAFE_POINT();
2786 END_CASE(JSOP_TRACE
)
2788 /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
2789 BEGIN_CASE(JSOP_LINENO
)
2790 END_CASE(JSOP_LINENO
)
2792 BEGIN_CASE(JSOP_BLOCKCHAIN
)
2793 END_CASE(JSOP_BLOCKCHAIN
)
2795 BEGIN_CASE(JSOP_PUSH
)
2799 BEGIN_CASE(JSOP_POP
)
2803 BEGIN_CASE(JSOP_POPN
)
2805 regs
.sp
-= GET_UINT16(regs
.pc
);
2807 JS_ASSERT(regs
.fp
->base() <= regs
.sp
);
2808 JSObject
*obj
= GetBlockChain(cx
, regs
.fp
);
2810 OBJ_BLOCK_DEPTH(cx
, obj
) + OBJ_BLOCK_COUNT(cx
, obj
)
2811 <= (size_t) (regs
.sp
- regs
.fp
->base()));
2812 for (obj
= ®s
.fp
->scopeChain(); obj
; obj
= obj
->getParent()) {
2813 Class
*clasp
= obj
->getClass();
2814 if (clasp
!= &js_BlockClass
&& clasp
!= &js_WithClass
)
2816 if (obj
->getPrivate() != js_FloatingFrameIfGenerator(cx
, regs
.fp
))
2818 JS_ASSERT(regs
.fp
->base() + OBJ_BLOCK_DEPTH(cx
, obj
)
2819 + ((clasp
== &js_BlockClass
)
2820 ? OBJ_BLOCK_COUNT(cx
, obj
)
2828 BEGIN_CASE(JSOP_SETRVAL
)
2829 BEGIN_CASE(JSOP_POPV
)
2833 BEGIN_CASE(JSOP_ENTERWITH
)
2834 if (!js_EnterWith(cx
, -1, JSOP_ENTERWITH
, JSOP_ENTERWITH_LENGTH
))
2838 * We must ensure that different "with" blocks have different stack depth
2839 * associated with them. This allows the try handler search to properly
2840 * recover the scope chain. Thus we must keep the stack at least at the
2843 * We set sp[-1] to the current "with" object to help asserting the
2844 * enter/leave balance in [leavewith].
2846 regs
.sp
[-1].setObject(regs
.fp
->scopeChain());
2847 END_CASE(JSOP_ENTERWITH
)
2849 BEGIN_CASE(JSOP_LEAVEWITH
)
2850 JS_ASSERT(®s
.sp
[-1].toObject() == ®s
.fp
->scopeChain());
2853 END_CASE(JSOP_LEAVEWITH
)
2855 BEGIN_CASE(JSOP_RETURN
)
2859 BEGIN_CASE(JSOP_RETRVAL
) /* fp return value already set */
2860 BEGIN_CASE(JSOP_STOP
)
2863 * When the inlined frame exits with an exception or an error, ok will be
2864 * false after the inline_return label.
2869 if (regs
.fp
->hasImacropc()) {
2871 * If we are at the end of an imacro, return to its caller in the
2874 JS_ASSERT(op
== JSOP_STOP
);
2875 JS_ASSERT((uintN
)(regs
.sp
- regs
.fp
->slots()) <= script
->nslots
);
2876 jsbytecode
*imacpc
= regs
.fp
->imacropc();
2877 regs
.pc
= imacpc
+ js_CodeSpec
[*imacpc
].length
;
2878 regs
.fp
->clearImacropc();
2879 LEAVE_ON_SAFE_POINT();
2880 atoms
= script
->atomMap
.vector
;
2881 op
= JSOp(*regs
.pc
);
2886 interpReturnOK
= true;
2887 if (entryFrame
!= regs
.fp
)
2890 JS_ASSERT(!js_IsActiveWithOrBlock(cx
, ®s
.fp
->scopeChain(), 0));
2891 interpReturnOK
= ScriptEpilogue(cx
, regs
.fp
, interpReturnOK
);
2892 CHECK_INTERRUPT_HANDLER();
2894 /* The JIT inlines ScriptEpilogue. */
2896 Value
*newsp
= regs
.fp
->actualArgs() - 1;
2897 newsp
[-1] = regs
.fp
->returnValue();
2898 cx
->stack().popInlineFrame(cx
, regs
.fp
->prev(), newsp
);
2900 /* Sync interpreter registers. */
2901 script
= regs
.fp
->script();
2902 argv
= regs
.fp
->maybeFormalArgs();
2903 atoms
= FrameAtomBase(cx
, regs
.fp
);
2905 /* Resume execution in the calling frame. */
2906 JS_ASSERT(inlineCallCount
);
2908 if (JS_LIKELY(interpReturnOK
)) {
2909 JS_ASSERT(js_CodeSpec
[js_GetOpcode(cx
, script
, regs
.pc
)].length
2910 == JSOP_CALL_LENGTH
);
2911 TRACE_0(LeaveFrame
);
2912 len
= JSOP_CALL_LENGTH
;
2917 JS_ASSERT(regs
.sp
== regs
.fp
->base());
2919 interpReturnOK
= true;
2923 BEGIN_CASE(JSOP_DEFAULT
)
2926 BEGIN_CASE(JSOP_GOTO
)
2928 len
= GET_JUMP_OFFSET(regs
.pc
);
2933 BEGIN_CASE(JSOP_IFEQ
)
2937 POP_BOOLEAN(cx
, _
, cond
);
2938 if (cond
== false) {
2939 len
= GET_JUMP_OFFSET(regs
.pc
);
2945 BEGIN_CASE(JSOP_IFNE
)
2949 POP_BOOLEAN(cx
, _
, cond
);
2950 if (cond
!= false) {
2951 len
= GET_JUMP_OFFSET(regs
.pc
);
2961 POP_BOOLEAN(cx
, vp
, cond
);
2963 len
= GET_JUMP_OFFSET(regs
.pc
);
2970 BEGIN_CASE(JSOP_AND
)
2974 POP_BOOLEAN(cx
, vp
, cond
);
2975 if (cond
== false) {
2976 len
= GET_JUMP_OFFSET(regs
.pc
);
2983 BEGIN_CASE(JSOP_DEFAULTX
)
2986 BEGIN_CASE(JSOP_GOTOX
)
2988 len
= GET_JUMPX_OFFSET(regs
.pc
);
2991 END_CASE(JSOP_GOTOX
);
2993 BEGIN_CASE(JSOP_IFEQX
)
2997 POP_BOOLEAN(cx
, _
, cond
);
2998 if (cond
== false) {
2999 len
= GET_JUMPX_OFFSET(regs
.pc
);
3003 END_CASE(JSOP_IFEQX
)
3005 BEGIN_CASE(JSOP_IFNEX
)
3009 POP_BOOLEAN(cx
, _
, cond
);
3010 if (cond
!= false) {
3011 len
= GET_JUMPX_OFFSET(regs
.pc
);
3015 END_CASE(JSOP_IFNEX
)
3017 BEGIN_CASE(JSOP_ORX
)
3021 POP_BOOLEAN(cx
, vp
, cond
);
3023 len
= GET_JUMPX_OFFSET(regs
.pc
);
3030 BEGIN_CASE(JSOP_ANDX
)
3034 POP_BOOLEAN(cx
, vp
, cond
);
3035 if (cond
== JS_FALSE
) {
3036 len
= GET_JUMPX_OFFSET(regs
.pc
);
3044 * If the index value at sp[n] is not an int that fits in a jsval, it could
3045 * be an object (an XML QName, AttributeName, or AnyName), but only if we are
3046 * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a
3049 #define FETCH_ELEMENT_ID(obj, n, id) \
3051 const Value &idval_ = regs.sp[n]; \
3053 if (ValueFitsInInt32(idval_, &i_) && INT_FITS_IN_JSID(i_)) { \
3054 id = INT_TO_JSID(i_); \
3056 if (!js_InternNonIntElementId(cx, obj, idval_, &id, ®s.sp[n])) \
3061 #define TRY_BRANCH_AFTER_COND(cond,spdec) \
3063 JS_ASSERT(js_CodeSpec[op].length == 1); \
3064 uintN diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ; \
3067 if (cond == (diff_ != 0)) { \
3069 len = GET_JUMP_OFFSET(regs.pc); \
3072 len = 1 + JSOP_IFEQ_LENGTH; \
3079 const Value
&rref
= regs
.sp
[-1];
3080 if (!rref
.isObject()) {
3081 js_ReportValueError(cx
, JSMSG_IN_NOT_OBJECT
, -1, rref
, NULL
);
3084 JSObject
*obj
= &rref
.toObject();
3086 FETCH_ELEMENT_ID(obj
, -2, id
);
3089 if (!obj
->lookupProperty(cx
, id
, &obj2
, &prop
))
3091 bool cond
= prop
!= NULL
;
3092 TRY_BRANCH_AFTER_COND(cond
, 2);
3094 regs
.sp
[-1].setBoolean(cond
);
3098 BEGIN_CASE(JSOP_ITER
)
3100 JS_ASSERT(regs
.sp
> regs
.fp
->base());
3101 uintN flags
= regs
.pc
[1];
3102 if (!js_ValueToIterator(cx
, flags
, ®s
.sp
[-1]))
3104 CHECK_INTERRUPT_HANDLER();
3105 JS_ASSERT(!regs
.sp
[-1].isPrimitive());
3109 BEGIN_CASE(JSOP_MOREITER
)
3111 JS_ASSERT(regs
.sp
- 1 >= regs
.fp
->base());
3112 JS_ASSERT(regs
.sp
[-1].isObject());
3115 if (!IteratorMore(cx
, ®s
.sp
[-2].toObject(), &cond
, ®s
.sp
[-1]))
3117 CHECK_INTERRUPT_HANDLER();
3118 regs
.sp
[-1].setBoolean(cond
);
3120 END_CASE(JSOP_MOREITER
)
3122 BEGIN_CASE(JSOP_ENDITER
)
3124 JS_ASSERT(regs
.sp
- 1 >= regs
.fp
->base());
3125 bool ok
= !!js_CloseIterator(cx
, ®s
.sp
[-1].toObject());
3130 END_CASE(JSOP_ENDITER
)
3132 BEGIN_CASE(JSOP_FORARG
)
3134 JS_ASSERT(regs
.sp
- 1 >= regs
.fp
->base());
3135 uintN slot
= GET_ARGNO(regs
.pc
);
3136 JS_ASSERT(slot
< regs
.fp
->numFormalArgs());
3137 JS_ASSERT(regs
.sp
[-1].isObject());
3138 if (!IteratorNext(cx
, ®s
.sp
[-1].toObject(), &argv
[slot
]))
3141 END_CASE(JSOP_FORARG
)
3143 BEGIN_CASE(JSOP_FORLOCAL
)
3145 JS_ASSERT(regs
.sp
- 1 >= regs
.fp
->base());
3146 uintN slot
= GET_SLOTNO(regs
.pc
);
3147 JS_ASSERT(slot
< regs
.fp
->numSlots());
3148 JS_ASSERT(regs
.sp
[-1].isObject());
3149 if (!IteratorNext(cx
, ®s
.sp
[-1].toObject(), ®s
.fp
->slots()[slot
]))
3152 END_CASE(JSOP_FORLOCAL
)
3154 BEGIN_CASE(JSOP_FORNAME
)
3155 BEGIN_CASE(JSOP_FORGNAME
)
3157 JS_ASSERT(regs
.sp
- 1 >= regs
.fp
->base());
3160 jsid id
= ATOM_TO_JSID(atom
);
3161 JSObject
*obj
, *obj2
;
3163 if (!js_FindProperty(cx
, id
, &obj
, &obj2
, &prop
))
3167 AutoValueRooter
tvr(cx
);
3168 JS_ASSERT(regs
.sp
[-1].isObject());
3169 if (!IteratorNext(cx
, ®s
.sp
[-1].toObject(), tvr
.addr()))
3171 if (!obj
->setProperty(cx
, id
, tvr
.addr(), script
->strictModeCode
))
3175 END_CASE(JSOP_FORNAME
)
3177 BEGIN_CASE(JSOP_FORPROP
)
3179 JS_ASSERT(regs
.sp
- 2 >= regs
.fp
->base());
3182 jsid id
= ATOM_TO_JSID(atom
);
3184 FETCH_OBJECT(cx
, -1, obj
);
3186 AutoValueRooter
tvr(cx
);
3187 JS_ASSERT(regs
.sp
[-2].isObject());
3188 if (!IteratorNext(cx
, ®s
.sp
[-2].toObject(), tvr
.addr()))
3190 if (!obj
->setProperty(cx
, id
, tvr
.addr(), script
->strictModeCode
))
3195 END_CASE(JSOP_FORPROP
)
3197 BEGIN_CASE(JSOP_FORELEM
)
3199 * JSOP_FORELEM simply dups the property identifier at top of stack and
3200 * lets the subsequent JSOP_ENUMELEM opcode sequence handle the left-hand
3201 * side expression evaluation and assignment. This opcode exists solely to
3202 * help the decompiler.
3204 JS_ASSERT(regs
.sp
- 1 >= regs
.fp
->base());
3205 JS_ASSERT(regs
.sp
[-1].isObject());
3207 if (!IteratorNext(cx
, ®s
.sp
[-2].toObject(), ®s
.sp
[-1]))
3209 END_CASE(JSOP_FORELEM
)
3211 BEGIN_CASE(JSOP_DUP
)
3213 JS_ASSERT(regs
.sp
> regs
.fp
->base());
3214 const Value
&rref
= regs
.sp
[-1];
3219 BEGIN_CASE(JSOP_DUP2
)
3221 JS_ASSERT(regs
.sp
- 2 >= regs
.fp
->base());
3222 const Value
&lref
= regs
.sp
[-2];
3223 const Value
&rref
= regs
.sp
[-1];
3229 BEGIN_CASE(JSOP_SWAP
)
3231 JS_ASSERT(regs
.sp
- 2 >= regs
.fp
->base());
3232 Value
&lref
= regs
.sp
[-2];
3233 Value
&rref
= regs
.sp
[-1];
3238 BEGIN_CASE(JSOP_PICK
)
3240 jsint i
= regs
.pc
[1];
3241 JS_ASSERT(regs
.sp
- (i
+1) >= regs
.fp
->base());
3242 Value lval
= regs
.sp
[-(i
+1)];
3243 memmove(regs
.sp
- (i
+1), regs
.sp
- i
, sizeof(Value
)*i
);
3248 #define NATIVE_GET(cx,obj,pobj,shape,getHow,vp) \
3250 if (shape->isDataDescriptor() && shape->hasDefaultGetter()) { \
3251 /* Fast path for Object instance properties. */ \
3252 JS_ASSERT((shape)->slot != SHAPE_INVALID_SLOT || \
3253 !shape->hasDefaultSetter()); \
3254 if (((shape)->slot != SHAPE_INVALID_SLOT)) \
3255 *(vp) = (pobj)->nativeGetSlot((shape)->slot); \
3257 (vp)->setUndefined(); \
3259 if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp)) \
3264 #define NATIVE_SET(cx,obj,shape,entry,vp) \
3266 if (shape->hasDefaultSetter() && \
3267 (shape)->slot != SHAPE_INVALID_SLOT && \
3268 !(obj)->brandedOrHasMethodBarrier()) { \
3269 /* Fast path for, e.g., plain Object instance properties. */ \
3270 (obj)->nativeSetSlot((shape)->slot, *vp); \
3272 if (!js_NativeSet(cx, obj, shape, false, vp)) \
3278 * Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is
3279 * the constant length of the SET opcode sequence, and spdec is the constant
3280 * by which to decrease the stack pointer to pop all of the SET op's operands.
3282 * NB: unlike macros that could conceivably be replaced by functions (ignoring
3283 * goto error), where a call should not have to be braced in order to expand
3284 * correctly (e.g., in if (cond) FOO(); else BAR()), these three macros lack
3285 * JS_{BEGIN,END}_MACRO brackets. They are also indented so as to align with
3286 * nearby opcode code.
3288 #define SKIP_POP_AFTER_SET(oplen,spdec) \
3289 if (regs.pc[oplen] == JSOP_POP) { \
3291 regs.pc += oplen + JSOP_POP_LENGTH; \
3292 op = (JSOp) *regs.pc; \
3296 #define END_SET_CASE(OP) \
3297 SKIP_POP_AFTER_SET(OP##_LENGTH, 1); \
3300 #define END_SET_CASE_STORE_RVAL(OP,spdec) \
3301 SKIP_POP_AFTER_SET(OP##_LENGTH, spdec); \
3303 Value *newsp = regs.sp - ((spdec) - 1); \
3304 newsp[-1] = regs.sp[-1]; \
3309 BEGIN_CASE(JSOP_SETCONST
)
3313 JSObject
&obj
= regs
.fp
->varobj(cx
);
3314 const Value
&ref
= regs
.sp
[-1];
3315 if (!obj
.defineProperty(cx
, ATOM_TO_JSID(atom
), ref
,
3316 PropertyStub
, PropertyStub
,
3317 JSPROP_ENUMERATE
| JSPROP_PERMANENT
| JSPROP_READONLY
)) {
3321 END_SET_CASE(JSOP_SETCONST
);
3323 #if JS_HAS_DESTRUCTURING
3324 BEGIN_CASE(JSOP_ENUMCONSTELEM
)
3326 const Value
&ref
= regs
.sp
[-3];
3328 FETCH_OBJECT(cx
, -2, obj
);
3330 FETCH_ELEMENT_ID(obj
, -1, id
);
3331 if (!obj
->defineProperty(cx
, id
, ref
,
3332 PropertyStub
, PropertyStub
,
3333 JSPROP_ENUMERATE
| JSPROP_PERMANENT
| JSPROP_READONLY
)) {
3338 END_CASE(JSOP_ENUMCONSTELEM
)
3341 BEGIN_CASE(JSOP_BINDGNAME
)
3342 PUSH_OBJECT(*regs
.fp
->scopeChain().getGlobal());
3343 END_CASE(JSOP_BINDGNAME
)
3345 BEGIN_CASE(JSOP_BINDNAME
)
3350 * We can skip the property lookup for the global object. If the
3351 * property does not exist anywhere on the scope chain, JSOP_SETNAME
3352 * adds the property to the global.
3354 * As a consequence of this optimization for the global object we run
3355 * its JSRESOLVE_ASSIGNING-tolerant resolve hooks only in JSOP_SETNAME,
3356 * after the interpreter evaluates the right- hand-side of the
3357 * assignment, and not here.
3359 * This should be transparent to the hooks because the script, instead
3360 * of name = rhs, could have used global.name = rhs given a global
3361 * object reference, which also calls the hooks only after evaluating
3362 * the rhs. We desire such resolve hook equivalence between the two
3365 obj
= ®s
.fp
->scopeChain();
3366 if (!obj
->getParent())
3369 PropertyCacheEntry
*entry
;
3372 JS_PROPERTY_CACHE(cx
).test(cx
, regs
.pc
, obj
, obj2
, entry
, atom
);
3374 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj
, obj2
, entry
);
3378 jsid id
= ATOM_TO_JSID(atom
);
3379 obj
= js_FindIdentifierBase(cx
, ®s
.fp
->scopeChain(), id
);
3385 END_CASE(JSOP_BINDNAME
)
3387 BEGIN_CASE(JSOP_IMACOP
)
3388 JS_ASSERT(JS_UPTRDIFF(regs
.fp
->imacropc(), script
->code
) < script
->length
);
3389 op
= JSOp(*regs
.fp
->imacropc());
3392 #define BITWISE_OP(OP) \
3395 if (!ValueToECMAInt32(cx, regs.sp[-2], &i)) \
3397 if (!ValueToECMAInt32(cx, regs.sp[-1], &j)) \
3401 regs.sp[-1].setInt32(i); \
3404 BEGIN_CASE(JSOP_BITOR
)
3406 END_CASE(JSOP_BITOR
)
3408 BEGIN_CASE(JSOP_BITXOR
)
3410 END_CASE(JSOP_BITXOR
)
3412 BEGIN_CASE(JSOP_BITAND
)
3414 END_CASE(JSOP_BITAND
)
3419 * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies
3420 * because they begin if/else chains, so callers must not put semicolons after
3421 * the call expressions!
3423 #if JS_HAS_XML_SUPPORT
3424 #define XML_EQUALITY_OP(OP) \
3425 if ((lval.isObject() && lval.toObject().isXML()) || \
3426 (rval.isObject() && rval.toObject().isXML())) { \
3427 if (!js_TestXMLEquality(cx, lval, rval, &cond)) \
3429 cond = cond OP JS_TRUE; \
3432 #define XML_EQUALITY_OP(OP) /* nothing */
3435 #define EQUALITY_OP(OP, IFNAN) \
3438 Value rval = regs.sp[-1]; \
3439 Value lval = regs.sp[-2]; \
3440 XML_EQUALITY_OP(OP) \
3441 if (SameType(lval, rval)) { \
3442 if (lval.isString()) { \
3443 JSString *l = lval.toString(), *r = rval.toString(); \
3445 if (!EqualStrings(cx, l, r, &equal)) \
3447 cond = equal OP JS_TRUE; \
3448 } else if (lval.isDouble()) { \
3449 double l = lval.toDouble(), r = rval.toDouble(); \
3450 cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN); \
3451 } else if (lval.isObject()) { \
3452 JSObject *l = &lval.toObject(), *r = &rval.toObject(); \
3453 l->assertSpecialEqualitySynced(); \
3454 if (EqualityOp eq = l->getClass()->ext.equality) { \
3455 if (!eq(cx, l, &rval, &cond)) \
3457 cond = cond OP JS_TRUE; \
3462 cond = lval.payloadAsRawUint32() OP rval.payloadAsRawUint32();\
3465 if (lval.isNullOrUndefined()) { \
3466 cond = rval.isNullOrUndefined() OP true; \
3467 } else if (rval.isNullOrUndefined()) { \
3468 cond = true OP false; \
3470 if (lval.isObject()) \
3471 DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval); \
3472 if (rval.isObject()) \
3473 DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); \
3474 if (lval.isString() && rval.isString()) { \
3475 JSString *l = lval.toString(), *r = rval.toString(); \
3477 if (!EqualStrings(cx, l, r, &equal)) \
3479 cond = equal OP JS_TRUE; \
3482 if (!ValueToNumber(cx, lval, &l) || \
3483 !ValueToNumber(cx, rval, &r)) { \
3486 cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN); \
3490 TRY_BRANCH_AFTER_COND(cond, 2); \
3492 regs.sp[-1].setBoolean(cond); \
3496 EQUALITY_OP(==, false);
3500 EQUALITY_OP(!=, true);
3504 #undef XML_EQUALITY_OP
3505 #undef EXTENDED_EQUALITY_OP
3507 #define STRICT_EQUALITY_OP(OP, COND) \
3509 const Value &rref = regs.sp[-1]; \
3510 const Value &lref = regs.sp[-2]; \
3512 if (!StrictlyEqual(cx, lref, rref, &equal)) \
3514 COND = equal OP JS_TRUE; \
3518 BEGIN_CASE(JSOP_STRICTEQ
)
3521 STRICT_EQUALITY_OP(==, cond
);
3522 regs
.sp
[-1].setBoolean(cond
);
3524 END_CASE(JSOP_STRICTEQ
)
3526 BEGIN_CASE(JSOP_STRICTNE
)
3529 STRICT_EQUALITY_OP(!=, cond
);
3530 regs
.sp
[-1].setBoolean(cond
);
3532 END_CASE(JSOP_STRICTNE
)
3534 BEGIN_CASE(JSOP_CASE
)
3537 STRICT_EQUALITY_OP(==, cond
);
3540 len
= GET_JUMP_OFFSET(regs
.pc
);
3546 BEGIN_CASE(JSOP_CASEX
)
3549 STRICT_EQUALITY_OP(==, cond
);
3552 len
= GET_JUMPX_OFFSET(regs
.pc
);
3556 END_CASE(JSOP_CASEX
)
3558 #undef STRICT_EQUALITY_OP
3560 #define RELATIONAL_OP(OP) \
3562 Value rval = regs.sp[-1]; \
3563 Value lval = regs.sp[-2]; \
3565 /* Optimize for two int-tagged operands (typical loop control). */ \
3566 if (lval.isInt32() && rval.isInt32()) { \
3567 cond = lval.toInt32() OP rval.toInt32(); \
3569 if (lval.isObject()) \
3570 DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \
3571 if (rval.isObject()) \
3572 DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \
3573 if (lval.isString() && rval.isString()) { \
3574 JSString *l = lval.toString(), *r = rval.toString(); \
3576 if (!CompareStrings(cx, l, r, &result)) \
3578 cond = result OP 0; \
3581 if (!ValueToNumber(cx, lval, &l) || \
3582 !ValueToNumber(cx, rval, &r)) { \
3585 cond = JSDOUBLE_COMPARE(l, OP, r, false); \
3588 TRY_BRANCH_AFTER_COND(cond, 2); \
3590 regs.sp[-1].setBoolean(cond); \
3609 #undef RELATIONAL_OP
3611 #define SIGNED_SHIFT_OP(OP) \
3614 if (!ValueToECMAInt32(cx, regs.sp[-2], &i)) \
3616 if (!ValueToECMAInt32(cx, regs.sp[-1], &j)) \
3618 i = i OP (j & 31); \
3620 regs.sp[-1].setInt32(i); \
3623 BEGIN_CASE(JSOP_LSH
)
3624 SIGNED_SHIFT_OP(<<);
3627 BEGIN_CASE(JSOP_RSH
)
3628 SIGNED_SHIFT_OP(>>);
3631 #undef SIGNED_SHIFT_OP
3633 BEGIN_CASE(JSOP_URSH
)
3636 if (!ValueToECMAUint32(cx
, regs
.sp
[-2], &u
))
3639 if (!ValueToECMAInt32(cx
, regs
.sp
[-1], &j
))
3645 regs
.sp
[-1].setNumber(uint32(u
));
3649 BEGIN_CASE(JSOP_ADD
)
3651 Value rval
= regs
.sp
[-1];
3652 Value lval
= regs
.sp
[-2];
3654 if (lval
.isInt32() && rval
.isInt32()) {
3655 int32_t l
= lval
.toInt32(), r
= rval
.toInt32();
3656 int32_t sum
= l
+ r
;
3658 if (JS_UNLIKELY(bool((l
^ sum
) & (r
^ sum
) & 0x80000000)))
3659 regs
.sp
[-1].setDouble(double(l
) + double(r
));
3661 regs
.sp
[-1].setInt32(sum
);
3663 #if JS_HAS_XML_SUPPORT
3664 if (IsXML(lval
) && IsXML(rval
)) {
3665 if (!js_ConcatenateXML(cx
, &lval
.toObject(), &rval
.toObject(), &rval
))
3672 if (lval
.isObject())
3673 DEFAULT_VALUE(cx
, -2, JSTYPE_VOID
, lval
);
3674 if (rval
.isObject())
3675 DEFAULT_VALUE(cx
, -1, JSTYPE_VOID
, rval
);
3676 bool lIsString
, rIsString
;
3677 if ((lIsString
= lval
.isString()) | (rIsString
= rval
.isString())) {
3678 JSString
*lstr
, *rstr
;
3680 lstr
= lval
.toString();
3682 lstr
= js_ValueToString(cx
, lval
);
3685 regs
.sp
[-2].setString(lstr
);
3688 rstr
= rval
.toString();
3690 rstr
= js_ValueToString(cx
, rval
);
3693 regs
.sp
[-1].setString(rstr
);
3695 JSString
*str
= js_ConcatStrings(cx
, lstr
, rstr
);
3699 regs
.sp
[-1].setString(str
);
3702 if (!ValueToNumber(cx
, lval
, &l
) || !ValueToNumber(cx
, rval
, &r
))
3706 regs
.sp
[-1].setNumber(l
);
3712 #define BINARY_OP(OP) \
3715 if (!ValueToNumber(cx, regs.sp[-2], &d1) || \
3716 !ValueToNumber(cx, regs.sp[-1], &d2)) { \
3719 double d = d1 OP d2; \
3721 regs.sp[-1].setNumber(d); \
3724 BEGIN_CASE(JSOP_SUB
)
3728 BEGIN_CASE(JSOP_MUL
)
3734 BEGIN_CASE(JSOP_DIV
)
3737 if (!ValueToNumber(cx
, regs
.sp
[-2], &d1
) ||
3738 !ValueToNumber(cx
, regs
.sp
[-1], &d2
)) {
3745 /* XXX MSVC miscompiles such that (NaN == 0) */
3746 if (JSDOUBLE_IS_NaN(d2
))
3750 if (d1
== 0 || JSDOUBLE_IS_NaN(d1
))
3752 else if (JSDOUBLE_IS_NEG(d1
) != JSDOUBLE_IS_NEG(d2
))
3753 vp
= &rt
->negativeInfinityValue
;
3755 vp
= &rt
->positiveInfinityValue
;
3759 regs
.sp
[-1].setNumber(d1
);
3764 BEGIN_CASE(JSOP_MOD
)
3766 Value
&lref
= regs
.sp
[-2];
3767 Value
&rref
= regs
.sp
[-1];
3769 if (lref
.isInt32() && rref
.isInt32() &&
3770 (l
= lref
.toInt32()) >= 0 && (r
= rref
.toInt32()) > 0) {
3771 int32_t mod
= l
% r
;
3773 regs
.sp
[-1].setInt32(mod
);
3776 if (!ValueToNumber(cx
, regs
.sp
[-2], &d1
) ||
3777 !ValueToNumber(cx
, regs
.sp
[-1], &d2
)) {
3782 regs
.sp
[-1].setDouble(js_NaN
);
3784 d1
= js_fmod(d1
, d2
);
3785 regs
.sp
[-1].setDouble(d1
);
3791 BEGIN_CASE(JSOP_NOT
)
3795 POP_BOOLEAN(cx
, _
, cond
);
3796 PUSH_BOOLEAN(!cond
);
3800 BEGIN_CASE(JSOP_BITNOT
)
3803 if (!ValueToECMAInt32(cx
, regs
.sp
[-1], &i
))
3806 regs
.sp
[-1].setInt32(i
);
3808 END_CASE(JSOP_BITNOT
)
3810 BEGIN_CASE(JSOP_NEG
)
3813 * When the operand is int jsval, INT32_FITS_IN_JSVAL(i) implies
3814 * INT32_FITS_IN_JSVAL(-i) unless i is 0 or INT32_MIN when the
3815 * results, -0.0 or INT32_MAX + 1, are jsdouble values.
3817 const Value
&ref
= regs
.sp
[-1];
3819 if (ref
.isInt32() && (i
= ref
.toInt32()) != 0 && i
!= INT32_MIN
) {
3821 regs
.sp
[-1].setInt32(i
);
3824 if (!ValueToNumber(cx
, regs
.sp
[-1], &d
))
3827 regs
.sp
[-1].setDouble(d
);
3832 BEGIN_CASE(JSOP_POS
)
3833 if (!ValueToNumber(cx
, ®s
.sp
[-1]))
3837 BEGIN_CASE(JSOP_DELNAME
)
3841 jsid id
= ATOM_TO_JSID(atom
);
3842 JSObject
*obj
, *obj2
;
3844 if (!js_FindProperty(cx
, id
, &obj
, &obj2
, &prop
))
3847 /* Strict mode code should never contain JSOP_DELNAME opcodes. */
3848 JS_ASSERT(!script
->strictModeCode
);
3850 /* ECMA says to return true if name is undefined or inherited. */
3853 if (!obj
->deleteProperty(cx
, id
, ®s
.sp
[-1], false))
3857 END_CASE(JSOP_DELNAME
)
3859 BEGIN_CASE(JSOP_DELPROP
)
3863 jsid id
= ATOM_TO_JSID(atom
);
3866 FETCH_OBJECT(cx
, -1, obj
);
3869 if (!obj
->deleteProperty(cx
, id
, &rval
, script
->strictModeCode
))
3874 END_CASE(JSOP_DELPROP
)
3876 BEGIN_CASE(JSOP_DELELEM
)
3878 /* Fetch the left part and resolve it to a non-null object. */
3880 FETCH_OBJECT(cx
, -2, obj
);
3882 /* Fetch index and convert it to id suitable for use with obj. */
3884 FETCH_ELEMENT_ID(obj
, -1, id
);
3886 /* Get or set the element. */
3887 if (!obj
->deleteProperty(cx
, id
, ®s
.sp
[-2], script
->strictModeCode
))
3892 END_CASE(JSOP_DELELEM
)
3894 BEGIN_CASE(JSOP_TYPEOFEXPR
)
3895 BEGIN_CASE(JSOP_TYPEOF
)
3897 const Value
&ref
= regs
.sp
[-1];
3898 JSType type
= JS_TypeOfValue(cx
, Jsvalify(ref
));
3899 JSAtom
*atom
= rt
->atomState
.typeAtoms
[type
];
3900 regs
.sp
[-1].setString(ATOM_TO_STRING(atom
));
3902 END_CASE(JSOP_TYPEOF
)
3904 BEGIN_CASE(JSOP_VOID
)
3905 regs
.sp
[-1].setUndefined();
3914 BEGIN_CASE(JSOP_INCELEM
)
3915 BEGIN_CASE(JSOP_DECELEM
)
3916 BEGIN_CASE(JSOP_ELEMINC
)
3917 BEGIN_CASE(JSOP_ELEMDEC
)
3920 * Delay fetching of id until we have the object to ensure the proper
3921 * evaluation order. See bug 372331.
3925 goto fetch_incop_obj
;
3927 BEGIN_CASE(JSOP_INCPROP
)
3928 BEGIN_CASE(JSOP_DECPROP
)
3929 BEGIN_CASE(JSOP_PROPINC
)
3930 BEGIN_CASE(JSOP_PROPDEC
)
3932 id
= ATOM_TO_JSID(atom
);
3936 FETCH_OBJECT(cx
, i
, obj
);
3937 if (JSID_IS_VOID(id
))
3938 FETCH_ELEMENT_ID(obj
, -1, id
);
3941 BEGIN_CASE(JSOP_INCNAME
)
3942 BEGIN_CASE(JSOP_DECNAME
)
3943 BEGIN_CASE(JSOP_NAMEINC
)
3944 BEGIN_CASE(JSOP_NAMEDEC
)
3945 BEGIN_CASE(JSOP_INCGNAME
)
3946 BEGIN_CASE(JSOP_DECGNAME
)
3947 BEGIN_CASE(JSOP_GNAMEINC
)
3948 BEGIN_CASE(JSOP_GNAMEDEC
)
3950 obj
= ®s
.fp
->scopeChain();
3951 if (js_CodeSpec
[op
].format
& JOF_GNAME
)
3952 obj
= obj
->getGlobal();
3955 PropertyCacheEntry
*entry
;
3956 JS_PROPERTY_CACHE(cx
).test(cx
, regs
.pc
, obj
, obj2
, entry
, atom
);
3958 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj
, obj2
, entry
);
3959 if (obj
== obj2
&& entry
->vword
.isSlot()) {
3960 uint32 slot
= entry
->vword
.toSlot();
3961 Value
&rref
= obj
->nativeGetSlotRef(slot
);
3963 if (JS_LIKELY(rref
.isInt32() && CanIncDecWithoutOverflow(tmp
= rref
.toInt32()))) {
3964 int32_t inc
= tmp
+ ((js_CodeSpec
[op
].format
& JOF_INC
) ? 1 : -1);
3965 if (!(js_CodeSpec
[op
].format
& JOF_POST
))
3967 rref
.getInt32Ref() = inc
;
3969 len
= JSOP_INCNAME_LENGTH
;
3976 id
= ATOM_TO_JSID(atom
);
3978 if (!js_FindPropertyHelper(cx
, id
, true, &obj
, &obj2
, &prop
))
3981 atomNotDefined
= atom
;
3982 goto atom_not_defined
;
3989 * We need a root to store the value to leave on the stack until
3990 * we have done with obj->setProperty.
3993 if (!obj
->getProperty(cx
, id
, ®s
.sp
[-1]))
3996 const JSCodeSpec
*cs
= &js_CodeSpec
[op
];
3997 JS_ASSERT(cs
->ndefs
== 1);
3998 JS_ASSERT((cs
->format
& JOF_TMPSLOT_MASK
) >= JOF_TMPSLOT2
);
3999 Value
&ref
= regs
.sp
[-1];
4001 if (JS_LIKELY(ref
.isInt32() && CanIncDecWithoutOverflow(tmp
= ref
.toInt32()))) {
4002 int incr
= (cs
->format
& JOF_INC
) ? 1 : -1;
4003 if (cs
->format
& JOF_POST
)
4004 ref
.getInt32Ref() = tmp
+ incr
;
4006 ref
.getInt32Ref() = tmp
+= incr
;
4007 regs
.fp
->setAssigning();
4008 JSBool ok
= obj
->setProperty(cx
, id
, &ref
, script
->strictModeCode
);
4009 regs
.fp
->clearAssigning();
4014 * We must set regs.sp[-1] to tmp for both post and pre increments
4015 * as the setter overwrites regs.sp[-1].
4019 /* We need an extra root for the result. */
4021 if (!js_DoIncDec(cx
, cs
, ®s
.sp
[-2], ®s
.sp
[-1]))
4023 regs
.fp
->setAssigning();
4024 JSBool ok
= obj
->setProperty(cx
, id
, ®s
.sp
[-1], script
->strictModeCode
);
4025 regs
.fp
->clearAssigning();
4031 if (cs
->nuses
== 0) {
4032 /* regs.sp[-1] already contains the result of name increment. */
4034 regs
.sp
[-1 - cs
->nuses
] = regs
.sp
[-1];
4035 regs
.sp
-= cs
->nuses
;
4047 /* Position cases so the most frequent i++ does not need a jump. */
4048 BEGIN_CASE(JSOP_DECARG
)
4049 incr
= -1; incr2
= -1; goto do_arg_incop
;
4050 BEGIN_CASE(JSOP_ARGDEC
)
4051 incr
= -1; incr2
= 0; goto do_arg_incop
;
4052 BEGIN_CASE(JSOP_INCARG
)
4053 incr
= 1; incr2
= 1; goto do_arg_incop
;
4054 BEGIN_CASE(JSOP_ARGINC
)
4055 incr
= 1; incr2
= 0;
4058 slot
= GET_ARGNO(regs
.pc
);
4059 JS_ASSERT(slot
< regs
.fp
->numFormalArgs());
4060 METER_SLOT_OP(op
, slot
);
4062 goto do_int_fast_incop
;
4064 BEGIN_CASE(JSOP_DECLOCAL
)
4065 incr
= -1; incr2
= -1; goto do_local_incop
;
4066 BEGIN_CASE(JSOP_LOCALDEC
)
4067 incr
= -1; incr2
= 0; goto do_local_incop
;
4068 BEGIN_CASE(JSOP_INCLOCAL
)
4069 incr
= 1; incr2
= 1; goto do_local_incop
;
4070 BEGIN_CASE(JSOP_LOCALINC
)
4071 incr
= 1; incr2
= 0;
4074 * do_local_incop comes right before do_int_fast_incop as we want to
4075 * avoid an extra jump for variable cases as local++ is more frequent
4079 slot
= GET_SLOTNO(regs
.pc
);
4080 JS_ASSERT(slot
< regs
.fp
->numSlots());
4081 METER_SLOT_OP(op
, slot
);
4082 vp
= regs
.fp
->slots() + slot
;
4086 if (JS_LIKELY(vp
->isInt32() && CanIncDecWithoutOverflow(tmp
= vp
->toInt32()))) {
4087 vp
->getInt32Ref() = tmp
+ incr
;
4088 JS_ASSERT(JSOP_INCARG_LENGTH
== js_CodeSpec
[op
].length
);
4089 SKIP_POP_AFTER_SET(JSOP_INCARG_LENGTH
, 0);
4090 PUSH_INT32(tmp
+ incr2
);
4093 if (!js_DoIncDec(cx
, &js_CodeSpec
[op
], ®s
.sp
[-1], vp
))
4096 len
= JSOP_INCARG_LENGTH
;
4097 JS_ASSERT(len
== js_CodeSpec
[op
].length
);
4101 BEGIN_CASE(JSOP_THIS
)
4102 if (!regs
.fp
->computeThis(cx
))
4104 PUSH_COPY(regs
.fp
->thisValue());
4107 BEGIN_CASE(JSOP_UNBRANDTHIS
)
4109 if (!regs
.fp
->computeThis(cx
))
4111 Value
&thisv
= regs
.fp
->thisValue();
4112 if (thisv
.isObject()) {
4113 JSObject
*obj
= &thisv
.toObject();
4114 if (obj
->isNative())
4118 END_CASE(JSOP_UNBRANDTHIS
)
4125 BEGIN_CASE(JSOP_GETTHISPROP
)
4126 if (!regs
.fp
->computeThis(cx
))
4129 PUSH_COPY(regs
.fp
->thisValue());
4130 goto do_getprop_body
;
4132 BEGIN_CASE(JSOP_GETARGPROP
)
4135 uint32 slot
= GET_ARGNO(regs
.pc
);
4136 JS_ASSERT(slot
< regs
.fp
->numFormalArgs());
4137 PUSH_COPY(argv
[slot
]);
4138 goto do_getprop_body
;
4141 BEGIN_CASE(JSOP_GETLOCALPROP
)
4144 uint32 slot
= GET_SLOTNO(regs
.pc
);
4145 JS_ASSERT(slot
< script
->nslots
);
4146 PUSH_COPY(regs
.fp
->slots()[slot
]);
4147 goto do_getprop_body
;
4150 BEGIN_CASE(JSOP_GETPROP
)
4151 BEGIN_CASE(JSOP_GETXPROP
)
4157 do_getprop_with_lval
:
4158 VALUE_TO_OBJECT(cx
, vp
, obj
);
4164 * We do not impose the method read barrier if in an imacro,
4165 * assuming any property gets it does (e.g., for 'toString'
4166 * from JSOP_NEW) will not be leaked to the calling script.
4168 JSObject
*aobj
= js_GetProtoIfDenseArray(obj
);
4170 PropertyCacheEntry
*entry
;
4173 JS_PROPERTY_CACHE(cx
).test(cx
, regs
.pc
, aobj
, obj2
, entry
, atom
);
4175 ASSERT_VALID_PROPERTY_CACHE_HIT(i
, aobj
, obj2
, entry
);
4176 if (entry
->vword
.isFunObj()) {
4177 rval
.setObject(entry
->vword
.toFunObj());
4178 } else if (entry
->vword
.isSlot()) {
4179 uint32 slot
= entry
->vword
.toSlot();
4180 rval
= obj2
->nativeGetSlot(slot
);
4182 JS_ASSERT(entry
->vword
.isShape());
4183 const Shape
*shape
= entry
->vword
.toShape();
4184 NATIVE_GET(cx
, obj
, obj2
, shape
,
4185 regs
.fp
->hasImacropc() ? JSGET_NO_METHOD_BARRIER
: JSGET_METHOD_BARRIER
,
4191 jsid id
= ATOM_TO_JSID(atom
);
4192 if (JS_LIKELY(!aobj
->getOps()->getProperty
)
4193 ? !js_GetPropertyHelper(cx
, obj
, id
,
4194 (regs
.fp
->hasImacropc() ||
4195 regs
.pc
[JSOP_GETPROP_LENGTH
+ i
] == JSOP_IFEQ
)
4196 ? JSGET_CACHE_RESULT
| JSGET_NO_METHOD_BARRIER
4197 : JSGET_CACHE_RESULT
| JSGET_METHOD_BARRIER
,
4199 : !obj
->getProperty(cx
, id
, &rval
)) {
4205 assertSameCompartment(cx
, regs
.sp
[-1]);
4206 JS_ASSERT(JSOP_GETPROP_LENGTH
+ i
== js_CodeSpec
[op
].length
);
4207 len
= JSOP_GETPROP_LENGTH
+ i
;
4211 BEGIN_CASE(JSOP_LENGTH
)
4213 if (vp
->isString()) {
4214 vp
->setInt32(vp
->toString()->length());
4215 } else if (vp
->isObject()) {
4216 JSObject
*obj
= &vp
->toObject();
4217 if (obj
->isArray()) {
4218 jsuint length
= obj
->getArrayLength();
4219 regs
.sp
[-1].setNumber(length
);
4220 } else if (obj
->isArguments() && !obj
->isArgsLengthOverridden()) {
4221 uint32 length
= obj
->getArgsInitialLength();
4222 JS_ASSERT(length
< INT32_MAX
);
4223 regs
.sp
[-1].setInt32(int32_t(length
));
4226 goto do_getprop_with_lval
;
4230 goto do_getprop_with_lval
;
4232 END_CASE(JSOP_LENGTH
)
4236 BEGIN_CASE(JSOP_CALLPROP
)
4238 Value lval
= regs
.sp
[-1];
4241 if (lval
.isObject()) {
4244 JSProtoKey protoKey
;
4245 if (lval
.isString()) {
4246 protoKey
= JSProto_String
;
4247 } else if (lval
.isNumber()) {
4248 protoKey
= JSProto_Number
;
4249 } else if (lval
.isBoolean()) {
4250 protoKey
= JSProto_Boolean
;
4252 JS_ASSERT(lval
.isNull() || lval
.isUndefined());
4253 js_ReportIsNullOrUndefined(cx
, -1, lval
, NULL
);
4257 if (!js_GetClassPrototype(cx
, NULL
, protoKey
, &pobj
))
4259 objv
.setObject(*pobj
);
4262 JSObject
*aobj
= js_GetProtoIfDenseArray(&objv
.toObject());
4265 PropertyCacheEntry
*entry
;
4268 JS_PROPERTY_CACHE(cx
).test(cx
, regs
.pc
, aobj
, obj2
, entry
, atom
);
4270 ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj
, obj2
, entry
);
4271 if (entry
->vword
.isFunObj()) {
4272 rval
.setObject(entry
->vword
.toFunObj());
4273 } else if (entry
->vword
.isSlot()) {
4274 uint32 slot
= entry
->vword
.toSlot();
4275 rval
= obj2
->nativeGetSlot(slot
);
4277 JS_ASSERT(entry
->vword
.isShape());
4278 const Shape
*shape
= entry
->vword
.toShape();
4279 NATIVE_GET(cx
, &objv
.toObject(), obj2
, shape
, JSGET_NO_METHOD_BARRIER
, &rval
);
4282 assertSameCompartment(cx
, regs
.sp
[-1]);
4286 * Cache miss: use the immediate atom that was loaded for us under
4287 * PropertyCache::test.
4290 id
= ATOM_TO_JSID(atom
);
4293 if (lval
.isObject()) {
4294 if (!js_GetMethod(cx
, &objv
.toObject(), id
,
4295 JS_LIKELY(!aobj
->getOps()->getProperty
)
4296 ? JSGET_CACHE_RESULT
| JSGET_NO_METHOD_BARRIER
4297 : JSGET_NO_METHOD_BARRIER
,
4303 assertSameCompartment(cx
, regs
.sp
[-1], regs
.sp
[-2]);
4305 JS_ASSERT(!objv
.toObject().getOps()->getProperty
);
4306 if (!js_GetPropertyHelper(cx
, &objv
.toObject(), id
,
4307 JSGET_CACHE_RESULT
| JSGET_NO_METHOD_BARRIER
,
4313 assertSameCompartment(cx
, regs
.sp
[-1], regs
.sp
[-2]);
4316 #if JS_HAS_NO_SUCH_METHOD
4317 if (JS_UNLIKELY(rval
.isUndefined()) && regs
.sp
[-1].isObject()) {
4319 regs
.sp
[-2].setString(ATOM_TO_STRING(atom
));
4320 if (!js_OnUnknownMethod(cx
, regs
.sp
- 2))
4325 END_CASE(JSOP_CALLPROP
)
4327 BEGIN_CASE(JSOP_UNBRAND
)
4328 JS_ASSERT(regs
.sp
- regs
.fp
->slots() >= 1);
4329 regs
.sp
[-1].toObject().unbrand(cx
);
4330 END_CASE(JSOP_UNBRAND
)
4332 BEGIN_CASE(JSOP_SETGNAME
)
4333 BEGIN_CASE(JSOP_SETNAME
)
4334 BEGIN_CASE(JSOP_SETPROP
)
4335 BEGIN_CASE(JSOP_SETMETHOD
)
4337 Value rval
= regs
.sp
[-1];
4338 JS_ASSERT_IF(op
== JSOP_SETMETHOD
, IsFunctionObject(rval
));
4339 Value
&lref
= regs
.sp
[-2];
4340 JS_ASSERT_IF(op
== JSOP_SETNAME
, lref
.isObject());
4342 VALUE_TO_OBJECT(cx
, &lref
, obj
);
4344 JS_ASSERT_IF(op
== JSOP_SETGNAME
, obj
== regs
.fp
->scopeChain().getGlobal());
4347 PropertyCache
*cache
= &JS_PROPERTY_CACHE(cx
);
4350 * Probe the property cache, specializing for two important
4351 * set-property cases. First:
4353 * function f(a, b, c) {
4354 * var o = {p:a, q:b, r:c};
4358 * or similar real-world cases, which evolve a newborn native
4359 * object predicatably through some bounded number of property
4360 * additions. And second:
4364 * in a frequently executed method or loop body, where p will
4365 * (possibly after the first iteration) always exist in native
4368 PropertyCacheEntry
*entry
;
4371 if (cache
->testForSet(cx
, regs
.pc
, obj
, &entry
, &obj2
, &atom
)) {
4373 * Property cache hit, only partially confirmed by testForSet. We
4374 * know that the entry applies to regs.pc and that obj's shape
4377 * The entry predicts either a new property to be added directly to
4378 * obj by this set, or on an existing "own" property, or on a
4379 * prototype property that has a setter.
4381 const Shape
*shape
= entry
->vword
.toShape();
4382 JS_ASSERT_IF(shape
->isDataDescriptor(), shape
->writable());
4383 JS_ASSERT_IF(shape
->hasSlot(), entry
->vcapTag() == 0);
4386 * Fastest path: check whether obj already has the cached shape and
4387 * call NATIVE_SET and break to get out of the do-while(0). But we
4388 * can call NATIVE_SET only for a direct or proto-setter hit.
4390 if (!entry
->adding()) {
4391 if (entry
->vcapTag() == 0 ||
4392 ((obj2
= obj
->getProto()) && obj2
->shape() == entry
->vshape()))
4395 if (entry
->directHit()) {
4396 JS_ASSERT(obj
->nativeContains(*shape
));
4398 JS_ASSERT(obj2
->nativeContains(*shape
));
4399 JS_ASSERT(entry
->vcapTag() == 1);
4400 JS_ASSERT(entry
->kshape
!= entry
->vshape());
4401 JS_ASSERT(!shape
->hasSlot());
4405 PCMETER(cache
->pchits
++);
4406 PCMETER(cache
->setpchits
++);
4407 NATIVE_SET(cx
, obj
, shape
, entry
, &rval
);
4411 JS_ASSERT(obj
->isExtensible());
4413 if (obj
->nativeEmpty()) {
4414 if (!obj
->ensureClassReservedSlotsForEmptyObject(cx
))
4419 if (shape
->previous() == obj
->lastProperty() &&
4420 entry
->vshape() == rt
->protoHazardShape
&&
4421 shape
->hasDefaultSetter()) {
4423 JS_ASSERT(slot
== obj
->slotSpan());
4426 * Fast path: adding a plain old property that was once at
4427 * the frontier of the property tree, whose slot is next to
4428 * claim among the already-allocated slots in obj, where
4429 * shape->table has not been created yet.
4431 PCMETER(cache
->pchits
++);
4432 PCMETER(cache
->addpchits
++);
4434 if (slot
< obj
->numSlots()) {
4435 JS_ASSERT(obj
->getSlot(slot
).isUndefined());
4437 if (!obj
->allocSlot(cx
, &slot
))
4439 JS_ASSERT(slot
== shape
->slot
);
4442 /* Simply extend obj's property tree path with shape! */
4443 obj
->extend(cx
, shape
);
4446 * No method change check here because here we are adding a
4447 * new property, not updating an existing slot's value that
4448 * might contain a method of a branded shape.
4450 TRACE_1(AddProperty
, obj
);
4451 obj
->nativeSetSlot(slot
, rval
);
4454 * Purge the property cache of the id we may have just
4455 * shadowed in obj's scope and proto chains.
4457 js_PurgeScopeChain(cx
, obj
, shape
->id
);
4461 PCMETER(cache
->setpcmisses
++);
4468 jsid id
= ATOM_TO_JSID(atom
);
4469 if (entry
&& JS_LIKELY(!obj
->getOps()->setProperty
)) {
4471 if (op
== JSOP_SETMETHOD
)
4472 defineHow
= JSDNP_CACHE_RESULT
| JSDNP_SET_METHOD
;
4473 else if (op
== JSOP_SETNAME
)
4474 defineHow
= JSDNP_CACHE_RESULT
| JSDNP_UNQUALIFIED
;
4476 defineHow
= JSDNP_CACHE_RESULT
;
4477 if (!js_SetPropertyHelper(cx
, obj
, id
, defineHow
, &rval
, script
->strictModeCode
))
4480 if (!obj
->setProperty(cx
, id
, &rval
, script
->strictModeCode
))
4482 ABORT_RECORDING(cx
, "Non-native set");
4486 END_SET_CASE_STORE_RVAL(JSOP_SETPROP
, 2);
4488 BEGIN_CASE(JSOP_GETELEM
)
4490 Value
&lref
= regs
.sp
[-2];
4491 Value
&rref
= regs
.sp
[-1];
4492 if (lref
.isString() && rref
.isInt32()) {
4493 JSString
*str
= lref
.toString();
4494 int32_t i
= rref
.toInt32();
4495 if (size_t(i
) < str
->length()) {
4496 str
= JSString::getUnitString(cx
, str
, size_t(i
));
4500 regs
.sp
[-1].setString(str
);
4501 len
= JSOP_GETELEM_LENGTH
;
4507 VALUE_TO_OBJECT(cx
, &lref
, obj
);
4509 const Value
*copyFrom
;
4512 if (rref
.isInt32()) {
4513 int32_t i
= rref
.toInt32();
4514 if (obj
->isDenseArray()) {
4515 jsuint idx
= jsuint(i
);
4517 if (idx
< obj
->getDenseArrayCapacity()) {
4518 copyFrom
= obj
->addressOfDenseArrayElement(idx
);
4519 if (!copyFrom
->isMagic())
4522 } else if (obj
->isArguments()) {
4523 uint32 arg
= uint32(i
);
4525 if (arg
< obj
->getArgsInitialLength()) {
4526 copyFrom
= obj
->addressOfArgsElement(arg
);
4527 if (!copyFrom
->isMagic(JS_ARGS_HOLE
)) {
4528 if (JSStackFrame
*afp
= (JSStackFrame
*) obj
->getPrivate())
4529 copyFrom
= &afp
->canonicalActualArg(arg
);
4534 if (JS_LIKELY(INT_FITS_IN_JSID(i
)))
4535 id
= INT_TO_JSID(i
);
4537 goto intern_big_int
;
4540 if (ValueFitsInInt32(rref
, &i
) && INT_FITS_IN_JSID(i
)) {
4541 id
= INT_TO_JSID(i
);
4544 if (!js_InternNonIntElementId(cx
, obj
, rref
, &id
))
4549 if (!obj
->getProperty(cx
, id
, &rval
))
4555 regs
.sp
[-1] = *copyFrom
;
4556 assertSameCompartment(cx
, regs
.sp
[-1]);
4558 END_CASE(JSOP_GETELEM
)
4560 BEGIN_CASE(JSOP_CALLELEM
)
4562 /* Find the object on which to look for |this|'s properties. */
4563 Value thisv
= regs
.sp
[-2];
4564 JSObject
*thisObj
= ValuePropertyBearer(cx
, thisv
, -2);
4568 /* Fetch index and convert it to id suitable for use with obj. */
4570 FETCH_ELEMENT_ID(thisObj
, -1, id
);
4572 /* Get the method. */
4573 if (!js_GetMethod(cx
, thisObj
, id
, JSGET_NO_METHOD_BARRIER
, ®s
.sp
[-2]))
4576 #if JS_HAS_NO_SUCH_METHOD
4577 if (JS_UNLIKELY(regs
.sp
[-2].isUndefined()) && thisv
.isObject()) {
4578 /* For js_OnUnknownMethod, sp[-2] is the index, and sp[-1] is the object missing it. */
4579 regs
.sp
[-2] = regs
.sp
[-1];
4580 regs
.sp
[-1].setObject(*thisObj
);
4581 if (!js_OnUnknownMethod(cx
, regs
.sp
- 2))
4586 regs
.sp
[-1] = thisv
;
4589 END_CASE(JSOP_CALLELEM
)
4591 BEGIN_CASE(JSOP_SETELEM
)
4594 FETCH_OBJECT(cx
, -3, obj
);
4596 FETCH_ELEMENT_ID(obj
, -2, id
);
4599 if (obj
->isDenseArray() && JSID_IS_INT(id
)) {
4600 jsuint length
= obj
->getDenseArrayCapacity();
4601 jsint i
= JSID_TO_INT(id
);
4602 if ((jsuint
)i
< length
) {
4603 if (obj
->getDenseArrayElement(i
).isMagic(JS_ARRAY_HOLE
)) {
4604 if (js_PrototypeHasIndexedProperties(cx
, obj
))
4606 if ((jsuint
)i
>= obj
->getArrayLength())
4607 obj
->setArrayLength(i
+ 1);
4609 obj
->setDenseArrayElement(i
, regs
.sp
[-1]);
4615 if (!obj
->setProperty(cx
, id
, &rval
, script
->strictModeCode
))
4619 END_SET_CASE_STORE_RVAL(JSOP_SETELEM
, 3)
4621 BEGIN_CASE(JSOP_ENUMELEM
)
4623 /* Funky: the value to set is under the [obj, id] pair. */
4625 FETCH_OBJECT(cx
, -2, obj
);
4627 FETCH_ELEMENT_ID(obj
, -1, id
);
4628 Value rval
= regs
.sp
[-3];
4629 if (!obj
->setProperty(cx
, id
, &rval
, script
->strictModeCode
))
4633 END_CASE(JSOP_ENUMELEM
)
4635 { // begin block around calling opcodes
4642 BEGIN_CASE(JSOP_NEW
)
4644 /* Get immediate argc and find the constructor function. */
4645 argc
= GET_ARGC(regs
.pc
);
4646 vp
= regs
.sp
- (2 + argc
);
4647 JS_ASSERT(vp
>= regs
.fp
->base());
4650 * Assign lval, callee, and newfun exactly as the code at inline_call: expects to
4651 * find them, to avoid nesting a js_Interpret call via js_InvokeConstructor.
4653 if (IsFunctionObject(vp
[0], &callee
)) {
4654 newfun
= callee
->getFunctionPrivate();
4655 if (newfun
->isInterpreted()) {
4656 if (newfun
->u
.i
.script
->isEmpty()) {
4657 JSObject
*obj2
= js_CreateThisForFunction(cx
, callee
);
4660 vp
[0].setObject(*obj2
);
4665 flags
= JSFRAME_CONSTRUCTING
;
4670 if (!InvokeConstructor(cx
, InvokeArgsAlreadyOnTheStack(vp
, argc
)))
4673 CHECK_INTERRUPT_HANDLER();
4674 TRACE_0(NativeCallComplete
);
4680 BEGIN_CASE(JSOP_EVAL
)
4682 argc
= GET_ARGC(regs
.pc
);
4683 vp
= regs
.sp
- (argc
+ 2);
4685 if (!IsFunctionObject(*vp
, &callee
))
4686 goto call_using_invoke
;
4688 newfun
= callee
->getFunctionPrivate();
4689 if (!IsBuiltinEvalFunction(newfun
))
4690 goto call_using_invoke
;
4692 if (!DirectEval(cx
, newfun
, argc
, vp
))
4697 BEGIN_CASE(JSOP_CALL
)
4698 BEGIN_CASE(JSOP_FUNAPPLY
)
4699 BEGIN_CASE(JSOP_FUNCALL
)
4701 argc
= GET_ARGC(regs
.pc
);
4702 vp
= regs
.sp
- (argc
+ 2);
4704 if (IsFunctionObject(*vp
, &callee
)) {
4705 newfun
= callee
->getFunctionPrivate();
4707 /* Clear frame flags since this is not a constructor call. */
4709 if (newfun
->isInterpreted())
4712 JSScript
*newscript
= newfun
->script();
4713 if (JS_UNLIKELY(newscript
->isEmpty())) {
4719 /* Restrict recursion of lightweight functions. */
4720 if (JS_UNLIKELY(inlineCallCount
>= JS_MAX_INLINE_CALL_COUNT
)) {
4721 js_ReportOverRecursed(cx
);
4725 /* Get pointer to new frame/slots, prepare arguments. */
4726 StackSpace
&stack
= cx
->stack();
4727 JSStackFrame
*newfp
= stack
.getInlineFrame(cx
, regs
.sp
, argc
, newfun
,
4729 if (JS_UNLIKELY(!newfp
))
4731 JS_ASSERT_IF(!vp
[1].isPrimitive(), IsSaneThisObject(vp
[1].toObject()));
4733 /* Initialize frame, locals. */
4734 newfp
->initCallFrame(cx
, *callee
, newfun
, argc
, flags
);
4735 SetValueRangeToUndefined(newfp
->slots(), newscript
->nfixed
);
4737 /* Officially push the frame. */
4738 stack
.pushInlineFrame(cx
, newscript
, newfp
, ®s
);
4740 /* Refresh interpreter locals. */
4741 JS_ASSERT(newfp
== regs
.fp
);
4743 argv
= regs
.fp
->formalArgsEnd() - newfun
->nargs
;
4744 atoms
= script
->atomMap
.vector
;
4746 /* Now that the new frame is rooted, maybe create a call object. */
4747 if (newfun
->isHeavyweight() && !js_GetCallObject(cx
, regs
.fp
))
4751 JS_RUNTIME_METER(rt
, inlineCalls
);
4753 TRACE_0(EnterFrame
);
4755 CHECK_INTERRUPT_HANDLER();
4758 /* Try to ensure methods are method JIT'd. */
4759 mjit::CompileStatus status
= mjit::CanMethodJIT(cx
, script
, regs
.fp
);
4760 if (status
== mjit::Compile_Error
)
4762 if (!TRACE_RECORDER(cx
) && !TRACE_PROFILER(cx
) && status
== mjit::Compile_Okay
) {
4763 interpReturnOK
= mjit::JaegerShot(cx
);
4764 CHECK_INTERRUPT_HANDLER();
4769 if (!ScriptPrologue(cx
, regs
.fp
))
4772 CHECK_INTERRUPT_HANDLER();
4774 /* Load first op and dispatch it (safe since JSOP_STOP). */
4775 op
= (JSOp
) *regs
.pc
;
4779 Probes::enterJSFun(cx
, newfun
, script
);
4780 JSBool ok
= CallJSNative(cx
, newfun
->u
.n
.native
, argc
, vp
);
4781 Probes::exitJSFun(cx
, newfun
, script
);
4785 TRACE_0(NativeCallComplete
);
4791 ok
= Invoke(cx
, InvokeArgsAlreadyOnTheStack(vp
, argc
), 0);
4793 CHECK_INTERRUPT_HANDLER();
4796 JS_RUNTIME_METER(rt
, nonInlineCalls
);
4797 TRACE_0(NativeCallComplete
);
4803 } // end block around calling opcodes
4805 BEGIN_CASE(JSOP_SETCALL
)
4807 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_LEFTSIDE_OF_ASS
);
4810 END_CASE(JSOP_SETCALL
)
4812 #define SLOW_PUSH_THISV(cx, obj) \
4815 JSObject *thisp = obj; \
4816 if (!thisp->getParent() || \
4817 (clasp = thisp->getClass()) == &js_CallClass || \
4818 clasp == &js_BlockClass || \
4819 clasp == &js_DeclEnvClass) { \
4820 /* Push the ImplicitThisValue for the Environment Record */ \
4821 /* associated with obj. See ES5 sections 10.2.1.1.6 and */ \
4822 /* 10.2.1.2.6 (ImplicitThisValue) and section 11.2.3 */ \
4823 /* (Function Calls). */ \
4826 thisp = thisp->thisObject(cx); \
4829 PUSH_OBJECT(*thisp); \
4833 BEGIN_CASE(JSOP_GETGNAME
)
4834 BEGIN_CASE(JSOP_CALLGNAME
)
4835 BEGIN_CASE(JSOP_NAME
)
4836 BEGIN_CASE(JSOP_CALLNAME
)
4838 JSObject
*obj
= ®s
.fp
->scopeChain();
4843 PropertyCacheEntry
*entry
;
4846 JS_PROPERTY_CACHE(cx
).test(cx
, regs
.pc
, obj
, obj2
, entry
, atom
);
4848 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj
, obj2
, entry
);
4849 if (entry
->vword
.isFunObj()) {
4850 PUSH_OBJECT(entry
->vword
.toFunObj());
4851 } else if (entry
->vword
.isSlot()) {
4852 uintN slot
= entry
->vword
.toSlot();
4853 PUSH_COPY(obj2
->nativeGetSlot(slot
));
4855 JS_ASSERT(entry
->vword
.isShape());
4856 shape
= entry
->vword
.toShape();
4857 NATIVE_GET(cx
, obj
, obj2
, shape
, JSGET_METHOD_BARRIER
, &rval
);
4862 * Push results, the same as below, but with a prop$ hit there
4863 * is no need to test for the unusual and uncacheable case where
4864 * the caller determines |this|.
4868 JS_ASSERT(!obj
->getParent() ||
4869 (clasp
= obj
->getClass()) == &js_CallClass
||
4870 clasp
== &js_BlockClass
||
4871 clasp
== &js_DeclEnvClass
);
4873 if (op
== JSOP_CALLNAME
|| op
== JSOP_CALLGNAME
)
4875 len
= JSOP_NAME_LENGTH
;
4880 id
= ATOM_TO_JSID(atom
);
4882 if (!js_FindPropertyHelper(cx
, id
, true, &obj
, &obj2
, &prop
))
4885 /* Kludge to allow (typeof foo == "undefined") tests. */
4886 JSOp op2
= js_GetOpcode(cx
, script
, regs
.pc
+ JSOP_NAME_LENGTH
);
4887 if (op2
== JSOP_TYPEOF
) {
4889 len
= JSOP_NAME_LENGTH
;
4892 atomNotDefined
= atom
;
4893 goto atom_not_defined
;
4896 /* Take the slow path if prop was not found in a native object. */
4897 if (!obj
->isNative() || !obj2
->isNative()) {
4898 if (!obj
->getProperty(cx
, id
, &rval
))
4901 shape
= (Shape
*)prop
;
4902 JSObject
*normalized
= obj
;
4903 if (normalized
->getClass() == &js_WithClass
&& !shape
->hasDefaultGetter())
4904 normalized
= js_UnwrapWithObject(cx
, normalized
);
4905 NATIVE_GET(cx
, normalized
, obj2
, shape
, JSGET_METHOD_BARRIER
, &rval
);
4910 /* obj must be on the scope chain, thus not a function. */
4911 if (op
== JSOP_CALLNAME
|| op
== JSOP_CALLGNAME
)
4912 SLOW_PUSH_THISV(cx
, obj
);
4916 BEGIN_CASE(JSOP_UINT16
)
4917 PUSH_INT32((int32_t) GET_UINT16(regs
.pc
));
4918 END_CASE(JSOP_UINT16
)
4920 BEGIN_CASE(JSOP_UINT24
)
4921 PUSH_INT32((int32_t) GET_UINT24(regs
.pc
));
4922 END_CASE(JSOP_UINT24
)
4924 BEGIN_CASE(JSOP_INT8
)
4925 PUSH_INT32(GET_INT8(regs
.pc
));
4928 BEGIN_CASE(JSOP_INT32
)
4929 PUSH_INT32(GET_INT32(regs
.pc
));
4930 END_CASE(JSOP_INT32
)
4932 BEGIN_CASE(JSOP_INDEXBASE
)
4934 * Here atoms can exceed script->atomMap.length as we use atoms as a
4935 * segment register for object literals as well.
4937 atoms
+= GET_INDEXBASE(regs
.pc
);
4938 END_CASE(JSOP_INDEXBASE
)
4940 BEGIN_CASE(JSOP_INDEXBASE1
)
4941 BEGIN_CASE(JSOP_INDEXBASE2
)
4942 BEGIN_CASE(JSOP_INDEXBASE3
)
4943 atoms
+= (op
- JSOP_INDEXBASE1
+ 1) << 16;
4944 END_CASE(JSOP_INDEXBASE3
)
4946 BEGIN_CASE(JSOP_RESETBASE0
)
4947 BEGIN_CASE(JSOP_RESETBASE
)
4948 atoms
= script
->atomMap
.vector
;
4949 END_CASE(JSOP_RESETBASE
)
4951 BEGIN_CASE(JSOP_DOUBLE
)
4953 JS_ASSERT(!regs
.fp
->hasImacropc());
4955 LOAD_DOUBLE(0, dbl
);
4958 END_CASE(JSOP_DOUBLE
)
4960 BEGIN_CASE(JSOP_STRING
)
4964 PUSH_STRING(ATOM_TO_STRING(atom
));
4966 END_CASE(JSOP_STRING
)
4968 BEGIN_CASE(JSOP_OBJECT
)
4971 LOAD_OBJECT(0, obj
);
4974 END_CASE(JSOP_OBJECT
)
4976 BEGIN_CASE(JSOP_REGEXP
)
4979 * Push a regexp object cloned from the regexp literal object mapped by the
4980 * bytecode at pc. ES5 finally fixed this bad old ES3 design flaw which was
4981 * flouted by many browser-based implementations.
4983 * We avoid the GetScopeChain call here and pass fp->scopeChain as
4984 * js_GetClassPrototype uses the latter only to locate the global.
4986 jsatomid index
= GET_FULL_INDEX(0);
4988 if (!js_GetClassPrototype(cx
, ®s
.fp
->scopeChain(), JSProto_RegExp
, &proto
))
4991 JSObject
*obj
= js_CloneRegExpObject(cx
, script
->getRegExp(index
), proto
);
4996 END_CASE(JSOP_REGEXP
)
4998 BEGIN_CASE(JSOP_ZERO
)
5002 BEGIN_CASE(JSOP_ONE
)
5006 BEGIN_CASE(JSOP_NULL
)
5010 BEGIN_CASE(JSOP_FALSE
)
5011 PUSH_BOOLEAN(false);
5012 END_CASE(JSOP_FALSE
)
5014 BEGIN_CASE(JSOP_TRUE
)
5019 BEGIN_CASE(JSOP_TABLESWITCH
)
5021 jsbytecode
*pc2
= regs
.pc
;
5022 len
= GET_JUMP_OFFSET(pc2
);
5025 * ECMAv2+ forbids conversion of discriminant, so we will skip to the
5026 * default case if the discriminant isn't already an int jsval. (This
5027 * opcode is emitted only for dense jsint-domain switches.)
5029 const Value
&rref
= *--regs
.sp
;
5031 if (rref
.isInt32()) {
5035 /* Don't use JSDOUBLE_IS_INT32; treat -0 (double) as 0. */
5036 if (!rref
.isDouble() || (d
= rref
.toDouble()) != (i
= int32_t(rref
.toDouble())))
5040 pc2
+= JUMP_OFFSET_LEN
;
5041 jsint low
= GET_JUMP_OFFSET(pc2
);
5042 pc2
+= JUMP_OFFSET_LEN
;
5043 jsint high
= GET_JUMP_OFFSET(pc2
);
5046 if ((jsuint
)i
< (jsuint
)(high
- low
+ 1)) {
5047 pc2
+= JUMP_OFFSET_LEN
+ JUMP_OFFSET_LEN
* i
;
5048 jsint off
= (jsint
) GET_JUMP_OFFSET(pc2
);
5057 BEGIN_CASE(JSOP_TABLESWITCHX
)
5059 jsbytecode
*pc2
= regs
.pc
;
5060 len
= GET_JUMPX_OFFSET(pc2
);
5063 * ECMAv2+ forbids conversion of discriminant, so we will skip to the
5064 * default case if the discriminant isn't already an int jsval. (This
5065 * opcode is emitted only for dense jsint-domain switches.)
5067 const Value
&rref
= *--regs
.sp
;
5069 if (rref
.isInt32()) {
5071 } else if (rref
.isDouble() && rref
.toDouble() == 0) {
5072 /* Treat -0 (double) as 0. */
5078 pc2
+= JUMPX_OFFSET_LEN
;
5079 jsint low
= GET_JUMP_OFFSET(pc2
);
5080 pc2
+= JUMP_OFFSET_LEN
;
5081 jsint high
= GET_JUMP_OFFSET(pc2
);
5084 if ((jsuint
)i
< (jsuint
)(high
- low
+ 1)) {
5085 pc2
+= JUMP_OFFSET_LEN
+ JUMPX_OFFSET_LEN
* i
;
5086 jsint off
= (jsint
) GET_JUMPX_OFFSET(pc2
);
5095 BEGIN_CASE(JSOP_LOOKUPSWITCHX
)
5098 off
= JUMPX_OFFSET_LEN
;
5099 goto do_lookup_switch
;
5101 BEGIN_CASE(JSOP_LOOKUPSWITCH
)
5102 off
= JUMP_OFFSET_LEN
;
5106 * JSOP_LOOKUPSWITCH and JSOP_LOOKUPSWITCHX are never used if any atom
5107 * index in it would exceed 64K limit.
5109 JS_ASSERT(!regs
.fp
->hasImacropc());
5110 JS_ASSERT(atoms
== script
->atomMap
.vector
);
5111 jsbytecode
*pc2
= regs
.pc
;
5113 Value lval
= regs
.sp
[-1];
5116 if (!lval
.isPrimitive())
5117 goto end_lookup_switch
;
5121 npairs
= (jsint
) GET_UINT16(pc2
);
5123 JS_ASSERT(npairs
); /* empty switch uses JSOP_TABLESWITCH */
5126 #define SEARCH_PAIRS(MATCH_CODE) \
5128 Value rval = script->getConst(GET_INDEX(pc2)); \
5134 if (--npairs == 0) { \
5140 if (lval
.isString()) {
5141 JSLinearString
*str
= lval
.toString()->ensureLinear(cx
);
5144 JSLinearString
*str2
;
5146 match
= (rval
.isString() &&
5147 ((str2
= rval
.toString()->assertIsLinear()) == str
||
5148 EqualStrings(str2
, str
)));
5150 } else if (lval
.isNumber()) {
5151 double ldbl
= lval
.toNumber();
5153 match
= rval
.isNumber() && ldbl
== rval
.toNumber();
5157 match
= (lval
== rval
);
5163 len
= (op
== JSOP_LOOKUPSWITCH
)
5164 ? GET_JUMP_OFFSET(pc2
)
5165 : GET_JUMPX_OFFSET(pc2
);
5170 BEGIN_CASE(JSOP_TRAP
)
5173 JSTrapStatus status
= JS_HandleTrap(cx
, script
, regs
.pc
, Jsvalify(&rval
));
5178 regs
.fp
->setReturnValue(rval
);
5179 interpReturnOK
= JS_TRUE
;
5182 cx
->setPendingException(rval
);
5187 JS_ASSERT(status
== JSTRAP_CONTINUE
);
5188 CHECK_INTERRUPT_HANDLER();
5189 JS_ASSERT(rval
.isInt32());
5190 op
= (JSOp
) rval
.toInt32();
5191 JS_ASSERT((uintN
)op
< (uintN
)JSOP_LIMIT
);
5195 BEGIN_CASE(JSOP_ARGUMENTS
)
5198 if (!js_GetArgsValue(cx
, regs
.fp
, &rval
))
5202 END_CASE(JSOP_ARGUMENTS
)
5204 BEGIN_CASE(JSOP_ARGSUB
)
5206 jsid id
= INT_TO_JSID(GET_ARGNO(regs
.pc
));
5208 if (!js_GetArgsProperty(cx
, regs
.fp
, id
, &rval
))
5212 END_CASE(JSOP_ARGSUB
)
5214 BEGIN_CASE(JSOP_ARGCNT
)
5216 jsid id
= ATOM_TO_JSID(rt
->atomState
.lengthAtom
);
5218 if (!js_GetArgsProperty(cx
, regs
.fp
, id
, &rval
))
5222 END_CASE(JSOP_ARGCNT
)
5224 BEGIN_CASE(JSOP_GETARG
)
5225 BEGIN_CASE(JSOP_CALLARG
)
5227 uint32 slot
= GET_ARGNO(regs
.pc
);
5228 JS_ASSERT(slot
< regs
.fp
->numFormalArgs());
5229 METER_SLOT_OP(op
, slot
);
5230 PUSH_COPY(argv
[slot
]);
5231 if (op
== JSOP_CALLARG
)
5234 END_CASE(JSOP_GETARG
)
5236 BEGIN_CASE(JSOP_SETARG
)
5238 uint32 slot
= GET_ARGNO(regs
.pc
);
5239 JS_ASSERT(slot
< regs
.fp
->numFormalArgs());
5240 METER_SLOT_OP(op
, slot
);
5241 argv
[slot
] = regs
.sp
[-1];
5243 END_SET_CASE(JSOP_SETARG
)
5245 BEGIN_CASE(JSOP_GETLOCAL
)
5247 uint32 slot
= GET_SLOTNO(regs
.pc
);
5248 JS_ASSERT(slot
< script
->nslots
);
5249 PUSH_COPY(regs
.fp
->slots()[slot
]);
5251 END_CASE(JSOP_GETLOCAL
)
5253 BEGIN_CASE(JSOP_CALLLOCAL
)
5255 uint32 slot
= GET_SLOTNO(regs
.pc
);
5256 JS_ASSERT(slot
< script
->nslots
);
5257 PUSH_COPY(regs
.fp
->slots()[slot
]);
5260 END_CASE(JSOP_CALLLOCAL
)
5262 BEGIN_CASE(JSOP_SETLOCAL
)
5264 uint32 slot
= GET_SLOTNO(regs
.pc
);
5265 JS_ASSERT(slot
< script
->nslots
);
5266 regs
.fp
->slots()[slot
] = regs
.sp
[-1];
5268 END_SET_CASE(JSOP_SETLOCAL
)
5270 BEGIN_CASE(JSOP_GETUPVAR_DBG
)
5271 BEGIN_CASE(JSOP_CALLUPVAR_DBG
)
5273 JSFunction
*fun
= regs
.fp
->fun();
5274 JS_ASSERT(FUN_KIND(fun
) == JSFUN_INTERPRETED
);
5275 JS_ASSERT(fun
->u
.i
.wrapper
);
5277 /* Scope for tempPool mark and local names allocation in it. */
5278 JSObject
*obj
, *obj2
;
5283 AutoLocalNameArray
names(cx
, fun
);
5287 uintN index
= fun
->script()->bindings
.countArgsAndVars() + GET_UINT16(regs
.pc
);
5288 atom
= JS_LOCAL_NAME_TO_ATOM(names
[index
]);
5289 id
= ATOM_TO_JSID(atom
);
5291 if (!js_FindProperty(cx
, id
, &obj
, &obj2
, &prop
))
5296 atomNotDefined
= atom
;
5297 goto atom_not_defined
;
5300 /* Minimize footprint with generic code instead of NATIVE_GET. */
5301 Value
*vp
= regs
.sp
;
5303 if (!obj
->getProperty(cx
, id
, vp
))
5306 if (op
== JSOP_CALLUPVAR_DBG
)
5309 END_CASE(JSOP_GETUPVAR_DBG
)
5311 BEGIN_CASE(JSOP_GETFCSLOT
)
5312 BEGIN_CASE(JSOP_CALLFCSLOT
)
5314 JS_ASSERT(regs
.fp
->isFunctionFrame() && !regs
.fp
->isEvalFrame());
5315 uintN index
= GET_UINT16(regs
.pc
);
5316 JSObject
*obj
= &argv
[-2].toObject();
5318 JS_ASSERT(index
< obj
->getFunctionPrivate()->script()->bindings
.countUpvars());
5319 PUSH_COPY(obj
->getFlatClosureUpvar(index
));
5320 if (op
== JSOP_CALLFCSLOT
)
5323 END_CASE(JSOP_GETFCSLOT
)
5325 BEGIN_CASE(JSOP_GETGLOBAL
)
5326 BEGIN_CASE(JSOP_CALLGLOBAL
)
5328 uint32 slot
= GET_SLOTNO(regs
.pc
);
5329 slot
= script
->getGlobalSlot(slot
);
5330 JSObject
*obj
= regs
.fp
->scopeChain().getGlobal();
5331 JS_ASSERT(obj
->containsSlot(slot
));
5332 PUSH_COPY(obj
->getSlot(slot
));
5333 if (op
== JSOP_CALLGLOBAL
)
5336 END_CASE(JSOP_GETGLOBAL
)
5338 BEGIN_CASE(JSOP_DEFCONST
)
5339 BEGIN_CASE(JSOP_DEFVAR
)
5341 uint32 index
= GET_INDEX(regs
.pc
);
5342 JSAtom
*atom
= atoms
[index
];
5344 JSObject
*obj
= ®s
.fp
->varobj(cx
);
5345 JS_ASSERT(!obj
->getOps()->defineProperty
);
5346 uintN attrs
= JSPROP_ENUMERATE
;
5347 if (!regs
.fp
->isEvalFrame())
5348 attrs
|= JSPROP_PERMANENT
;
5349 if (op
== JSOP_DEFCONST
)
5350 attrs
|= JSPROP_READONLY
;
5352 /* Lookup id in order to check for redeclaration problems. */
5353 jsid id
= ATOM_TO_JSID(atom
);
5354 JSProperty
*prop
= NULL
;
5356 if (op
== JSOP_DEFVAR
) {
5358 * Redundant declaration of a |var|, even one for a non-writable
5359 * property like |undefined| in ES5, does nothing.
5361 if (!obj
->lookupProperty(cx
, id
, &obj2
, &prop
))
5364 if (!CheckRedeclaration(cx
, obj
, id
, attrs
, &obj2
, &prop
))
5368 /* Bind a variable only if it's not yet defined. */
5370 if (!js_DefineNativeProperty(cx
, obj
, id
, UndefinedValue(), PropertyStub
, PropertyStub
,
5371 attrs
, 0, 0, &prop
)) {
5378 END_CASE(JSOP_DEFVAR
)
5380 BEGIN_CASE(JSOP_DEFFUN
)
5383 * A top-level function defined in Global or Eval code (see ECMA-262
5384 * Ed. 3), or else a SpiderMonkey extension: a named function statement in
5385 * a compound statement (not at the top statement level of global code, or
5386 * at the top level of a function body).
5390 JSObject
*obj
= FUN_OBJECT(fun
);
5393 if (FUN_NULL_CLOSURE(fun
)) {
5395 * Even a null closure needs a parent for principals finding.
5396 * FIXME: bug 476950, although debugger users may also demand some kind
5397 * of scope link for debugger-assisted eval-in-frame.
5399 obj2
= ®s
.fp
->scopeChain();
5401 JS_ASSERT(!fun
->isFlatClosure());
5403 obj2
= GetScopeChainFast(cx
, regs
.fp
, JSOP_DEFFUN
, JSOP_DEFFUN_LENGTH
);
5409 * If static link is not current scope, clone fun's object to link to the
5410 * current scope via parent. We do this to enable sharing of compiled
5411 * functions among multiple equivalent scopes, amortizing the cost of
5412 * compilation over a number of executions. Examples include XUL scripts
5413 * and event handlers shared among Firefox or other Mozilla app chrome
5414 * windows, and user-defined JS functions precompiled and then shared among
5415 * requests in server-side JS.
5417 if (obj
->getParent() != obj2
) {
5418 obj
= CloneFunctionObject(cx
, fun
, obj2
);
5425 * ECMA requires functions defined when entering Eval code to be
5428 uintN attrs
= regs
.fp
->isEvalFrame()
5430 : JSPROP_ENUMERATE
| JSPROP_PERMANENT
;
5433 * We define the function as a property of the variable object and not the
5434 * current scope chain even for the case of function expression statements
5435 * and functions defined by eval inside let or with blocks.
5437 JSObject
*parent
= ®s
.fp
->varobj(cx
);
5439 /* ES5 10.5 (NB: with subsequent errata). */
5440 jsid id
= ATOM_TO_JSID(fun
->atom
);
5441 JSProperty
*prop
= NULL
;
5443 if (!parent
->lookupProperty(cx
, id
, &pobj
, &prop
))
5446 Value rval
= ObjectValue(*obj
);
5450 if (!prop
|| pobj
!= parent
) {
5451 if (!parent
->defineProperty(cx
, id
, rval
, PropertyStub
, PropertyStub
, attrs
))
5457 JS_ASSERT(parent
->isNative());
5458 Shape
*shape
= reinterpret_cast<Shape
*>(prop
);
5459 if (parent
->isGlobal()) {
5460 if (shape
->configurable()) {
5461 if (!parent
->defineProperty(cx
, id
, rval
, PropertyStub
, PropertyStub
, attrs
))
5466 if (shape
->isAccessorDescriptor() || !shape
->writable() || !shape
->enumerable()) {
5467 JSAutoByteString bytes
;
5468 if (const char *name
= js_ValueToPrintable(cx
, IdToValue(id
), &bytes
)) {
5469 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
5470 JSMSG_CANT_REDEFINE_PROP
, name
);
5477 * Non-global properties, and global properties which we aren't simply
5478 * redefining, must be set. First, this preserves their attributes.
5479 * Second, this will produce warnings and/or errors as necessary if the
5480 * specified Call object property is not writable (const).
5484 if (!parent
->setProperty(cx
, id
, &rval
, script
->strictModeCode
))
5488 END_CASE(JSOP_DEFFUN
)
5490 BEGIN_CASE(JSOP_DEFFUN_FC
)
5491 BEGIN_CASE(JSOP_DEFFUN_DBGFC
)
5496 JSObject
*obj
= (op
== JSOP_DEFFUN_FC
)
5497 ? js_NewFlatClosure(cx
, fun
, JSOP_DEFFUN_FC
, JSOP_DEFFUN_FC_LENGTH
)
5498 : js_NewDebuggableFlatClosure(cx
, fun
);
5502 Value rval
= ObjectValue(*obj
);
5504 uintN attrs
= regs
.fp
->isEvalFrame()
5506 : JSPROP_ENUMERATE
| JSPROP_PERMANENT
;
5508 JSObject
&parent
= regs
.fp
->varobj(cx
);
5510 jsid id
= ATOM_TO_JSID(fun
->atom
);
5511 if (!CheckRedeclaration(cx
, &parent
, id
, attrs
, NULL
, NULL
))
5514 if ((attrs
== JSPROP_ENUMERATE
)
5515 ? !parent
.setProperty(cx
, id
, &rval
, script
->strictModeCode
)
5516 : !parent
.defineProperty(cx
, id
, rval
, PropertyStub
, PropertyStub
, attrs
)) {
5520 END_CASE(JSOP_DEFFUN_FC
)
5522 BEGIN_CASE(JSOP_DEFLOCALFUN
)
5525 * Define a local function (i.e., one nested at the top level of another
5526 * function), parented by the current scope chain, stored in a local
5527 * variable slot that the compiler allocated. This is an optimization over
5528 * JSOP_DEFFUN that avoids requiring a call object for the outer function's
5532 LOAD_FUNCTION(SLOTNO_LEN
);
5533 JS_ASSERT(fun
->isInterpreted());
5534 JS_ASSERT(!FUN_FLAT_CLOSURE(fun
));
5535 JSObject
*obj
= FUN_OBJECT(fun
);
5537 if (FUN_NULL_CLOSURE(fun
)) {
5538 obj
= CloneFunctionObject(cx
, fun
, ®s
.fp
->scopeChain());
5542 JSObject
*parent
= GetScopeChainFast(cx
, regs
.fp
, JSOP_DEFLOCALFUN
,
5543 JSOP_DEFLOCALFUN_LENGTH
);
5547 if (obj
->getParent() != parent
) {
5549 if (TRACE_RECORDER(cx
))
5550 AbortRecording(cx
, "DEFLOCALFUN for closure");
5552 obj
= CloneFunctionObject(cx
, fun
, parent
);
5558 uint32 slot
= GET_SLOTNO(regs
.pc
);
5559 TRACE_2(DefLocalFunSetSlot
, slot
, obj
);
5561 regs
.fp
->slots()[slot
].setObject(*obj
);
5563 END_CASE(JSOP_DEFLOCALFUN
)
5565 BEGIN_CASE(JSOP_DEFLOCALFUN_FC
)
5568 LOAD_FUNCTION(SLOTNO_LEN
);
5570 JSObject
*obj
= js_NewFlatClosure(cx
, fun
, JSOP_DEFLOCALFUN_FC
, JSOP_DEFLOCALFUN_FC_LENGTH
);
5574 uint32 slot
= GET_SLOTNO(regs
.pc
);
5575 TRACE_2(DefLocalFunSetSlot
, slot
, obj
);
5577 regs
.fp
->slots()[slot
].setObject(*obj
);
5579 END_CASE(JSOP_DEFLOCALFUN_FC
)
5581 BEGIN_CASE(JSOP_DEFLOCALFUN_DBGFC
)
5584 LOAD_FUNCTION(SLOTNO_LEN
);
5586 JSObject
*obj
= js_NewDebuggableFlatClosure(cx
, fun
);
5590 uint32 slot
= GET_SLOTNO(regs
.pc
);
5591 regs
.fp
->slots()[slot
].setObject(*obj
);
5593 END_CASE(JSOP_DEFLOCALFUN_DBGFC
)
5595 BEGIN_CASE(JSOP_LAMBDA
)
5597 /* Load the specified function object literal. */
5600 JSObject
*obj
= FUN_OBJECT(fun
);
5602 /* do-while(0) so we can break instead of using a goto. */
5605 if (FUN_NULL_CLOSURE(fun
)) {
5606 parent
= ®s
.fp
->scopeChain();
5608 if (obj
->getParent() == parent
) {
5609 jsbytecode
*pc2
= AdvanceOverBlockchainOp(regs
.pc
+ JSOP_LAMBDA_LENGTH
);
5610 JSOp op2
= JSOp(*pc2
);
5613 * Optimize var obj = {method: function () { ... }, ...},
5614 * this.method = function () { ... }; and other significant
5615 * single-use-of-null-closure bytecode sequences.
5617 * WARNING: code in TraceRecorder::record_JSOP_LAMBDA must
5618 * match the optimization cases in the following code that
5619 * break from the outer do-while(0).
5621 if (op2
== JSOP_INITMETHOD
) {
5623 const Value
&lref
= regs
.sp
[-1];
5624 JS_ASSERT(lref
.isObject());
5625 JSObject
*obj2
= &lref
.toObject();
5626 JS_ASSERT(obj2
->getClass() == &js_ObjectClass
);
5629 fun
->setMethodAtom(script
->getAtom(GET_FULL_INDEX(pc2
- regs
.pc
)));
5630 JS_FUNCTION_METER(cx
, joinedinitmethod
);
5634 if (op2
== JSOP_SETMETHOD
) {
5636 op2
= JSOp(pc2
[JSOP_SETMETHOD_LENGTH
]);
5637 JS_ASSERT(op2
== JSOP_POP
|| op2
== JSOP_POPV
);
5639 const Value
&lref
= regs
.sp
[-1];
5640 if (lref
.isObject() && lref
.toObject().canHaveMethodBarrier()) {
5641 fun
->setMethodAtom(script
->getAtom(GET_FULL_INDEX(pc2
- regs
.pc
)));
5642 JS_FUNCTION_METER(cx
, joinedsetmethod
);
5645 } else if (fun
->joinable()) {
5646 if (op2
== JSOP_CALL
) {
5648 * Array.prototype.sort and String.prototype.replace are
5649 * optimized as if they are special form. We know that they
5650 * won't leak the joined function object in obj, therefore
5651 * we don't need to clone that compiler- created function
5652 * object for identity/mutation reasons.
5654 int iargc
= GET_ARGC(pc2
);
5657 * Note that we have not yet pushed obj as the final argument,
5658 * so regs.sp[1 - (iargc + 2)], and not regs.sp[-(iargc + 2)],
5659 * is the callee for this JSOP_CALL.
5661 const Value
&cref
= regs
.sp
[1 - (iargc
+ 2)];
5664 if (IsFunctionObject(cref
, &callee
)) {
5665 JSFunction
*calleeFun
= GET_FUNCTION_PRIVATE(cx
, callee
);
5666 if (Native native
= calleeFun
->maybeNative()) {
5667 if (iargc
== 1 && native
== array_sort
) {
5668 JS_FUNCTION_METER(cx
, joinedsort
);
5671 if (iargc
== 2 && native
== str_replace
) {
5672 JS_FUNCTION_METER(cx
, joinedreplace
);
5677 } else if (op2
== JSOP_NULL
) {
5678 pc2
+= JSOP_NULL_LENGTH
;
5681 if (op2
== JSOP_CALL
&& GET_ARGC(pc2
) == 0) {
5682 JS_FUNCTION_METER(cx
, joinedmodulepat
);
5690 if (rt
->functionMeterFilename
) {
5691 // No locking, this is mainly for js shell testing.
5692 ++rt
->functionMeter
.unjoined
;
5694 typedef JSRuntime::FunctionCountMap HM
;
5695 HM
&h
= rt
->unjoinedFunctionCountMap
;
5696 HM::AddPtr p
= h
.lookupForAdd(fun
);
5700 JS_ASSERT(p
->key
== fun
);
5706 parent
= GetScopeChainFast(cx
, regs
.fp
, JSOP_LAMBDA
, JSOP_LAMBDA_LENGTH
);
5711 obj
= CloneFunctionObject(cx
, fun
, parent
);
5718 END_CASE(JSOP_LAMBDA
)
5720 BEGIN_CASE(JSOP_LAMBDA_FC
)
5725 JSObject
*obj
= js_NewFlatClosure(cx
, fun
, JSOP_LAMBDA_FC
, JSOP_LAMBDA_FC_LENGTH
);
5731 END_CASE(JSOP_LAMBDA_FC
)
5733 BEGIN_CASE(JSOP_LAMBDA_DBGFC
)
5738 JSObject
*obj
= js_NewDebuggableFlatClosure(cx
, fun
);
5744 END_CASE(JSOP_LAMBDA_DBGFC
)
5746 BEGIN_CASE(JSOP_CALLEE
)
5747 JS_ASSERT(regs
.fp
->isFunctionFrame() && !regs
.fp
->isEvalFrame());
5748 PUSH_COPY(argv
[-2]);
5749 END_CASE(JSOP_CALLEE
)
5751 BEGIN_CASE(JSOP_GETTER
)
5752 BEGIN_CASE(JSOP_SETTER
)
5755 JSOp op2
= (JSOp
) *++regs
.pc
;
5761 case JSOP_INDEXBASE
:
5762 atoms
+= GET_INDEXBASE(regs
.pc
);
5763 regs
.pc
+= JSOP_INDEXBASE_LENGTH
- 1;
5764 goto do_getter_setter
;
5765 case JSOP_INDEXBASE1
:
5766 case JSOP_INDEXBASE2
:
5767 case JSOP_INDEXBASE3
:
5768 atoms
+= (op2
- JSOP_INDEXBASE1
+ 1) << 16;
5769 goto do_getter_setter
;
5776 id
= ATOM_TO_JSID(atom
);
5786 FETCH_OBJECT(cx
, i
- 1, obj
);
5791 JS_ASSERT(regs
.sp
- regs
.fp
->base() >= 2);
5796 id
= ATOM_TO_JSID(atom
);
5800 JS_ASSERT(op2
== JSOP_INITELEM
);
5802 JS_ASSERT(regs
.sp
- regs
.fp
->base() >= 3);
5808 const Value
&lref
= regs
.sp
[i
-1];
5809 JS_ASSERT(lref
.isObject());
5810 obj
= &lref
.toObject();
5815 /* Ensure that id has a type suitable for use with obj. */
5816 if (JSID_IS_VOID(id
))
5817 FETCH_ELEMENT_ID(obj
, i
, id
);
5819 if (!js_IsCallable(rval
)) {
5820 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
5821 JSMSG_BAD_GETTER_OR_SETTER
,
5829 * Getters and setters are just like watchpoints from an access control
5834 if (!CheckAccess(cx
, obj
, id
, JSACC_WATCH
, &rtmp
, &attrs
))
5837 PropertyOp getter
, setter
;
5838 if (op
== JSOP_GETTER
) {
5839 getter
= CastAsPropertyOp(&rval
.toObject());
5840 setter
= PropertyStub
;
5841 attrs
= JSPROP_GETTER
;
5843 getter
= PropertyStub
;
5844 setter
= CastAsPropertyOp(&rval
.toObject());
5845 attrs
= JSPROP_SETTER
;
5847 attrs
|= JSPROP_ENUMERATE
| JSPROP_SHARED
;
5849 /* Check for a readonly or permanent property of the same name. */
5850 if (!CheckRedeclaration(cx
, obj
, id
, attrs
, NULL
, NULL
))
5853 if (!obj
->defineProperty(cx
, id
, UndefinedValue(), getter
, setter
, attrs
))
5857 if (js_CodeSpec
[op2
].ndefs
> js_CodeSpec
[op2
].nuses
) {
5858 JS_ASSERT(js_CodeSpec
[op2
].ndefs
== js_CodeSpec
[op2
].nuses
+ 1);
5860 assertSameCompartment(cx
, regs
.sp
[-1]);
5862 len
= js_CodeSpec
[op2
].length
;
5866 BEGIN_CASE(JSOP_HOLE
)
5870 BEGIN_CASE(JSOP_NEWINIT
)
5872 jsint i
= regs
.pc
[1];
5874 JS_ASSERT(i
== JSProto_Array
|| i
== JSProto_Object
);
5877 if (i
== JSProto_Array
) {
5878 obj
= NewDenseEmptyArray(cx
);
5880 gc::FinalizeKind kind
= GuessObjectGCKind(0, false);
5881 obj
= NewBuiltinClassInstance(cx
, &js_ObjectClass
, kind
);
5888 CHECK_INTERRUPT_HANDLER();
5890 END_CASE(JSOP_NEWINIT
)
5892 BEGIN_CASE(JSOP_NEWARRAY
)
5894 unsigned count
= GET_UINT24(regs
.pc
);
5895 JSObject
*obj
= NewDenseAllocatedArray(cx
, count
);
5900 CHECK_INTERRUPT_HANDLER();
5902 END_CASE(JSOP_NEWARRAY
)
5904 BEGIN_CASE(JSOP_NEWOBJECT
)
5907 LOAD_OBJECT(0, baseobj
);
5909 JSObject
*obj
= CopyInitializerObject(cx
, baseobj
);
5915 CHECK_INTERRUPT_HANDLER();
5917 END_CASE(JSOP_NEWOBJECT
)
5919 BEGIN_CASE(JSOP_ENDINIT
)
5921 /* FIXME remove JSOP_ENDINIT bug 588522 */
5922 JS_ASSERT(regs
.sp
- regs
.fp
->base() >= 1);
5923 JS_ASSERT(regs
.sp
[-1].isObject());
5925 END_CASE(JSOP_ENDINIT
)
5927 BEGIN_CASE(JSOP_INITPROP
)
5928 BEGIN_CASE(JSOP_INITMETHOD
)
5930 /* Load the property's initial value into rval. */
5931 JS_ASSERT(regs
.sp
- regs
.fp
->base() >= 2);
5932 Value rval
= regs
.sp
[-1];
5934 /* Load the object being initialized into lval/obj. */
5935 JSObject
*obj
= ®s
.sp
[-2].toObject();
5936 JS_ASSERT(obj
->isObject());
5939 * Probe the property cache.
5941 * On a hit, if the cached shape has a non-default setter, it must be
5942 * __proto__. If shape->previous() != obj->lastProperty(), there must be a
5943 * repeated property name. The fast path does not handle these two cases.
5945 PropertyCacheEntry
*entry
;
5947 if (JS_PROPERTY_CACHE(cx
).testForInit(rt
, regs
.pc
, obj
, &shape
, &entry
) &&
5948 shape
->hasDefaultSetter() &&
5949 shape
->previous() == obj
->lastProperty())
5951 /* Fast path. Property cache hit. */
5952 uint32 slot
= shape
->slot
;
5954 JS_ASSERT(slot
== obj
->slotSpan());
5955 JS_ASSERT(slot
>= JSSLOT_FREE(obj
->getClass()));
5956 if (slot
< obj
->numSlots()) {
5957 JS_ASSERT(obj
->getSlot(slot
).isUndefined());
5959 if (!obj
->allocSlot(cx
, &slot
))
5961 JS_ASSERT(slot
== shape
->slot
);
5964 /* A new object, or one we just extended in a recent initprop op. */
5965 JS_ASSERT(!obj
->lastProperty() ||
5966 obj
->shape() == obj
->lastProperty()->shape
);
5967 obj
->extend(cx
, shape
);
5970 * No method change check here because here we are adding a new
5971 * property, not updating an existing slot's value that might
5972 * contain a method of a branded shape.
5974 TRACE_1(AddProperty
, obj
);
5975 obj
->nativeSetSlot(slot
, rval
);
5977 PCMETER(JS_PROPERTY_CACHE(cx
).inipcmisses
++);
5979 /* Get the immediate property name into id. */
5982 jsid id
= ATOM_TO_JSID(atom
);
5984 /* No need to check for duplicate property; the compiler already did. */
5986 uintN defineHow
= (op
== JSOP_INITMETHOD
)
5987 ? JSDNP_CACHE_RESULT
| JSDNP_SET_METHOD
5988 : JSDNP_CACHE_RESULT
;
5989 if (!(JS_UNLIKELY(atom
== cx
->runtime
->atomState
.protoAtom
)
5990 ? js_SetPropertyHelper(cx
, obj
, id
, defineHow
, &rval
, script
->strictModeCode
)
5991 : js_DefineNativeProperty(cx
, obj
, id
, rval
, NULL
, NULL
,
5992 JSPROP_ENUMERATE
, 0, 0, NULL
,
5998 /* Common tail for property cache hit and miss cases. */
6001 END_CASE(JSOP_INITPROP
);
6003 BEGIN_CASE(JSOP_INITELEM
)
6005 /* Pop the element's value into rval. */
6006 JS_ASSERT(regs
.sp
- regs
.fp
->base() >= 3);
6007 const Value
&rref
= regs
.sp
[-1];
6009 /* Find the object being initialized at top of stack. */
6010 const Value
&lref
= regs
.sp
[-3];
6011 JS_ASSERT(lref
.isObject());
6012 JSObject
*obj
= &lref
.toObject();
6014 /* Fetch id now that we have obj. */
6016 FETCH_ELEMENT_ID(obj
, -2, id
);
6018 /* No need to check for duplicate property; the compiler already did. */
6021 * If rref is a hole, do not call JSObject::defineProperty. In this case,
6022 * obj must be an array, so if the current op is the last element
6023 * initialiser, set the array length to one greater than id.
6025 if (rref
.isMagic(JS_ARRAY_HOLE
)) {
6026 JS_ASSERT(obj
->isArray());
6027 JS_ASSERT(JSID_IS_INT(id
));
6028 JS_ASSERT(jsuint(JSID_TO_INT(id
)) < JS_ARGS_LENGTH_MAX
);
6029 if (js_GetOpcode(cx
, script
, regs
.pc
+ JSOP_INITELEM_LENGTH
) == JSOP_ENDINIT
&&
6030 !js_SetLengthProperty(cx
, obj
, (jsuint
) (JSID_TO_INT(id
) + 1))) {
6034 if (!obj
->defineProperty(cx
, id
, rref
, NULL
, NULL
, JSPROP_ENUMERATE
))
6039 END_CASE(JSOP_INITELEM
)
6041 #if JS_HAS_SHARP_VARS
6043 BEGIN_CASE(JSOP_DEFSHARP
)
6045 uint32 slot
= GET_UINT16(regs
.pc
);
6046 JS_ASSERT(slot
+ 1 < regs
.fp
->numFixed());
6047 const Value
&lref
= regs
.fp
->slots()[slot
];
6049 if (lref
.isObject()) {
6050 obj
= &lref
.toObject();
6052 JS_ASSERT(lref
.isUndefined());
6053 obj
= NewDenseEmptyArray(cx
);
6056 regs
.fp
->slots()[slot
].setObject(*obj
);
6058 jsint i
= (jsint
) GET_UINT16(regs
.pc
+ UINT16_LEN
);
6059 jsid id
= INT_TO_JSID(i
);
6060 const Value
&rref
= regs
.sp
[-1];
6061 if (rref
.isPrimitive()) {
6063 JS_snprintf(numBuf
, sizeof numBuf
, "%u", (unsigned) i
);
6064 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6065 JSMSG_BAD_SHARP_DEF
, numBuf
);
6068 if (!obj
->defineProperty(cx
, id
, rref
, NULL
, NULL
, JSPROP_ENUMERATE
))
6071 END_CASE(JSOP_DEFSHARP
)
6073 BEGIN_CASE(JSOP_USESHARP
)
6075 uint32 slot
= GET_UINT16(regs
.pc
);
6076 JS_ASSERT(slot
+ 1 < regs
.fp
->numFixed());
6077 const Value
&lref
= regs
.fp
->slots()[slot
];
6078 jsint i
= (jsint
) GET_UINT16(regs
.pc
+ UINT16_LEN
);
6080 if (lref
.isUndefined()) {
6081 rval
.setUndefined();
6083 JSObject
*obj
= ®s
.fp
->slots()[slot
].toObject();
6084 jsid id
= INT_TO_JSID(i
);
6085 if (!obj
->getProperty(cx
, id
, &rval
))
6088 if (!rval
.isObjectOrNull()) {
6091 JS_snprintf(numBuf
, sizeof numBuf
, "%u", (unsigned) i
);
6092 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6093 JSMSG_BAD_SHARP_USE
, numBuf
);
6098 END_CASE(JSOP_USESHARP
)
6100 BEGIN_CASE(JSOP_SHARPINIT
)
6102 uint32 slot
= GET_UINT16(regs
.pc
);
6103 JS_ASSERT(slot
+ 1 < regs
.fp
->numFixed());
6104 Value
*vp
= ®s
.fp
->slots()[slot
];
6108 * We peek ahead safely here because empty initialisers get zero
6109 * JSOP_SHARPINIT ops, and non-empty ones get two: the first comes
6110 * immediately after JSOP_NEWINIT followed by one or more property
6111 * initialisers; and the second comes directly before JSOP_ENDINIT.
6113 if (regs
.pc
[JSOP_SHARPINIT_LENGTH
] != JSOP_ENDINIT
) {
6114 rval
.setInt32(rval
.isUndefined() ? 1 : rval
.toInt32() + 1);
6116 JS_ASSERT(rval
.isInt32());
6117 rval
.getInt32Ref() -= 1;
6118 if (rval
.toInt32() == 0)
6119 vp
[0].setUndefined();
6123 END_CASE(JSOP_SHARPINIT
)
6125 #endif /* JS_HAS_SHARP_VARS */
6128 BEGIN_CASE(JSOP_GOSUB
)
6129 PUSH_BOOLEAN(false);
6130 jsint i
= (regs
.pc
- script
->main
) + JSOP_GOSUB_LENGTH
;
6132 len
= GET_JUMP_OFFSET(regs
.pc
);
6137 BEGIN_CASE(JSOP_GOSUBX
)
6138 PUSH_BOOLEAN(false);
6139 jsint i
= (regs
.pc
- script
->main
) + JSOP_GOSUBX_LENGTH
;
6140 len
= GET_JUMPX_OFFSET(regs
.pc
);
6146 BEGIN_CASE(JSOP_RETSUB
)
6147 /* Pop [exception or hole, retsub pc-index]. */
6151 JS_ASSERT(lval
.isBoolean());
6152 if (lval
.toBoolean()) {
6154 * Exception was pending during finally, throw it *before* we adjust
6155 * pc, because pc indexes into script->trynotes. This turns out not to
6156 * be necessary, but it seems clearer. And it points out a FIXME:
6157 * 350509, due to Igor Bukanov.
6159 cx
->setPendingException(rval
);
6162 JS_ASSERT(rval
.isInt32());
6163 len
= rval
.toInt32();
6164 regs
.pc
= script
->main
;
6168 BEGIN_CASE(JSOP_EXCEPTION
)
6169 PUSH_COPY(cx
->getPendingException());
6170 cx
->clearPendingException();
6171 #if defined(JS_TRACER) && defined(JS_METHODJIT)
6172 if (interpMode
== JSINTERP_PROFILE
) {
6173 leaveOnSafePoint
= true;
6174 LEAVE_ON_SAFE_POINT();
6178 END_CASE(JSOP_EXCEPTION
)
6180 BEGIN_CASE(JSOP_FINALLY
)
6182 END_CASE(JSOP_FINALLY
)
6184 BEGIN_CASE(JSOP_THROWING
)
6186 JS_ASSERT(!cx
->isExceptionPending());
6189 cx
->setPendingException(v
);
6191 END_CASE(JSOP_THROWING
)
6193 BEGIN_CASE(JSOP_THROW
)
6195 JS_ASSERT(!cx
->isExceptionPending());
6199 cx
->setPendingException(v
);
6200 /* let the code at error try to catch the exception. */
6203 BEGIN_CASE(JSOP_SETLOCALPOP
)
6206 * The stack must have a block with at least one local slot below the
6209 JS_ASSERT((size_t) (regs
.sp
- regs
.fp
->base()) >= 2);
6210 uint32 slot
= GET_UINT16(regs
.pc
);
6211 JS_ASSERT(slot
+ 1 < script
->nslots
);
6212 POP_COPY_TO(regs
.fp
->slots()[slot
]);
6214 END_CASE(JSOP_SETLOCALPOP
)
6216 BEGIN_CASE(JSOP_IFPRIMTOP
)
6218 * If the top of stack is of primitive type, jump to our target. Otherwise
6219 * advance to the next opcode.
6221 JS_ASSERT(regs
.sp
> regs
.fp
->base());
6222 if (regs
.sp
[-1].isPrimitive()) {
6223 len
= GET_JUMP_OFFSET(regs
.pc
);
6226 END_CASE(JSOP_IFPRIMTOP
)
6228 BEGIN_CASE(JSOP_PRIMTOP
)
6229 JS_ASSERT(regs
.sp
> regs
.fp
->base());
6230 if (regs
.sp
[-1].isObject()) {
6231 jsint i
= GET_INT8(regs
.pc
);
6232 js_ReportValueError2(cx
, JSMSG_CANT_CONVERT_TO
, -2, regs
.sp
[-2], NULL
,
6233 (i
== JSTYPE_VOID
) ? "primitive type" : JS_TYPE_STR(i
));
6236 END_CASE(JSOP_PRIMTOP
)
6238 BEGIN_CASE(JSOP_OBJTOP
)
6239 if (regs
.sp
[-1].isPrimitive()) {
6240 js_ReportValueError(cx
, GET_UINT16(regs
.pc
), -1, regs
.sp
[-1], NULL
);
6243 END_CASE(JSOP_OBJTOP
)
6245 BEGIN_CASE(JSOP_INSTANCEOF
)
6247 const Value
&rref
= regs
.sp
[-1];
6248 if (rref
.isPrimitive()) {
6249 js_ReportValueError(cx
, JSMSG_BAD_INSTANCEOF_RHS
, -1, rref
, NULL
);
6252 JSObject
*obj
= &rref
.toObject();
6253 const Value
&lref
= regs
.sp
[-2];
6254 JSBool cond
= JS_FALSE
;
6255 if (!HasInstance(cx
, obj
, &lref
, &cond
))
6258 regs
.sp
[-1].setBoolean(cond
);
6260 END_CASE(JSOP_INSTANCEOF
)
6262 BEGIN_CASE(JSOP_DEBUGGER
)
6264 JSDebuggerHandler handler
= cx
->debugHooks
->debuggerHandler
;
6267 switch (handler(cx
, script
, regs
.pc
, Jsvalify(&rval
), cx
->debugHooks
->debuggerHandlerData
)) {
6270 case JSTRAP_CONTINUE
:
6273 regs
.fp
->setReturnValue(rval
);
6274 interpReturnOK
= JS_TRUE
;
6277 cx
->setPendingException(rval
);
6281 CHECK_INTERRUPT_HANDLER();
6284 END_CASE(JSOP_DEBUGGER
)
6286 #if JS_HAS_XML_SUPPORT
6287 BEGIN_CASE(JSOP_DEFXMLNS
)
6289 if (!js_SetDefaultXMLNamespace(cx
, regs
.sp
[-1]))
6293 END_CASE(JSOP_DEFXMLNS
)
6295 BEGIN_CASE(JSOP_ANYNAME
)
6298 if (!js_GetAnyName(cx
, &id
))
6300 PUSH_COPY(IdToValue(id
));
6302 END_CASE(JSOP_ANYNAME
)
6304 BEGIN_CASE(JSOP_QNAMEPART
)
6308 PUSH_STRING(ATOM_TO_STRING(atom
));
6310 END_CASE(JSOP_QNAMEPART
)
6312 BEGIN_CASE(JSOP_QNAMECONST
)
6316 Value rval
= StringValue(ATOM_TO_STRING(atom
));
6317 Value lval
= regs
.sp
[-1];
6318 JSObject
*obj
= js_ConstructXMLQNameObject(cx
, lval
, rval
);
6321 regs
.sp
[-1].setObject(*obj
);
6323 END_CASE(JSOP_QNAMECONST
)
6325 BEGIN_CASE(JSOP_QNAME
)
6327 Value rval
= regs
.sp
[-1];
6328 Value lval
= regs
.sp
[-2];
6329 JSObject
*obj
= js_ConstructXMLQNameObject(cx
, lval
, rval
);
6333 regs
.sp
[-1].setObject(*obj
);
6335 END_CASE(JSOP_QNAME
)
6337 BEGIN_CASE(JSOP_TOATTRNAME
)
6341 if (!js_ToAttributeName(cx
, &rval
))
6345 END_CASE(JSOP_TOATTRNAME
)
6347 BEGIN_CASE(JSOP_TOATTRVAL
)
6351 JS_ASSERT(rval
.isString());
6352 JSString
*str
= js_EscapeAttributeValue(cx
, rval
.toString(), JS_FALSE
);
6355 regs
.sp
[-1].setString(str
);
6357 END_CASE(JSOP_TOATTRVAL
)
6359 BEGIN_CASE(JSOP_ADDATTRNAME
)
6360 BEGIN_CASE(JSOP_ADDATTRVAL
)
6362 Value rval
= regs
.sp
[-1];
6363 Value lval
= regs
.sp
[-2];
6364 JSString
*str
= lval
.toString();
6365 JSString
*str2
= rval
.toString();
6366 str
= js_AddAttributePart(cx
, op
== JSOP_ADDATTRNAME
, str
, str2
);
6370 regs
.sp
[-1].setString(str
);
6372 END_CASE(JSOP_ADDATTRNAME
)
6374 BEGIN_CASE(JSOP_BINDXMLNAME
)
6380 if (!js_FindXMLProperty(cx
, lval
, &obj
, &id
))
6382 regs
.sp
[-1].setObjectOrNull(obj
);
6383 PUSH_COPY(IdToValue(id
));
6385 END_CASE(JSOP_BINDXMLNAME
)
6387 BEGIN_CASE(JSOP_SETXMLNAME
)
6389 JSObject
*obj
= ®s
.sp
[-3].toObject();
6390 Value rval
= regs
.sp
[-1];
6392 FETCH_ELEMENT_ID(obj
, -2, id
);
6393 if (!obj
->setProperty(cx
, id
, &rval
, script
->strictModeCode
))
6399 END_CASE(JSOP_SETXMLNAME
)
6401 BEGIN_CASE(JSOP_CALLXMLNAME
)
6402 BEGIN_CASE(JSOP_XMLNAME
)
6404 Value lval
= regs
.sp
[-1];
6407 if (!js_FindXMLProperty(cx
, lval
, &obj
, &id
))
6410 if (!obj
->getProperty(cx
, id
, &rval
))
6413 if (op
== JSOP_CALLXMLNAME
)
6414 SLOW_PUSH_THISV(cx
, obj
);
6416 END_CASE(JSOP_XMLNAME
)
6418 BEGIN_CASE(JSOP_DESCENDANTS
)
6419 BEGIN_CASE(JSOP_DELDESC
)
6422 FETCH_OBJECT(cx
, -2, obj
);
6423 jsval rval
= Jsvalify(regs
.sp
[-1]);
6424 if (!js_GetXMLDescendants(cx
, obj
, rval
, &rval
))
6427 if (op
== JSOP_DELDESC
) {
6428 regs
.sp
[-1] = Valueify(rval
); /* set local root */
6429 if (!js_DeleteXMLListElements(cx
, JSVAL_TO_OBJECT(rval
)))
6431 rval
= JSVAL_TRUE
; /* always succeed */
6435 regs
.sp
[-1] = Valueify(rval
);
6437 END_CASE(JSOP_DESCENDANTS
)
6440 BEGIN_CASE(JSOP_FILTER
)
6442 * We push the hole value before jumping to [enditer] so we can detect the
6443 * first iteration and direct js_StepXMLListFilter to initialize filter's
6447 len
= GET_JUMP_OFFSET(regs
.pc
);
6452 BEGIN_CASE(JSOP_ENDFILTER
)
6454 bool cond
= !regs
.sp
[-1].isMagic();
6456 /* Exit the "with" block left from the previous iteration. */
6459 if (!js_StepXMLListFilter(cx
, cond
))
6461 if (!regs
.sp
[-1].isNull()) {
6463 * Decrease sp after EnterWith returns as we use sp[-1] there to root
6466 JS_ASSERT(IsXML(regs
.sp
[-1]));
6467 if (!js_EnterWith(cx
, -2, JSOP_ENDFILTER
, JSOP_ENDFILTER_LENGTH
))
6470 len
= GET_JUMP_OFFSET(regs
.pc
);
6476 END_CASE(JSOP_ENDFILTER
);
6478 BEGIN_CASE(JSOP_TOXML
)
6480 Value rval
= regs
.sp
[-1];
6481 JSObject
*obj
= js_ValueToXMLObject(cx
, rval
);
6484 regs
.sp
[-1].setObject(*obj
);
6486 END_CASE(JSOP_TOXML
)
6488 BEGIN_CASE(JSOP_TOXMLLIST
)
6490 Value rval
= regs
.sp
[-1];
6491 JSObject
*obj
= js_ValueToXMLListObject(cx
, rval
);
6494 regs
.sp
[-1].setObject(*obj
);
6496 END_CASE(JSOP_TOXMLLIST
)
6498 BEGIN_CASE(JSOP_XMLTAGEXPR
)
6500 Value rval
= regs
.sp
[-1];
6501 JSString
*str
= js_ValueToString(cx
, rval
);
6504 regs
.sp
[-1].setString(str
);
6506 END_CASE(JSOP_XMLTAGEXPR
)
6508 BEGIN_CASE(JSOP_XMLELTEXPR
)
6510 Value rval
= regs
.sp
[-1];
6513 str
= js_ValueToXMLString(cx
, rval
);
6515 str
= js_ValueToString(cx
, rval
);
6517 str
= js_EscapeElementValue(cx
, str
);
6521 regs
.sp
[-1].setString(str
);
6523 END_CASE(JSOP_XMLELTEXPR
)
6525 BEGIN_CASE(JSOP_XMLCDATA
)
6529 JSString
*str
= ATOM_TO_STRING(atom
);
6530 JSObject
*obj
= js_NewXMLSpecialObject(cx
, JSXML_CLASS_TEXT
, NULL
, str
);
6535 END_CASE(JSOP_XMLCDATA
)
6537 BEGIN_CASE(JSOP_XMLCOMMENT
)
6541 JSString
*str
= ATOM_TO_STRING(atom
);
6542 JSObject
*obj
= js_NewXMLSpecialObject(cx
, JSXML_CLASS_COMMENT
, NULL
, str
);
6547 END_CASE(JSOP_XMLCOMMENT
)
6549 BEGIN_CASE(JSOP_XMLPI
)
6553 JSString
*str
= ATOM_TO_STRING(atom
);
6554 Value rval
= regs
.sp
[-1];
6555 JSString
*str2
= rval
.toString();
6556 JSObject
*obj
= js_NewXMLSpecialObject(cx
, JSXML_CLASS_PROCESSING_INSTRUCTION
, str
, str2
);
6559 regs
.sp
[-1].setObject(*obj
);
6561 END_CASE(JSOP_XMLPI
)
6563 BEGIN_CASE(JSOP_GETFUNNS
)
6566 if (!js_GetFunctionNamespace(cx
, &rval
))
6570 END_CASE(JSOP_GETFUNNS
)
6571 #endif /* JS_HAS_XML_SUPPORT */
6573 BEGIN_CASE(JSOP_ENTERBLOCK
)
6576 LOAD_OBJECT(0, obj
);
6577 JS_ASSERT(obj
->isStaticBlock());
6578 JS_ASSERT(regs
.fp
->base() + OBJ_BLOCK_DEPTH(cx
, obj
) == regs
.sp
);
6579 Value
*vp
= regs
.sp
+ OBJ_BLOCK_COUNT(cx
, obj
);
6580 JS_ASSERT(regs
.sp
< vp
);
6581 JS_ASSERT(vp
<= regs
.fp
->slots() + script
->nslots
);
6582 SetValueRangeToUndefined(regs
.sp
, vp
);
6587 * The young end of fp->scopeChain may omit blocks if we haven't closed
6588 * over them, but if there are any closure blocks on fp->scopeChain, they'd
6589 * better be (clones of) ancestors of the block we're entering now;
6590 * anything else we should have popped off fp->scopeChain when we left its
6593 JSObject
*obj2
= ®s
.fp
->scopeChain();
6595 while ((clasp
= obj2
->getClass()) == &js_WithClass
)
6596 obj2
= obj2
->getParent();
6597 if (clasp
== &js_BlockClass
&&
6598 obj2
->getPrivate() == js_FloatingFrameIfGenerator(cx
, regs
.fp
)) {
6599 JSObject
*youngestProto
= obj2
->getProto();
6600 JS_ASSERT(youngestProto
->isStaticBlock());
6601 JSObject
*parent
= obj
;
6602 while ((parent
= parent
->getParent()) != youngestProto
)
6607 END_CASE(JSOP_ENTERBLOCK
)
6609 BEGIN_CASE(JSOP_LEAVEBLOCKEXPR
)
6610 BEGIN_CASE(JSOP_LEAVEBLOCK
)
6612 JSObject
*blockChain
;
6613 LOAD_OBJECT(UINT16_LEN
, blockChain
);
6615 JS_ASSERT(blockChain
->isStaticBlock());
6616 uintN blockDepth
= OBJ_BLOCK_DEPTH(cx
, blockChain
);
6617 JS_ASSERT(blockDepth
<= StackDepth(script
));
6620 * If we're about to leave the dynamic scope of a block that has been
6621 * cloned onto fp->scopeChain, clear its private data, move its locals from
6622 * the stack into the clone, and pop it off the chain.
6624 JSObject
&obj
= regs
.fp
->scopeChain();
6625 if (obj
.getProto() == blockChain
) {
6626 JS_ASSERT(obj
.isClonedBlock());
6627 if (!js_PutBlockObject(cx
, JS_TRUE
))
6631 /* Move the result of the expression to the new topmost stack slot. */
6632 Value
*vp
= NULL
; /* silence GCC warnings */
6633 if (op
== JSOP_LEAVEBLOCKEXPR
)
6635 regs
.sp
-= GET_UINT16(regs
.pc
);
6636 if (op
== JSOP_LEAVEBLOCKEXPR
) {
6637 JS_ASSERT(regs
.fp
->base() + blockDepth
== regs
.sp
- 1);
6640 JS_ASSERT(regs
.fp
->base() + blockDepth
== regs
.sp
);
6643 END_CASE(JSOP_LEAVEBLOCK
)
6645 #if JS_HAS_GENERATORS
6646 BEGIN_CASE(JSOP_GENERATOR
)
6648 JS_ASSERT(!cx
->isExceptionPending());
6649 regs
.pc
+= JSOP_GENERATOR_LENGTH
;
6650 JSObject
*obj
= js_NewGenerator(cx
);
6653 JS_ASSERT(!regs
.fp
->hasCallObj() && !regs
.fp
->hasArgsObj());
6654 regs
.fp
->setReturnValue(ObjectValue(*obj
));
6655 interpReturnOK
= true;
6656 if (entryFrame
!= regs
.fp
)
6661 BEGIN_CASE(JSOP_YIELD
)
6662 JS_ASSERT(!cx
->isExceptionPending());
6663 JS_ASSERT(regs
.fp
->isFunctionFrame() && !regs
.fp
->isEvalFrame());
6664 if (cx
->generatorFor(regs
.fp
)->state
== JSGEN_CLOSING
) {
6665 js_ReportValueError(cx
, JSMSG_BAD_GENERATOR_YIELD
,
6666 JSDVG_SEARCH_STACK
, argv
[-2], NULL
);
6669 regs
.fp
->setReturnValue(regs
.sp
[-1]);
6670 regs
.fp
->setYielding();
6671 regs
.pc
+= JSOP_YIELD_LENGTH
;
6672 interpReturnOK
= JS_TRUE
;
6675 BEGIN_CASE(JSOP_ARRAYPUSH
)
6677 uint32 slot
= GET_UINT16(regs
.pc
);
6678 JS_ASSERT(script
->nfixed
<= slot
);
6679 JS_ASSERT(slot
< script
->nslots
);
6680 JSObject
*obj
= ®s
.fp
->slots()[slot
].toObject();
6681 if (!js_ArrayCompPush(cx
, obj
, regs
.sp
[-1]))
6685 END_CASE(JSOP_ARRAYPUSH
)
6686 #endif /* JS_HAS_GENERATORS */
6688 #if JS_THREADED_INTERP
6690 L_JSOP_BACKPATCH_POP
:
6692 # if !JS_HAS_GENERATORS
6698 # if !JS_HAS_SHARP_VARS
6704 # if !JS_HAS_DESTRUCTURING
6705 L_JSOP_ENUMCONSTELEM
:
6708 # if !JS_HAS_XML_SUPPORT
6710 L_JSOP_STARTXMLEXPR
:
6738 #endif /* !JS_THREADED_INTERP */
6739 #if !JS_THREADED_INTERP
6744 JS_snprintf(numBuf
, sizeof numBuf
, "%d", op
);
6745 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6746 JSMSG_BAD_BYTECODE
, numBuf
);
6750 #if !JS_THREADED_INTERP
6753 #endif /* !JS_THREADED_INTERP */
6756 JS_ASSERT(cx
->regs
== ®s
);
6758 if (regs
.fp
->hasImacropc() && cx
->isExceptionPending()) {
6759 // Handle exceptions as if they came from the imacro-calling pc.
6760 regs
.pc
= regs
.fp
->imacropc();
6761 regs
.fp
->clearImacropc();
6765 JS_ASSERT(size_t((regs
.fp
->hasImacropc() ? regs
.fp
->imacropc() : regs
.pc
) - script
->code
) <
6770 * This abort could be weakened to permit tracing through exceptions that
6771 * are thrown and caught within a loop, with the co-operation of the tracer.
6772 * For now just bail on any sign of trouble.
6774 if (TRACE_RECORDER(cx
))
6775 AbortRecording(cx
, "error or exception while recording");
6776 # ifdef JS_METHODJIT
6777 if (TRACE_PROFILER(cx
))
6782 if (!cx
->isExceptionPending()) {
6783 /* This is an error, not a catchable exception, quit the frame ASAP. */
6784 interpReturnOK
= JS_FALSE
;
6786 JSThrowHook handler
;
6787 JSTryNote
*tn
, *tnlimit
;
6790 /* Restore atoms local in case we will resume. */
6791 atoms
= script
->atomMap
.vector
;
6793 /* Call debugger throw hook if set. */
6794 handler
= cx
->debugHooks
->throwHook
;
6797 switch (handler(cx
, script
, regs
.pc
, Jsvalify(&rval
),
6798 cx
->debugHooks
->throwHookData
)) {
6800 cx
->clearPendingException();
6803 cx
->clearPendingException();
6804 regs
.fp
->setReturnValue(rval
);
6805 interpReturnOK
= JS_TRUE
;
6808 cx
->setPendingException(rval
);
6809 case JSTRAP_CONTINUE
:
6812 CHECK_INTERRUPT_HANDLER();
6816 * Look for a try block in script that can catch this exception.
6818 if (!JSScript::isValidOffset(script
->trynotesOffset
))
6821 offset
= (uint32
)(regs
.pc
- script
->main
);
6822 tn
= script
->trynotes()->vector
;
6823 tnlimit
= tn
+ script
->trynotes()->length
;
6825 if (offset
- tn
->start
>= tn
->length
)
6829 * We have a note that covers the exception pc but we must check
6830 * whether the interpreter has already executed the corresponding
6831 * handler. This is possible when the executed bytecode
6832 * implements break or return from inside a for-in loop.
6834 * In this case the emitter generates additional [enditer] and
6835 * [gosub] opcodes to close all outstanding iterators and execute
6836 * the finally blocks. If such an [enditer] throws an exception,
6837 * its pc can still be inside several nested for-in loops and
6838 * try-finally statements even if we have already closed the
6839 * corresponding iterators and invoked the finally blocks.
6841 * To address this, we make [enditer] always decrease the stack
6842 * even when its implementation throws an exception. Thus already
6843 * executed [enditer] and [gosub] opcodes will have try notes
6844 * with the stack depth exceeding the current one and this
6845 * condition is what we use to filter them out.
6847 if (tn
->stackDepth
> regs
.sp
- regs
.fp
->base())
6851 * Set pc to the first bytecode after the the try note to point
6852 * to the beginning of catch or finally or to [enditer] closing
6855 regs
.pc
= (script
)->main
+ tn
->start
+ tn
->length
;
6857 JSBool ok
= js_UnwindScope(cx
, tn
->stackDepth
, JS_TRUE
);
6858 JS_ASSERT(regs
.sp
== regs
.fp
->base() + tn
->stackDepth
);
6861 * Restart the handler search with updated pc and stack depth
6862 * to properly notify the debugger.
6869 #if JS_HAS_GENERATORS
6870 /* Catch cannot intercept the closing of a generator. */
6871 if (JS_UNLIKELY(cx
->getPendingException().isMagic(JS_GENERATOR_CLOSING
)))
6876 * Don't clear exceptions to save cx->exception from GC
6877 * until it is pushed to the stack via [exception] in the
6885 * Push (true, exception) pair for finally to indicate that
6886 * [retsub] should rethrow the exception.
6889 PUSH_COPY(cx
->getPendingException());
6890 cx
->clearPendingException();
6895 /* This is similar to JSOP_ENDITER in the interpreter loop. */
6896 JS_ASSERT(js_GetOpcode(cx
, regs
.fp
->script(), regs
.pc
) == JSOP_ENDITER
);
6897 Value v
= cx
->getPendingException();
6898 cx
->clearPendingException();
6899 ok
= js_CloseIterator(cx
, ®s
.sp
[-1].toObject());
6903 cx
->setPendingException(v
);
6906 } while (++tn
!= tnlimit
);
6910 * Propagate the exception or error to the caller unless the exception
6911 * is an asynchronous return from a generator.
6913 interpReturnOK
= JS_FALSE
;
6914 #if JS_HAS_GENERATORS
6915 if (JS_UNLIKELY(cx
->isExceptionPending() &&
6916 cx
->getPendingException().isMagic(JS_GENERATOR_CLOSING
))) {
6917 cx
->clearPendingException();
6918 interpReturnOK
= JS_TRUE
;
6919 regs
.fp
->clearReturnValue();
6926 * Unwind the scope making sure that interpReturnOK stays false even when
6927 * js_UnwindScope returns true.
6929 * When a trap handler returns JSTRAP_RETURN, we jump here with
6930 * interpReturnOK set to true bypassing any finally blocks.
6932 interpReturnOK
&= js_UnwindScope(cx
, 0, interpReturnOK
|| cx
->isExceptionPending());
6933 JS_ASSERT(regs
.sp
== regs
.fp
->base());
6936 cx
->logPrevPc
= NULL
;
6939 if (entryFrame
!= regs
.fp
)
6943 interpReturnOK
= ScriptEpilogue(cx
, regs
.fp
, interpReturnOK
);
6944 regs
.fp
->setFinishedInInterpreter();
6947 * At this point we are inevitably leaving an interpreted function or a
6948 * top-level script, and returning to one of:
6949 * (a) an "out of line" call made through js_Invoke;
6950 * (b) a js_Execute activation;
6951 * (c) a generator (SendToGenerator, jsiter.c).
6953 * We must not be in an inline frame. The check above ensures that for the
6954 * error case and for a normal return, the code jumps directly to parent's
6957 JS_ASSERT(entryFrame
== regs
.fp
);
6960 JS_ASSERT_IF(interpReturnOK
&& interpMode
== JSINTERP_RECORD
, !TRACE_RECORDER(cx
));
6961 if (TRACE_RECORDER(cx
))
6962 AbortRecording(cx
, "recording out of Interpret");
6963 # ifdef JS_METHODJIT
6964 if (TRACE_PROFILER(cx
))
6969 JS_ASSERT_IF(!regs
.fp
->isGeneratorFrame(), !js_IsActiveWithOrBlock(cx
, ®s
.fp
->scopeChain(), 0));
6971 return interpReturnOK
;
6975 JSAutoByteString printable
;
6976 if (js_AtomToPrintableString(cx
, atomNotDefined
, &printable
))
6977 js_ReportIsNotDefined(cx
, printable
.ptr());
6982 * This path is used when it's guaranteed the method can be finished
6985 #if defined(JS_TRACER) && defined(JS_METHODJIT)
6986 leave_on_safe_point
:
6988 return interpReturnOK
;
6991 } /* namespace js */
6993 #endif /* !defined jsinvoke_cpp___ */