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/MethodJIT-inl.h"
80 #include "methodjit/Logging.h"
82 #include "jsatominlines.h"
83 #include "jscntxtinlines.h"
84 #include "jsinterpinlines.h"
85 #include "jsobjinlines.h"
87 #include "jspropertycacheinlines.h"
88 #include "jsscopeinlines.h"
89 #include "jsscriptinlines.h"
90 #include "jsstrinlines.h"
91 #include "jsopcodeinlines.h"
93 #if JS_HAS_XML_SUPPORT
97 #include "jsautooplen.h"
99 #if defined(JS_METHODJIT) && defined(JS_MONOIC)
100 #include "methodjit/MonoIC.h"
104 using namespace js::gc
;
106 /* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */
107 #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___
110 JSObject
*const JSStackFrame::sInvalidScopeChain
= (JSObject
*)0xbeef;
114 JSStackFrame::pc(JSContext
*cx
, JSStackFrame
*next
)
116 JS_ASSERT_IF(next
, next
->prev_
== this);
117 JS_ASSERT(cx
->containingSegment(this) != NULL
);
123 StackSegment
*segment
= cx
->getCurrentSegment();
124 regs
= segment
->getSuspendedRegs();
127 if (this == regs
->fp
)
131 next
= cx
->computeNextFrame(this);
133 if (next
->flags_
& JSFRAME_HAS_PREVPC
)
134 return next
->prevpc_
;
136 #if defined(JS_METHODJIT) && defined(JS_MONOIC)
137 js::mjit::JITScript
*jit
= script()->getJIT(isConstructing());
138 return jit
->nativeToPC(next
->ncode_
);
140 JS_NOT_REACHED("Unknown PC for frame");
146 js::GetScopeChain(JSContext
*cx
)
148 JSStackFrame
*fp
= js_GetTopStackFrame(cx
);
151 * There is no code active on this context. In place of an actual
152 * scope chain, use the context's global object, which is set in
153 * js_InitFunctionAndObjectClasses, and which represents the default
154 * scope chain for the embedding. See also js_FindClassObject.
156 * For embeddings that use the inner and outer object hooks, the inner
157 * object represents the ultimate global object, with the outer object
158 * acting as a stand-in.
160 JSObject
*obj
= cx
->globalObject
;
162 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_INACTIVE
);
166 OBJ_TO_INNER_OBJECT(cx
, obj
);
169 return GetScopeChain(cx
, fp
);
173 * This computes the blockChain by iterating through the bytecode
174 * of the current script until it reaches the PC. Each time it sees
175 * an ENTERBLOCK or LEAVEBLOCK instruction, it records the new
176 * blockChain. A faster variant of this function that doesn't
177 * require bytecode scanning appears below.
180 js::GetBlockChain(JSContext
*cx
, JSStackFrame
*fp
)
182 if (!fp
->isScriptFrame())
185 /* Assume that imacros don't affect blockChain */
186 jsbytecode
*target
= fp
->hasImacropc() ? fp
->imacropc() : fp
->pc(cx
);
188 JSScript
*script
= fp
->script();
189 jsbytecode
*start
= script
->code
;
190 JS_ASSERT(target
>= start
&& target
< start
+ script
->length
);
192 JSObject
*blockChain
= NULL
;
195 for (jsbytecode
*pc
= start
; pc
< target
; pc
+= oplen
) {
196 JSOp op
= js_GetOpcode(cx
, script
, pc
);
197 const JSCodeSpec
*cs
= &js_CodeSpec
[op
];
200 oplen
= js_GetVariableBytecodeLength(pc
);
202 if (op
== JSOP_INDEXBASE
)
203 indexBase
= GET_INDEXBASE(pc
);
204 else if (op
== JSOP_INDEXBASE1
|| op
== JSOP_INDEXBASE2
|| op
== JSOP_INDEXBASE3
)
205 indexBase
= (op
- JSOP_INDEXBASE1
+ 1) << 16;
206 else if (op
== JSOP_RESETBASE
|| op
== JSOP_RESETBASE0
)
208 else if (op
== JSOP_ENTERBLOCK
)
209 blockChain
= script
->getObject(indexBase
+ GET_INDEX(pc
));
210 else if (op
== JSOP_LEAVEBLOCK
|| op
== JSOP_LEAVEBLOCKEXPR
)
211 blockChain
= blockChain
->getParent();
212 else if (op
== JSOP_BLOCKCHAIN
)
213 blockChain
= script
->getObject(indexBase
+ GET_INDEX(pc
));
214 else if (op
== JSOP_NULLBLOCKCHAIN
)
222 * This function computes the current blockChain, but only in
223 * the special case where a BLOCKCHAIN or NULLBLOCKCHAIN
224 * instruction appears immediately after the current PC.
225 * We ensure this happens for a few important ops like DEFFUN.
226 * |oplen| is the length of opcode at the current PC.
229 js::GetBlockChainFast(JSContext
*cx
, JSStackFrame
*fp
, JSOp op
, size_t oplen
)
231 /* Assume that we're in a script frame. */
232 jsbytecode
*pc
= fp
->pc(cx
);
233 JS_ASSERT(js_GetOpcode(cx
, fp
->script(), pc
) == op
);
237 JS_ASSERT(js_GetOpcode(cx
, fp
->script(), pc
) == op
);
239 /* The fast paths assume no JSOP_RESETBASE/INDEXBASE noise. */
240 if (op
== JSOP_NULLBLOCKCHAIN
)
242 if (op
== JSOP_BLOCKCHAIN
)
243 return fp
->script()->getObject(GET_INDEX(pc
));
245 return GetBlockChain(cx
, fp
);
249 * We can't determine in advance which local variables can live on the stack and
250 * be freed when their dynamic scope ends, and which will be closed over and
251 * need to live in the heap. So we place variables on the stack initially, note
252 * when they are closed over, and copy those that are out to the heap when we
253 * leave their dynamic scope.
255 * The bytecode compiler produces a tree of block objects accompanying each
256 * JSScript representing those lexical blocks in the script that have let-bound
257 * variables associated with them. These block objects are never modified, and
258 * never become part of any function's scope chain. Their parent slots point to
259 * the innermost block that encloses them, or are NULL in the outermost blocks
260 * within a function or in eval or global code.
262 * When we are in the static scope of such a block, blockChain points to its
263 * compiler-allocated block object; otherwise, it is NULL.
265 * scopeChain is the current scope chain, including 'call' and 'block' objects
266 * for those function calls and lexical blocks whose static scope we are
267 * currently executing in, and 'with' objects for with statements; the chain is
268 * typically terminated by a global object. However, as an optimization, the
269 * young end of the chain omits block objects we have not yet cloned. To create
270 * a closure, we clone the missing blocks from blockChain (which is always
271 * current), place them at the head of scopeChain, and use that for the
272 * closure's scope chain. If we never close over a lexical block, we never
273 * place a mutable clone of it on scopeChain.
275 * This lazy cloning is implemented in GetScopeChain, which is also used in
276 * some other cases --- entering 'with' blocks, for example.
279 GetScopeChainFull(JSContext
*cx
, JSStackFrame
*fp
, JSObject
*blockChain
)
281 JSObject
*sharedBlock
= blockChain
;
285 * Don't force a call object for a lightweight function call, but do
286 * insist that there is a call object for a heavyweight function call.
288 JS_ASSERT_IF(fp
->isFunctionFrame() && fp
->fun()->isHeavyweight(),
290 return &fp
->scopeChain();
293 /* We don't handle cloning blocks on trace. */
297 * We have one or more lexical scopes to reflect into fp->scopeChain, so
298 * make sure there's a call object at the current head of the scope chain,
299 * if this frame is a call frame.
301 * Also, identify the innermost compiler-allocated block we needn't clone.
303 JSObject
*limitBlock
, *limitClone
;
304 if (fp
->isFunctionFrame() && !fp
->hasCallObj()) {
305 JS_ASSERT_IF(fp
->scopeChain().isClonedBlock(),
306 fp
->scopeChain().getPrivate() != js_FloatingFrameIfGenerator(cx
, fp
));
307 if (!js_GetCallObject(cx
, fp
))
310 /* We know we must clone everything on blockChain. */
311 limitBlock
= limitClone
= NULL
;
314 * scopeChain includes all blocks whose static scope we're within that
315 * have already been cloned. Find the innermost such block. Its
316 * prototype should appear on blockChain; we'll clone blockChain up
317 * to, but not including, that prototype.
319 limitClone
= &fp
->scopeChain();
320 while (limitClone
->getClass() == &js_WithClass
)
321 limitClone
= limitClone
->getParent();
322 JS_ASSERT(limitClone
);
325 * It may seem like we don't know enough about limitClone to be able
326 * to just grab its prototype as we do here, but it's actually okay.
328 * If limitClone is a block object belonging to this frame, then its
329 * prototype is the innermost entry in blockChain that we have already
330 * cloned, and is thus the place to stop when we clone below.
332 * Otherwise, there are no blocks for this frame on scopeChain, and we
333 * need to clone the whole blockChain. In this case, limitBlock can
334 * point to any object known not to be on blockChain, since we simply
335 * loop until we hit limitBlock or NULL. If limitClone is a block, it
336 * isn't a block from this function, since blocks can't be nested
337 * within themselves on scopeChain (recursion is dynamic nesting, not
338 * static nesting). If limitClone isn't a block, its prototype won't
339 * be a block either. So we can just grab limitClone's prototype here
340 * regardless of its type or which frame it belongs to.
342 limitBlock
= limitClone
->getProto();
344 /* If the innermost block has already been cloned, we are done. */
345 if (limitBlock
== sharedBlock
)
346 return &fp
->scopeChain();
350 * Special-case cloning the innermost block; this doesn't have enough in
351 * common with subsequent steps to include in the loop.
353 * js_CloneBlockObject leaves the clone's parent slot uninitialized. We
356 JSObject
*innermostNewChild
= js_CloneBlockObject(cx
, sharedBlock
, fp
);
357 if (!innermostNewChild
)
359 AutoObjectRooter
tvr(cx
, innermostNewChild
);
362 * Clone our way towards outer scopes until we reach the innermost
363 * enclosing function, or the innermost block we've already cloned.
365 JSObject
*newChild
= innermostNewChild
;
367 JS_ASSERT(newChild
->getProto() == sharedBlock
);
368 sharedBlock
= sharedBlock
->getParent();
370 /* Sometimes limitBlock will be NULL, so check that first. */
371 if (sharedBlock
== limitBlock
|| !sharedBlock
)
374 /* As in the call above, we don't know the real parent yet. */
375 JSObject
*clone
= js_CloneBlockObject(cx
, sharedBlock
, fp
);
379 newChild
->setParent(clone
);
382 newChild
->setParent(&fp
->scopeChain());
386 * If we found a limit block belonging to this frame, then we should have
387 * found it in blockChain.
389 JS_ASSERT_IF(limitBlock
&&
390 limitBlock
->isBlock() &&
391 limitClone
->getPrivate() == js_FloatingFrameIfGenerator(cx
, fp
),
394 /* Place our newly cloned blocks at the head of the scope chain. */
395 fp
->setScopeChainNoCallObj(*innermostNewChild
);
396 return innermostNewChild
;
400 js::GetScopeChain(JSContext
*cx
, JSStackFrame
*fp
)
402 return GetScopeChainFull(cx
, fp
, GetBlockChain(cx
, fp
));
406 js::GetScopeChainFast(JSContext
*cx
, JSStackFrame
*fp
, JSOp op
, size_t oplen
)
408 return GetScopeChainFull(cx
, fp
, GetBlockChainFast(cx
, fp
, op
, oplen
));
411 /* Some objects (e.g., With) delegate 'this' to another object. */
412 static inline JSObject
*
413 CallThisObjectHook(JSContext
*cx
, JSObject
*obj
, Value
*argv
)
415 JSObject
*thisp
= obj
->thisObject(cx
);
418 argv
[-1].setObject(*thisp
);
423 * ECMA requires "the global object", but in embeddings such as the browser,
424 * which have multiple top-level objects (windows, frames, etc. in the DOM),
425 * we prefer fun's parent. An example that causes this code to run:
428 * function f() { return this }
429 * function g() { return f }
435 * The alert should display "true".
437 JS_STATIC_INTERPRET
bool
438 ComputeGlobalThis(JSContext
*cx
, Value
*vp
)
440 JSObject
*thisp
= vp
[0].toObject().getGlobal()->thisObject(cx
);
443 vp
[1].setObject(*thisp
);
450 ReportIncompatibleMethod(JSContext
*cx
, Value
*vp
, Class
*clasp
)
452 Value
&thisv
= vp
[1];
455 if (thisv
.isObject()) {
456 JS_ASSERT(thisv
.toObject().getClass() != clasp
);
457 } else if (thisv
.isString()) {
458 JS_ASSERT(clasp
!= &js_StringClass
);
459 } else if (thisv
.isNumber()) {
460 JS_ASSERT(clasp
!= &js_NumberClass
);
461 } else if (thisv
.isBoolean()) {
462 JS_ASSERT(clasp
!= &js_BooleanClass
);
464 JS_ASSERT(thisv
.isUndefined() || thisv
.isNull());
468 if (JSFunction
*fun
= js_ValueToFunction(cx
, &vp
[0], 0)) {
469 const char *name
= thisv
.isObject()
470 ? thisv
.toObject().getClass()->name
479 : thisv
.isUndefined()
482 JSAutoByteString funNameBytes
;
483 if (const char *funName
= GetFunctionNameBytes(cx
, fun
, &funNameBytes
)) {
484 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_INCOMPATIBLE_PROTO
,
485 clasp
->name
, funName
, name
);
491 BoxThisForVp(JSContext
*cx
, Value
*vp
)
494 * Check for SynthesizeFrame poisoning and fast constructors which
495 * didn't check their vp properly.
497 JS_ASSERT(!vp
[1].isMagic());
499 JSFunction
*fun
= vp
[0].toObject().isFunction() ? vp
[0].toObject().getFunctionPrivate() : NULL
;
500 JS_ASSERT_IF(fun
&& fun
->isInterpreted(), !fun
->inStrictMode());
503 if (vp
[1].isNullOrUndefined())
504 return ComputeGlobalThis(cx
, vp
);
506 if (!vp
[1].isObject())
507 return !!js_PrimitiveToObject(cx
, &vp
[1]);
514 #if JS_HAS_NO_SUCH_METHOD
516 const uint32 JSSLOT_FOUND_FUNCTION
= 0;
517 const uint32 JSSLOT_SAVED_ID
= 1;
519 Class js_NoSuchMethodClass
= {
521 JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS
,
522 PropertyStub
, /* addProperty */
523 PropertyStub
, /* delProperty */
524 PropertyStub
, /* getProperty */
525 StrictPropertyStub
, /* setProperty */
532 * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of
533 * the base object, we search for the __noSuchMethod__ method in the base.
534 * If it exists, we store the method and the property's id into an object of
535 * NoSuchMethod class and store this object into the callee's stack slot.
536 * Later, js_Invoke will recognise such an object and transfer control to
537 * NoSuchMethod that invokes the method like:
539 * this.__noSuchMethod__(id, args)
541 * where id is the name of the method that this invocation attempted to
542 * call by name, and args is an Array containing this invocation's actual
546 js_OnUnknownMethod(JSContext
*cx
, Value
*vp
)
548 JS_ASSERT(!vp
[1].isPrimitive());
550 JSObject
*obj
= &vp
[1].toObject();
551 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.noSuchMethodAtom
);
552 AutoValueRooter
tvr(cx
);
553 if (!js_GetMethod(cx
, obj
, id
, JSGET_NO_METHOD_BARRIER
, tvr
.addr()))
555 if (tvr
.value().isPrimitive()) {
558 #if JS_HAS_XML_SUPPORT
559 /* Extract the function name from function::name qname. */
560 if (vp
[0].isObject()) {
561 obj
= &vp
[0].toObject();
562 if (!js_IsFunctionQName(cx
, obj
, &id
))
564 if (!JSID_IS_VOID(id
))
565 vp
[0] = IdToValue(id
);
568 obj
= js_NewGCObject(cx
, FINALIZE_OBJECT2
);
573 * Null map to cause prompt and safe crash if this object were to
574 * escape due to a bug. This will make the object appear to be a
575 * stillborn instance that needs no finalization, which is sound:
576 * NoSuchMethod helper objects own no manually allocated resources.
579 obj
->init(cx
, &js_NoSuchMethodClass
, NULL
, NULL
, NULL
, false);
580 obj
->setSlot(JSSLOT_FOUND_FUNCTION
, tvr
.value());
581 obj
->setSlot(JSSLOT_SAVED_ID
, vp
[0]);
582 vp
[0].setObject(*obj
);
587 static JS_REQUIRES_STACK JSBool
588 NoSuchMethod(JSContext
*cx
, uintN argc
, Value
*vp
, uint32 flags
)
590 InvokeArgsGuard args
;
591 if (!cx
->stack().pushInvokeArgs(cx
, 2, &args
))
594 JS_ASSERT(vp
[0].isObject());
595 JS_ASSERT(vp
[1].isObject());
596 JSObject
*obj
= &vp
[0].toObject();
597 JS_ASSERT(obj
->getClass() == &js_NoSuchMethodClass
);
599 args
.callee() = obj
->getSlot(JSSLOT_FOUND_FUNCTION
);
600 args
.thisv() = vp
[1];
601 args
[0] = obj
->getSlot(JSSLOT_SAVED_ID
);
602 JSObject
*argsobj
= NewDenseCopiedArray(cx
, argc
, vp
+ 2);
605 args
[1].setObject(*argsobj
);
606 JSBool ok
= (flags
& JSINVOKE_CONSTRUCT
)
607 ? InvokeConstructor(cx
, args
)
608 : Invoke(cx
, args
, flags
);
613 #endif /* JS_HAS_NO_SUCH_METHOD */
617 JS_REQUIRES_STACK
bool
618 RunScript(JSContext
*cx
, JSScript
*script
, JSStackFrame
*fp
)
622 /* FIXME: Once bug 470510 is fixed, make this an assert. */
623 if (script
->compileAndGo
) {
624 int32 flags
= fp
->scopeChain().getGlobal()->getReservedSlot(JSRESERVED_GLOBAL_FLAGS
).toInt32();
625 if (flags
& JSGLOBAL_FLAGS_CLEARED
) {
626 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_CLEARED_SCOPE
);
631 #ifdef JS_METHODJIT_SPEW
635 AutoInterpPreparer
prepareInterp(cx
, script
);
637 JS_ASSERT(fp
== cx
->fp());
638 JS_ASSERT(fp
->script() == script
);
641 mjit::CompileStatus status
=
642 mjit::CanMethodJIT(cx
, script
, fp
, mjit::CompileRequest_Interpreter
);
643 if (status
== mjit::Compile_Error
)
646 if (status
== mjit::Compile_Okay
)
647 return mjit::JaegerShot(cx
);
650 return Interpret(cx
, fp
);
654 * Find a function reference and its 'this' value implicit first parameter
655 * under argc arguments on cx's stack, and call the function. Push missing
656 * required arguments, allocate declared local variables, and pop everything
657 * when done. Then push the return value.
659 JS_REQUIRES_STACK
bool
660 Invoke(JSContext
*cx
, const CallArgs
&argsRef
, uint32 flags
)
662 /* N.B. Must be kept in sync with InvokeSessionGuard::start/invoke */
664 CallArgs args
= argsRef
;
665 JS_ASSERT(args
.argc() <= JS_ARGS_LENGTH_MAX
);
667 if (args
.callee().isPrimitive()) {
668 js_ReportIsNotFunction(cx
, &args
.callee(), flags
& JSINVOKE_FUNFLAGS
);
672 JSObject
&callee
= args
.callee().toObject();
673 Class
*clasp
= callee
.getClass();
675 /* Invoke non-functions. */
676 if (JS_UNLIKELY(clasp
!= &js_FunctionClass
)) {
677 #if JS_HAS_NO_SUCH_METHOD
678 if (JS_UNLIKELY(clasp
== &js_NoSuchMethodClass
))
679 return NoSuchMethod(cx
, args
.argc(), args
.base(), 0);
681 JS_ASSERT_IF(flags
& JSINVOKE_CONSTRUCT
, !clasp
->construct
);
683 js_ReportIsNotFunction(cx
, &args
.callee(), flags
);
686 return CallJSNative(cx
, clasp
->call
, args
.argc(), args
.base());
689 /* Invoke native functions. */
690 JSFunction
*fun
= callee
.getFunctionPrivate();
691 JS_ASSERT_IF(flags
& JSINVOKE_CONSTRUCT
, !fun
->isConstructor());
693 return CallJSNative(cx
, fun
->u
.n
.native
, args
.argc(), args
.base());
695 /* Handle the empty-script special case. */
696 JSScript
*script
= fun
->script();
697 if (JS_UNLIKELY(script
->isEmpty())) {
698 if (flags
& JSINVOKE_CONSTRUCT
) {
699 JSObject
*obj
= js_CreateThisForFunction(cx
, &callee
);
702 args
.rval().setObject(*obj
);
704 args
.rval().setUndefined();
709 /* Get pointer to new frame/slots, prepare arguments. */
710 InvokeFrameGuard frame
;
711 if (JS_UNLIKELY(!cx
->stack().getInvokeFrame(cx
, args
, fun
, script
, &flags
, &frame
)))
714 /* Initialize frame, locals. */
715 JSStackFrame
*fp
= frame
.fp();
716 fp
->initCallFrame(cx
, callee
, fun
, args
.argc(), flags
);
717 SetValueRangeToUndefined(fp
->slots(), script
->nfixed
);
719 /* Officially push fp. frame's destructor pops. */
720 cx
->stack().pushInvokeFrame(cx
, args
, &frame
);
722 /* Now that the new frame is rooted, maybe create a call object. */
723 if (fun
->isHeavyweight() && !js_GetCallObject(cx
, fp
))
726 /* Run function until JSOP_STOP, JSOP_RETURN or error. */
729 AutoPreserveEnumerators
preserve(cx
);
730 ok
= RunScript(cx
, script
, fp
);
733 args
.rval() = fp
->returnValue();
734 JS_ASSERT_IF(ok
&& (flags
& JSINVOKE_CONSTRUCT
), !args
.rval().isPrimitive());
740 InvokeSessionGuard::start(JSContext
*cx
, const Value
&calleev
, const Value
&thisv
, uintN argc
)
743 if (TRACE_RECORDER(cx
))
744 AbortRecording(cx
, "attempt to reenter VM while recording");
748 /* Always push arguments, regardless of optimized/normal invoke. */
749 StackSpace
&stack
= cx
->stack();
750 if (!stack
.pushInvokeArgs(cx
, argc
, &args_
))
753 /* Callees may clobber 'this' or 'callee'. */
754 savedCallee_
= args_
.callee() = calleev
;
755 savedThis_
= args_
.thisv() = thisv
;
758 /* Hoist dynamic checks from scripted Invoke. */
759 if (!calleev
.isObject())
761 JSObject
&callee
= calleev
.toObject();
762 if (callee
.getClass() != &js_FunctionClass
)
764 JSFunction
*fun
= callee
.getFunctionPrivate();
767 script_
= fun
->script();
768 if (fun
->isHeavyweight() || script_
->isEmpty() || cx
->compartment
->debugMode
)
771 /* Push the stack frame once for the session. */
773 if (!stack
.getInvokeFrame(cx
, args_
, fun
, script_
, &flags
, &frame_
))
775 JSStackFrame
*fp
= frame_
.fp();
776 fp
->initCallFrame(cx
, calleev
.toObject(), fun
, argc
, flags
);
777 stack
.pushInvokeFrame(cx
, args_
, &frame_
);
780 /* Hoist dynamic checks from RunScript. */
781 mjit::CompileStatus status
= mjit::CanMethodJIT(cx
, script_
, fp
, mjit::CompileRequest_JIT
);
782 if (status
== mjit::Compile_Error
)
784 if (status
!= mjit::Compile_Okay
)
786 /* Cannot also cache the raw code pointer; it can change. */
788 /* Hoist dynamic checks from CheckStackAndEnterMethodJIT. */
789 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
790 stackLimit_
= stack
.getStackLimit(cx
);
794 stop_
= script_
->code
+ script_
->length
- 1;
795 JS_ASSERT(*stop_
== JSOP_STOP
);
798 /* Cached to avoid canonicalActualArg in InvokeSessionGuard::operator[]. */
799 nformals_
= fp
->numFormalArgs();
800 formals_
= fp
->formalArgs();
801 actuals_
= args_
.argv();
802 JS_ASSERT(actuals_
== fp
->actualArgs());
807 * Use the normal invoke path.
809 * The callee slot gets overwritten during an unoptimized Invoke, so we
810 * cache it here and restore it before every Invoke call. The 'this' value
811 * does not get overwritten, so we can fill it here once.
815 formals_
= actuals_
= args_
.argv();
816 nformals_
= (unsigned)-1;
821 ExternalInvoke(JSContext
*cx
, const Value
&thisv
, const Value
&fval
,
822 uintN argc
, Value
*argv
, Value
*rval
)
826 InvokeArgsGuard args
;
827 if (!cx
->stack().pushInvokeArgs(cx
, argc
, &args
))
830 args
.callee() = fval
;
831 args
.thisv() = thisv
;
832 memcpy(args
.argv(), argv
, argc
* sizeof(Value
));
834 if (args
.thisv().isObject()) {
836 * We must call the thisObject hook in case we are not called from the
837 * interpreter, where a prior bytecode has computed an appropriate
840 JSObject
*thisp
= args
.thisv().toObject().thisObject(cx
);
843 args
.thisv().setObject(*thisp
);
846 if (!Invoke(cx
, args
, 0))
854 ExternalInvokeConstructor(JSContext
*cx
, const Value
&fval
, uintN argc
, Value
*argv
,
859 InvokeArgsGuard args
;
860 if (!cx
->stack().pushInvokeArgs(cx
, argc
, &args
))
863 args
.callee() = fval
;
864 args
.thisv().setMagic(JS_THIS_POISON
);
865 memcpy(args
.argv(), argv
, argc
* sizeof(Value
));
867 if (!InvokeConstructor(cx
, args
))
875 ExternalGetOrSet(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
&fval
,
876 JSAccessMode mode
, uintN argc
, Value
*argv
, Value
*rval
)
881 * ExternalInvoke could result in another try to get or set the same id
882 * again, see bug 355497.
884 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
886 return ExternalInvoke(cx
, ObjectValue(*obj
), fval
, argc
, argv
, rval
);
890 Execute(JSContext
*cx
, JSObject
*chain
, JSScript
*script
,
891 JSStackFrame
*prev
, uintN flags
, Value
*result
)
894 JS_ASSERT_IF(prev
, !prev
->isDummyFrame());
896 if (script
->isEmpty()) {
898 result
->setUndefined();
905 * Get a pointer to new frame/slots. This memory is not "claimed", so the
906 * code before pushExecuteFrame must not reenter the interpreter.
908 ExecuteFrameGuard frame
;
909 if (!cx
->stack().getExecuteFrame(cx
, script
, &frame
))
912 /* Initialize fixed slots (GVAR ops expect NULL). */
913 SetValueRangeToNull(frame
.fp()->slots(), script
->nfixed
);
915 /* Initialize frame and locals. */
916 JSObject
*initialVarObj
;
918 JS_ASSERT(chain
== &prev
->scopeChain());
919 frame
.fp()->initEvalFrame(cx
, script
, prev
, flags
);
922 * We want to call |prev->varobj()|, but this requires knowing the
923 * CallStackSegment of |prev|. If |prev == cx->fp()|, the callstack is
924 * simply the context's active callstack, so we can use
925 * |prev->varobj(cx)|. When |prev != cx->fp()|, we need to do a slow
926 * linear search. Luckily, this only happens with EvaluateInFrame.
928 initialVarObj
= (prev
== cx
->maybefp())
930 : &prev
->varobj(cx
->containingSegment(prev
));
932 /* The scope chain could be anything, so innerize just in case. */
933 JSObject
*innerizedChain
= chain
;
934 OBJ_TO_INNER_OBJECT(cx
, innerizedChain
);
938 /* If we were handed a non-native object, complain bitterly. */
939 if (!innerizedChain
->isNative()) {
940 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
941 JSMSG_NON_NATIVE_SCOPE
);
945 /* Initialize frame. */
946 frame
.fp()->initGlobalFrame(script
, *innerizedChain
, flags
);
948 /* Compute 'this'. */
949 JSObject
*thisp
= chain
->thisObject(cx
);
952 frame
.fp()->globalThis().setObject(*thisp
);
954 initialVarObj
= cx
->hasRunOption(JSOPTION_VAROBJFIX
) ? chain
->getGlobal() : chain
;
958 * Strict mode eval code receives its own, fresh lexical environment; thus
959 * strict mode eval can't mutate its calling frame's binding set.
961 if ((flags
& JSFRAME_EVAL
) && script
->strictModeCode
) {
962 AutoScriptRooter
root(cx
, script
);
963 initialVarObj
= NewCallObject(cx
, &script
->bindings
, *initialVarObj
, NULL
);
966 initialVarObj
->setPrivate(frame
.fp());
968 /* Clear the Call object propagated from the previous frame, if any. */
969 if (frame
.fp()->hasCallObj())
970 frame
.fp()->clearCallObj();
971 frame
.fp()->setScopeChainAndCallObj(*initialVarObj
);
973 JS_ASSERT(!initialVarObj
->getOps()->defineProperty
);
975 #if JS_HAS_SHARP_VARS
976 JS_STATIC_ASSERT(SHARP_NSLOTS
== 2);
977 if (script
->hasSharps
) {
978 JS_ASSERT(script
->nfixed
>= SHARP_NSLOTS
);
979 Value
*sharps
= &frame
.fp()->slots()[script
->nfixed
- SHARP_NSLOTS
];
980 if (prev
&& prev
->script()->hasSharps
) {
981 JS_ASSERT(prev
->numFixed() >= SHARP_NSLOTS
);
982 int base
= (prev
->isFunctionFrame() && !prev
->isEvalOrDebuggerFrame())
983 ? prev
->fun()->script()->bindings
.sharpSlotBase(cx
)
984 : prev
->numFixed() - SHARP_NSLOTS
;
987 sharps
[0] = prev
->slots()[base
];
988 sharps
[1] = prev
->slots()[base
+ 1];
990 sharps
[0].setUndefined();
991 sharps
[1].setUndefined();
996 /* Officially push |fp|. |frame|'s destructor pops. */
997 cx
->stack().pushExecuteFrame(cx
, initialVarObj
, &frame
);
999 /* Now that the frame has been pushed, we can call the thisObject hook. */
1001 JSObject
*thisp
= chain
->thisObject(cx
);
1004 frame
.fp()->globalThis().setObject(*thisp
);
1007 Probes::startExecution(cx
, script
);
1009 /* Run script until JSOP_STOP or error. */
1010 AutoPreserveEnumerators
preserve(cx
);
1011 JSBool ok
= RunScript(cx
, script
, frame
.fp());
1013 *result
= frame
.fp()->returnValue();
1015 Probes::stopExecution(cx
, script
);
1021 CheckRedeclaration(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN attrs
)
1027 const char *type
, *name
;
1029 if (!obj
->lookupProperty(cx
, id
, &obj2
, &prop
))
1033 if (obj2
->isNative()) {
1034 oldAttrs
= ((Shape
*) prop
)->attributes();
1036 if (!obj2
->getAttributes(cx
, id
, &oldAttrs
))
1040 /* We allow redeclaring some non-readonly properties. */
1041 if (((oldAttrs
| attrs
) & JSPROP_READONLY
) == 0) {
1042 /* Allow redeclaration of variables and functions. */
1043 if (!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)))
1047 * Allow adding a getter only if a property already has a setter
1048 * but no getter and similarly for adding a setter. That is, we
1049 * allow only the following transitions:
1051 * no-property --> getter --> getter + setter
1052 * no-property --> setter --> getter + setter
1054 if ((~(oldAttrs
^ attrs
) & (JSPROP_GETTER
| JSPROP_SETTER
)) == 0)
1058 * Allow redeclaration of an impermanent property (in which case
1059 * anyone could delete it and redefine it, willy-nilly).
1061 if (!(oldAttrs
& JSPROP_PERMANENT
))
1065 isFunction
= (oldAttrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) != 0;
1068 if (!obj
->getProperty(cx
, id
, &value
))
1070 isFunction
= IsFunctionObject(value
);
1073 type
= (oldAttrs
& attrs
& JSPROP_GETTER
)
1075 : (oldAttrs
& attrs
& JSPROP_SETTER
)
1077 : (oldAttrs
& JSPROP_READONLY
)
1082 JSAutoByteString bytes
;
1083 name
= js_ValueToPrintable(cx
, IdToValue(id
), &bytes
);
1086 JS_ALWAYS_FALSE(JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
, js_GetErrorMessage
, NULL
,
1087 JSMSG_REDECLARED_VAR
, type
, name
));
1092 HasInstance(JSContext
*cx
, JSObject
*obj
, const Value
*v
, JSBool
*bp
)
1094 Class
*clasp
= obj
->getClass();
1095 if (clasp
->hasInstance
)
1096 return clasp
->hasInstance(cx
, obj
, v
, bp
);
1097 js_ReportValueError(cx
, JSMSG_BAD_INSTANCEOF_RHS
,
1098 JSDVG_SEARCH_STACK
, ObjectValue(*obj
), NULL
);
1103 StrictlyEqual(JSContext
*cx
, const Value
&lref
, const Value
&rref
, JSBool
*equal
)
1105 Value lval
= lref
, rval
= rref
;
1106 if (SameType(lval
, rval
)) {
1107 if (lval
.isString())
1108 return EqualStrings(cx
, lval
.toString(), rval
.toString(), equal
);
1109 if (lval
.isDouble()) {
1110 *equal
= JSDOUBLE_COMPARE(lval
.toDouble(), ==, rval
.toDouble(), JS_FALSE
);
1113 if (lval
.isObject()) {
1114 *equal
= &lval
.toObject() == &rval
.toObject();
1117 if (lval
.isUndefined()) {
1121 *equal
= lval
.payloadAsRawUint32() == rval
.payloadAsRawUint32();
1125 if (lval
.isDouble() && rval
.isInt32()) {
1126 double ld
= lval
.toDouble();
1127 double rd
= rval
.toInt32();
1128 *equal
= JSDOUBLE_COMPARE(ld
, ==, rd
, JS_FALSE
);
1131 if (lval
.isInt32() && rval
.isDouble()) {
1132 double ld
= lval
.toInt32();
1133 double rd
= rval
.toDouble();
1134 *equal
= JSDOUBLE_COMPARE(ld
, ==, rd
, JS_FALSE
);
1143 IsNegativeZero(const Value
&v
)
1145 return v
.isDouble() && JSDOUBLE_IS_NEGZERO(v
.toDouble());
1149 IsNaN(const Value
&v
)
1151 return v
.isDouble() && JSDOUBLE_IS_NaN(v
.toDouble());
1155 SameValue(JSContext
*cx
, const Value
&v1
, const Value
&v2
, JSBool
*same
)
1157 if (IsNegativeZero(v1
)) {
1158 *same
= IsNegativeZero(v2
);
1161 if (IsNegativeZero(v2
)) {
1165 if (IsNaN(v1
) && IsNaN(v2
)) {
1169 return StrictlyEqual(cx
, v1
, v2
, same
);
1173 TypeOfValue(JSContext
*cx
, const Value
&vref
)
1177 return JSTYPE_NUMBER
;
1179 return JSTYPE_STRING
;
1181 return JSTYPE_OBJECT
;
1182 if (v
.isUndefined())
1185 return v
.toObject().typeOf(cx
);
1186 JS_ASSERT(v
.isBoolean());
1187 return JSTYPE_BOOLEAN
;
1191 InstanceOfSlow(JSContext
*cx
, JSObject
*obj
, Class
*clasp
, Value
*argv
)
1193 JS_ASSERT(!obj
|| obj
->getClass() != clasp
);
1195 JSFunction
*fun
= js_ValueToFunction(cx
, &argv
[-2], 0);
1197 JSAutoByteString funNameBytes
;
1198 if (const char *funName
= GetFunctionNameBytes(cx
, fun
, &funNameBytes
)) {
1199 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_INCOMPATIBLE_PROTO
,
1200 clasp
->name
, funName
,
1201 obj
? obj
->getClass()->name
: js_null_str
);
1208 JS_REQUIRES_STACK
bool
1209 InvokeConstructor(JSContext
*cx
, const CallArgs
&argsRef
)
1211 JS_ASSERT(!js_FunctionClass
.construct
);
1212 CallArgs args
= argsRef
;
1215 if (args
.callee().isPrimitive() || !(callee
= &args
.callee().toObject())->getParent()) {
1216 js_ReportIsNotFunction(cx
, &args
.callee(), JSV2F_CONSTRUCT
);
1220 /* Handle the fast-constructors cases before falling into the general case . */
1221 Class
*clasp
= callee
->getClass();
1222 JSFunction
*fun
= NULL
;
1223 if (clasp
== &js_FunctionClass
) {
1224 fun
= callee
->getFunctionPrivate();
1225 if (fun
->isConstructor()) {
1226 args
.thisv().setMagicWithObjectOrNullPayload(NULL
);
1227 return CallJSNativeConstructor(cx
, fun
->u
.n
.native
, args
.argc(), args
.base());
1229 } else if (clasp
->construct
) {
1230 args
.thisv().setMagicWithObjectOrNullPayload(NULL
);
1231 return CallJSNativeConstructor(cx
, clasp
->construct
, args
.argc(), args
.base());
1234 /* Scripts create their own |this| in JSOP_BEGIN */
1235 if (!fun
|| !fun
->isInterpreted()) {
1236 JSObject
*obj
= js_CreateThis(cx
, callee
);
1239 args
.thisv().setObject(*obj
);
1242 if (!Invoke(cx
, args
, JSINVOKE_CONSTRUCT
))
1245 if (args
.rval().isPrimitive()) {
1246 if (clasp
!= &js_FunctionClass
) {
1247 /* native [[Construct]] returning primitive is error */
1248 JSAutoByteString bytes
;
1249 if (js_ValueToPrintable(cx
, args
.rval(), &bytes
)) {
1250 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1251 JSMSG_BAD_NEW_RESULT
, bytes
.ptr());
1256 /* The interpreter fixes rval for us. */
1257 JS_ASSERT(!fun
->isInterpreted());
1259 args
.rval() = args
.thisv();
1262 JS_RUNTIME_METER(cx
->runtime
, constructs
);
1267 InvokeConstructorWithGivenThis(JSContext
*cx
, JSObject
*thisobj
, const Value
&fval
,
1268 uintN argc
, Value
*argv
, Value
*rval
)
1272 InvokeArgsGuard args
;
1273 if (!cx
->stack().pushInvokeArgs(cx
, argc
, &args
))
1276 args
.callee() = fval
;
1277 /* Initialize args.thisv on all paths below. */
1278 memcpy(args
.argv(), argv
, argc
* sizeof(Value
));
1280 /* Handle the fast-constructor cases before calling the general case. */
1281 JSObject
&callee
= fval
.toObject();
1282 Class
*clasp
= callee
.getClass();
1285 if (clasp
== &js_FunctionClass
&& (fun
= callee
.getFunctionPrivate())->isConstructor()) {
1286 args
.thisv().setMagicWithObjectOrNullPayload(thisobj
);
1287 ok
= CallJSNativeConstructor(cx
, fun
->u
.n
.native
, args
.argc(), args
.base());
1288 } else if (clasp
->construct
) {
1289 args
.thisv().setMagicWithObjectOrNullPayload(thisobj
);
1290 ok
= CallJSNativeConstructor(cx
, clasp
->construct
, args
.argc(), args
.base());
1292 args
.thisv().setObjectOrNull(thisobj
);
1293 ok
= Invoke(cx
, args
, JSINVOKE_CONSTRUCT
);
1296 *rval
= args
.rval();
1301 DirectEval(JSContext
*cx
, JSFunction
*evalfun
, uint32 argc
, Value
*vp
)
1303 JS_ASSERT(vp
== cx
->regs
->sp
- argc
- 2);
1304 JS_ASSERT(vp
[0].isObject());
1305 JS_ASSERT(vp
[0].toObject().isFunction());
1306 JS_ASSERT(vp
[0].toObject().getFunctionPrivate() == evalfun
);
1307 JS_ASSERT(IsBuiltinEvalFunction(evalfun
));
1309 JSStackFrame
*caller
= cx
->fp();
1310 JS_ASSERT(caller
->isScriptFrame());
1311 AutoFunctionCallProbe
callProbe(cx
, evalfun
, caller
->script());
1313 JSObject
*scopeChain
=
1314 GetScopeChainFast(cx
, caller
, JSOP_EVAL
, JSOP_EVAL_LENGTH
+ JSOP_LINENO_LENGTH
);
1315 if (!scopeChain
|| !EvalKernel(cx
, argc
, vp
, DIRECT_EVAL
, caller
, scopeChain
))
1317 cx
->regs
->sp
= vp
+ 1;
1322 ValueToId(JSContext
*cx
, const Value
&v
, jsid
*idp
)
1325 if (ValueFitsInInt32(v
, &i
) && INT_FITS_IN_JSID(i
)) {
1326 *idp
= INT_TO_JSID(i
);
1330 #if JS_HAS_XML_SUPPORT
1332 JSObject
*obj
= &v
.toObject();
1333 if (obj
->isXMLId()) {
1334 *idp
= OBJECT_TO_JSID(obj
);
1340 return js_ValueToStringId(cx
, v
, idp
);
1343 } /* namespace js */
1346 * Enter the new with scope using an object at sp[-1] and associate the depth
1347 * of the with block with sp + stackIndex.
1349 JS_STATIC_INTERPRET JS_REQUIRES_STACK JSBool
1350 js_EnterWith(JSContext
*cx
, jsint stackIndex
, JSOp op
, size_t oplen
)
1352 JSStackFrame
*fp
= cx
->fp();
1353 Value
*sp
= cx
->regs
->sp
;
1354 JS_ASSERT(stackIndex
< 0);
1355 JS_ASSERT(fp
->base() <= sp
+ stackIndex
);
1358 if (sp
[-1].isObject()) {
1359 obj
= &sp
[-1].toObject();
1361 obj
= js_ValueToNonNullObject(cx
, sp
[-1]);
1364 sp
[-1].setObject(*obj
);
1367 JSObject
*parent
= GetScopeChainFast(cx
, fp
, op
, oplen
);
1371 OBJ_TO_INNER_OBJECT(cx
, obj
);
1375 JSObject
*withobj
= js_NewWithObject(cx
, obj
, parent
,
1376 sp
+ stackIndex
- fp
->base());
1380 fp
->setScopeChainNoCallObj(*withobj
);
1384 JS_STATIC_INTERPRET JS_REQUIRES_STACK
void
1385 js_LeaveWith(JSContext
*cx
)
1389 withobj
= &cx
->fp()->scopeChain();
1390 JS_ASSERT(withobj
->getClass() == &js_WithClass
);
1391 JS_ASSERT(withobj
->getPrivate() == js_FloatingFrameIfGenerator(cx
, cx
->fp()));
1392 JS_ASSERT(OBJ_BLOCK_DEPTH(cx
, withobj
) >= 0);
1393 withobj
->setPrivate(NULL
);
1394 cx
->fp()->setScopeChainNoCallObj(*withobj
->getParent());
1397 JS_REQUIRES_STACK Class
*
1398 js_IsActiveWithOrBlock(JSContext
*cx
, JSObject
*obj
, int stackDepth
)
1402 clasp
= obj
->getClass();
1403 if ((clasp
== &js_WithClass
|| clasp
== &js_BlockClass
) &&
1404 obj
->getPrivate() == js_FloatingFrameIfGenerator(cx
, cx
->fp()) &&
1405 OBJ_BLOCK_DEPTH(cx
, obj
) >= stackDepth
) {
1412 * Unwind block and scope chains to match the given depth. The function sets
1413 * fp->sp on return to stackDepth.
1415 JS_REQUIRES_STACK JSBool
1416 js_UnwindScope(JSContext
*cx
, jsint stackDepth
, JSBool normalUnwind
)
1420 JS_ASSERT(stackDepth
>= 0);
1421 JS_ASSERT(cx
->fp()->base() + stackDepth
<= cx
->regs
->sp
);
1423 JSStackFrame
*fp
= cx
->fp();
1425 clasp
= js_IsActiveWithOrBlock(cx
, &fp
->scopeChain(), stackDepth
);
1428 if (clasp
== &js_BlockClass
) {
1429 /* Don't fail until after we've updated all stacks. */
1430 normalUnwind
&= js_PutBlockObject(cx
, normalUnwind
);
1436 cx
->regs
->sp
= fp
->base() + stackDepth
;
1437 return normalUnwind
;
1440 JS_STATIC_INTERPRET JSBool
1441 js_DoIncDec(JSContext
*cx
, const JSCodeSpec
*cs
, Value
*vp
, Value
*vp2
)
1443 if (cs
->format
& JOF_POST
) {
1445 if (!ValueToNumber(cx
, *vp
, &d
))
1448 (cs
->format
& JOF_INC
) ? ++d
: --d
;
1454 if (!ValueToNumber(cx
, *vp
, &d
))
1456 (cs
->format
& JOF_INC
) ? ++d
: --d
;
1463 js::GetUpvar(JSContext
*cx
, uintN closureLevel
, UpvarCookie cookie
)
1465 JS_ASSERT(closureLevel
>= cookie
.level() && cookie
.level() > 0);
1466 const uintN targetLevel
= closureLevel
- cookie
.level();
1467 JS_ASSERT(targetLevel
< UpvarCookie::UPVAR_LEVEL_LIMIT
);
1469 JSStackFrame
*fp
= cx
->findFrameAtLevel(targetLevel
);
1470 uintN slot
= cookie
.slot();
1473 if (!fp
->isFunctionFrame() || fp
->isEvalFrame()) {
1474 vp
= fp
->slots() + fp
->numFixed();
1475 } else if (slot
< fp
->numFormalArgs()) {
1476 vp
= fp
->formalArgs();
1477 } else if (slot
== UpvarCookie::CALLEE_SLOT
) {
1478 vp
= &fp
->calleeValue();
1481 slot
-= fp
->numFormalArgs();
1482 JS_ASSERT(slot
< fp
->numSlots());
1491 JS_STATIC_INTERPRET JS_REQUIRES_STACK
void
1492 js_LogOpcode(JSContext
*cx
)
1497 intN ndefs
, n
, nuses
;
1500 logfp
= (FILE *) cx
->logfp
;
1506 * Operations in prologues don't produce interesting values, and
1507 * js_DecompileValueGenerator isn't set up to handle them anyway.
1509 if (cx
->logPrevPc
&& regs
->pc
>= fp
->script()->main
) {
1510 JSOp logPrevOp
= JSOp(*cx
->logPrevPc
);
1511 ndefs
= js_GetStackDefs(cx
, &js_CodeSpec
[logPrevOp
], logPrevOp
,
1512 fp
->script(), cx
->logPrevPc
);
1515 * If there aren't that many elements on the stack, then we have
1516 * probably entered a new frame, and printing output would just be
1520 ndefs
< regs
->sp
- fp
->slots()) {
1521 for (n
= -ndefs
; n
< 0; n
++) {
1522 char *bytes
= DecompileValueGenerator(cx
, n
, regs
->sp
[n
], NULL
);
1524 fprintf(logfp
, "%s %s",
1525 (n
== -ndefs
) ? " output:" : ",",
1529 JS_ClearPendingException(cx
);
1532 fprintf(logfp
, " @ %u\n", (uintN
) (regs
->sp
- fp
->base()));
1534 fprintf(logfp
, " stack: ");
1535 for (Value
*siter
= fp
->base(); siter
< regs
->sp
; siter
++) {
1536 if (siter
->isObject() && siter
->toObject().getClass() == &js_CallClass
) {
1538 * Call objects have NULL convert ops so that we catch cases
1539 * where they escape. So js_ValueToString doesn't work on them.
1541 fputs("<call>", logfp
);
1543 JSString
*str
= js_ValueToString(cx
, *siter
);
1544 JSLinearString
*linearStr
= str
? str
->ensureLinear(cx
) : NULL
;
1546 fputs("<null>", logfp
);
1547 JS_ClearPendingException(cx
);
1549 FileEscapedString(logfp
, linearStr
, 0);
1557 fprintf(logfp
, "%4u: ",
1558 js_PCToLineNumber(cx
, fp
->script(),
1559 fp
->hasImacropc() ? fp
->imacropc() : regs
->pc
));
1560 js_Disassemble1(cx
, fp
->script(), regs
->pc
,
1561 regs
->pc
- fp
->script()->code
,
1563 op
= (JSOp
) *regs
->pc
;
1564 nuses
= js_GetStackUses(&js_CodeSpec
[op
], op
, regs
->pc
);
1566 for (n
= -nuses
; n
< 0; n
++) {
1567 char *bytes
= DecompileValueGenerator(cx
, n
, regs
->sp
[n
], NULL
);
1569 fprintf(logfp
, "%s %s",
1570 (n
== -nuses
) ? " inputs:" : ",",
1574 JS_ClearPendingException(cx
);
1577 fprintf(logfp
, " @ %u\n", (uintN
) (regs
->sp
- fp
->base()));
1579 cx
->logPrevPc
= regs
->pc
;
1581 /* It's nice to have complete logs when debugging a crash. */
1589 # include <stdlib.h>
1591 # define HIST_NSLOTS 8
1594 * The second dimension is hardcoded at 256 because we know that many bits fit
1595 * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address
1596 * any particular row.
1598 static uint32 succeeds
[JSOP_LIMIT
][256];
1599 static uint32 slot_ops
[JSOP_LIMIT
][HIST_NSLOTS
];
1601 JS_STATIC_INTERPRET
void
1602 js_MeterOpcodePair(JSOp op1
, JSOp op2
)
1604 if (op1
!= JSOP_STOP
)
1605 ++succeeds
[op1
][op2
];
1608 JS_STATIC_INTERPRET
void
1609 js_MeterSlotOpcode(JSOp op
, uint32 slot
)
1611 if (slot
< HIST_NSLOTS
)
1612 ++slot_ops
[op
][slot
];
1615 typedef struct Edge
{
1622 compare_edges(const void *a
, const void *b
)
1624 const Edge
*ea
= (const Edge
*) a
;
1625 const Edge
*eb
= (const Edge
*) b
;
1627 return (int32
)eb
->count
- (int32
)ea
->count
;
1633 const char *name
, *from
, *style
;
1635 uint32 total
, count
;
1636 uint32 i
, j
, nedges
;
1639 name
= getenv("JS_OPMETER_FILE");
1641 name
= "/tmp/ops.dot";
1642 fp
= fopen(name
, "w");
1649 for (i
= 0; i
< JSOP_LIMIT
; i
++) {
1650 for (j
= 0; j
< JSOP_LIMIT
; j
++) {
1651 count
= succeeds
[i
][j
];
1659 # define SIGNIFICANT(count,total) (200. * (count) >= (total))
1661 graph
= (Edge
*) js_calloc(nedges
* sizeof graph
[0]);
1664 for (i
= nedges
= 0; i
< JSOP_LIMIT
; i
++) {
1665 from
= js_CodeName
[i
];
1666 for (j
= 0; j
< JSOP_LIMIT
; j
++) {
1667 count
= succeeds
[i
][j
];
1668 if (count
!= 0 && SIGNIFICANT(count
, total
)) {
1669 graph
[nedges
].from
= from
;
1670 graph
[nedges
].to
= js_CodeName
[j
];
1671 graph
[nedges
].count
= count
;
1676 qsort(graph
, nedges
, sizeof(Edge
), compare_edges
);
1680 fputs("digraph {\n", fp
);
1681 for (i
= 0, style
= NULL
; i
< nedges
; i
++) {
1682 JS_ASSERT(i
== 0 || graph
[i
-1].count
>= graph
[i
].count
);
1683 if (!style
|| graph
[i
-1].count
!= graph
[i
].count
) {
1684 style
= (i
> nedges
* .75) ? "dotted" :
1685 (i
> nedges
* .50) ? "dashed" :
1686 (i
> nedges
* .25) ? "solid" : "bold";
1688 fprintf(fp
, " %s -> %s [label=\"%lu\" style=%s]\n",
1689 graph
[i
].from
, graph
[i
].to
,
1690 (unsigned long)graph
[i
].count
, style
);
1696 name
= getenv("JS_OPMETER_HIST");
1698 name
= "/tmp/ops.hist";
1699 fp
= fopen(name
, "w");
1704 fputs("bytecode", fp
);
1705 for (j
= 0; j
< HIST_NSLOTS
; j
++)
1706 fprintf(fp
, " slot %1u", (unsigned)j
);
1708 fputs("========", fp
);
1709 for (j
= 0; j
< HIST_NSLOTS
; j
++)
1710 fputs(" =======", fp
);
1712 for (i
= 0; i
< JSOP_LIMIT
; i
++) {
1713 for (j
= 0; j
< HIST_NSLOTS
; j
++) {
1714 if (slot_ops
[i
][j
] != 0) {
1715 /* Reuse j in the next loop, since we break after. */
1716 fprintf(fp
, "%-8.8s", js_CodeName
[i
]);
1717 for (j
= 0; j
< HIST_NSLOTS
; j
++)
1718 fprintf(fp
, " %7lu", (unsigned long)slot_ops
[i
][j
]);
1727 #endif /* JS_OPSMETER */
1729 #endif /* !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___ */
1731 #ifndef jsinvoke_cpp___
1734 // jsval representation metering: this measures the kinds of jsvals that
1735 // are used as inputs to each JSOp.
1736 namespace reprmeter
{
1746 FUNCTION_INTERPRETED
,
1747 FUNCTION_FASTNATIVE
,
1752 // Return the |repr| value giving the representation of the given jsval.
1756 if (JSVAL_IS_INT(v
))
1758 if (JSVAL_IS_DOUBLE(v
))
1760 if (JSVAL_IS_SPECIAL(v
)) {
1761 return (v
== JSVAL_TRUE
|| v
== JSVAL_FALSE
)
1765 if (JSVAL_IS_STRING(v
))
1768 JS_ASSERT(JSVAL_IS_OBJECT(v
));
1770 JSObject
*obj
= JSVAL_TO_OBJECT(v
);
1771 if (VALUE_IS_FUNCTION(cx
, v
)) {
1772 JSFunction
*fun
= obj
->getFunctionPrivate();
1773 if (FUN_INTERPRETED(fun
))
1774 return FUNCTION_INTERPRETED
;
1775 return FUNCTION_FASTNATIVE
;
1777 // This must come before the general array test, because that
1778 // one subsumes this one.
1781 if (obj
->isDenseArray())
1785 return OBJECT_PLAIN
;
1788 static const char *reprName
[] = { "invalid", "int", "double", "bool", "special",
1789 "string", "null", "object",
1790 "fun:interp", "fun:native"
1791 "array:slow", "array:dense" };
1793 // Logically, a tuple of (JSOp, repr_1, ..., repr_n) where repr_i is
1794 // the |repr| of the ith input to the JSOp.
1796 enum { max_uses
= 16 };
1799 Repr uses
[max_uses
];
1801 OpInput() : op(JSOp(255)) {
1802 for (int i
= 0; i
< max_uses
; ++i
)
1806 OpInput(JSOp op
) : op(op
) {
1807 for (int i
= 0; i
< max_uses
; ++i
)
1812 operator uint32() const {
1814 for (int i
= 0; i
< max_uses
; ++i
)
1815 h
= h
* 7 + uses
[i
] * 13;
1819 bool operator==(const OpInput
&opinput
) const {
1820 if (op
!= opinput
.op
)
1822 for (int i
= 0; i
< max_uses
; ++i
) {
1823 if (uses
[i
] != opinput
.uses
[i
])
1829 OpInput
&operator=(const OpInput
&opinput
) {
1831 for (int i
= 0; i
< max_uses
; ++i
)
1832 uses
[i
] = opinput
.uses
[i
];
1837 typedef HashMap
<OpInput
, uint64
, DefaultHasher
<OpInput
>, SystemAllocPolicy
> OpInputHistogram
;
1839 OpInputHistogram opinputs
;
1840 bool opinputsInitialized
= false;
1842 // Record an OpInput for the current op. This should be called just
1843 // before executing the op.
1845 MeterRepr(JSContext
*cx
)
1847 // Note that we simply ignore the possibility of errors (OOMs)
1848 // using the hash map, since this is only metering code.
1850 if (!opinputsInitialized
) {
1852 opinputsInitialized
= true;
1855 JSOp op
= JSOp(*cx
->regs
->pc
);
1856 int nuses
= js_GetStackUses(&js_CodeSpec
[op
], op
, cx
->regs
->pc
);
1858 // Build the OpInput.
1859 OpInput
opinput(op
);
1860 for (int i
= 0; i
< nuses
; ++i
) {
1861 jsval v
= cx
->regs
->sp
[-nuses
+i
];
1862 opinput
.uses
[i
] = GetRepr(v
);
1865 OpInputHistogram::AddPtr p
= opinputs
.lookupForAdd(opinput
);
1869 opinputs
.add(p
, opinput
, 1);
1875 FILE *f
= fopen("/tmp/reprmeter.txt", "w");
1877 for (OpInputHistogram::Range r
= opinputs
.all(); !r
.empty(); r
.popFront()) {
1878 const OpInput
&o
= r
.front().key
;
1879 uint64 c
= r
.front().value
;
1880 fprintf(f
, "%3d,%s", o
.op
, js_CodeName
[o
.op
]);
1881 for (int i
= 0; i
< OpInput::max_uses
&& o
.uses
[i
] != NONE
; ++i
)
1882 fprintf(f
, ",%s", reprName
[o
.uses
[i
]]);
1883 fprintf(f
, ",%llu\n", c
);
1888 #endif /* JS_REPRMETER */
1890 #define PUSH_COPY(v) do { *regs.sp++ = v; assertSameCompartment(cx, regs.sp[-1]); } while (0)
1891 #define PUSH_NULL() regs.sp++->setNull()
1892 #define PUSH_UNDEFINED() regs.sp++->setUndefined()
1893 #define PUSH_BOOLEAN(b) regs.sp++->setBoolean(b)
1894 #define PUSH_DOUBLE(d) regs.sp++->setDouble(d)
1895 #define PUSH_INT32(i) regs.sp++->setInt32(i)
1896 #define PUSH_STRING(s) do { regs.sp++->setString(s); assertSameCompartment(cx, regs.sp[-1]); } while (0)
1897 #define PUSH_OBJECT(obj) do { regs.sp++->setObject(obj); assertSameCompartment(cx, regs.sp[-1]); } while (0)
1898 #define PUSH_OBJECT_OR_NULL(obj) do { regs.sp++->setObjectOrNull(obj); assertSameCompartment(cx, regs.sp[-1]); } while (0)
1899 #define PUSH_HOLE() regs.sp++->setMagic(JS_ARRAY_HOLE)
1900 #define POP_COPY_TO(v) v = *--regs.sp
1901 #define POP_RETURN_VALUE() regs.fp->setReturnValue(*--regs.sp)
1903 #define POP_BOOLEAN(cx, vp, b) \
1905 vp = ®s.sp[-1]; \
1906 if (vp->isNull()) { \
1908 } else if (vp->isBoolean()) { \
1909 b = vp->toBoolean(); \
1911 b = !!js_ValueToBoolean(*vp); \
1916 #define VALUE_TO_OBJECT(cx, vp, obj) \
1918 if ((vp)->isObject()) { \
1919 obj = &(vp)->toObject(); \
1921 obj = js_ValueToNonNullObject(cx, *(vp)); \
1924 (vp)->setObject(*obj); \
1928 #define FETCH_OBJECT(cx, n, obj) \
1930 Value *vp_ = ®s.sp[n]; \
1931 VALUE_TO_OBJECT(cx, vp_, obj); \
1934 #define DEFAULT_VALUE(cx, n, hint, v) \
1936 JS_ASSERT(v.isObject()); \
1937 JS_ASSERT(v == regs.sp[n]); \
1938 if (!DefaultValue(cx, &v.toObject(), hint, ®s.sp[n])) \
1943 /* Test whether v is an int in the range [-2^31 + 1, 2^31 - 2] */
1944 static JS_ALWAYS_INLINE
bool
1945 CanIncDecWithoutOverflow(int32_t i
)
1947 return (i
> JSVAL_INT_MIN
) && (i
< JSVAL_INT_MAX
);
1951 * Define JS_OPMETER to instrument bytecode succession, generating a .dot file
1952 * on shutdown that shows the graph of significant predecessor/successor pairs
1953 * executed, where the edge labels give the succession counts. The .dot file
1954 * is named by the JS_OPMETER_FILE envariable, and defaults to /tmp/ops.dot.
1956 * Bonus feature: JS_OPMETER also enables counters for stack-addressing ops
1957 * such as JSOP_GETLOCAL, JSOP_INCARG, via METER_SLOT_OP. The resulting counts
1958 * are written to JS_OPMETER_HIST, defaulting to /tmp/ops.hist.
1961 # define METER_OP_INIT(op) /* nothing */
1962 # define METER_OP_PAIR(op1,op2) /* nothing */
1963 # define METER_SLOT_OP(op,slot) /* nothing */
1967 * The second dimension is hardcoded at 256 because we know that many bits fit
1968 * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address
1969 * any particular row.
1971 # define METER_OP_INIT(op) ((op) = JSOP_STOP)
1972 # define METER_OP_PAIR(op1,op2) (js_MeterOpcodePair(op1, op2))
1973 # define METER_SLOT_OP(op,slot) (js_MeterSlotOpcode(op, slot))
1978 # define METER_REPR(cx) (reprmeter::MeterRepr(cx))
1980 # define METER_REPR(cx) ((void) 0)
1981 #endif /* JS_REPRMETER */
1984 * Threaded interpretation via computed goto appears to be well-supported by
1985 * GCC 3 and higher. IBM's C compiler when run with the right options (e.g.,
1986 * -qlanglvl=extended) also supports threading. Ditto the SunPro C compiler.
1987 * Currently it's broken for JS_VERSION < 160, though this isn't worth fixing.
1988 * Add your compiler support macros here.
1990 #ifndef JS_THREADED_INTERP
1991 # if JS_VERSION >= 160 && ( \
1993 (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \
1994 __SUNPRO_C >= 0x570)
1995 # define JS_THREADED_INTERP 1
1997 # define JS_THREADED_INTERP 0
2002 * Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on
2003 * single-thread DEBUG js shell testing to verify property cache hits.
2005 #if defined DEBUG && !defined JS_THREADSAFE
2007 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) \
2009 if (!AssertValidPropertyCacheHit(cx, script, regs, pcoff, obj, pobj, \
2016 AssertValidPropertyCacheHit(JSContext
*cx
, JSScript
*script
, JSFrameRegs
& regs
,
2017 ptrdiff_t pcoff
, JSObject
*start
, JSObject
*found
,
2018 PropertyCacheEntry
*entry
)
2020 uint32 sample
= cx
->runtime
->gcNumber
;
2024 GET_ATOM_FROM_BYTECODE(script
, regs
.pc
, pcoff
, atom
);
2026 atom
= cx
->runtime
->atomState
.lengthAtom
;
2028 JSObject
*obj
, *pobj
;
2032 if (JOF_OPMODE(*regs
.pc
) == JOF_NAME
) {
2033 ok
= js_FindProperty(cx
, ATOM_TO_JSID(atom
), &obj
, &pobj
, &prop
);
2036 ok
= js_LookupProperty(cx
, obj
, ATOM_TO_JSID(atom
), &pobj
, &prop
);
2040 if (cx
->runtime
->gcNumber
!= sample
|| entry
->vshape() != pobj
->shape())
2043 JS_ASSERT(pobj
== found
);
2045 const Shape
*shape
= (Shape
*) prop
;
2046 if (entry
->vword
.isSlot()) {
2047 JS_ASSERT(entry
->vword
.toSlot() == shape
->slot
);
2048 JS_ASSERT(!shape
->isMethod());
2049 } else if (entry
->vword
.isShape()) {
2050 JS_ASSERT(entry
->vword
.toShape() == shape
);
2051 JS_ASSERT_IF(shape
->isMethod(),
2052 &shape
->methodObject() == &pobj
->nativeGetSlot(shape
->slot
).toObject());
2055 JS_ASSERT(entry
->vword
.isFunObj());
2056 JS_ASSERT(!entry
->vword
.isNull());
2057 JS_ASSERT(pobj
->brandedOrHasMethodBarrier());
2058 JS_ASSERT(shape
->hasDefaultGetterOrIsMethod());
2059 JS_ASSERT(pobj
->containsSlot(shape
->slot
));
2060 v
= pobj
->nativeGetSlot(shape
->slot
);
2061 JS_ASSERT(&entry
->vword
.toFunObj() == &v
.toObject());
2063 if (shape
->isMethod()) {
2064 JS_ASSERT(js_CodeSpec
[*regs
.pc
].format
& JOF_CALLOP
);
2065 JS_ASSERT(&shape
->methodObject() == &v
.toObject());
2073 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0)
2077 * Ensure that the intrepreter switch can close call-bytecode cases in the
2078 * same way as non-call bytecodes.
2080 JS_STATIC_ASSERT(JSOP_NAME_LENGTH
== JSOP_CALLNAME_LENGTH
);
2081 JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH
== JSOP_GETFCSLOT_LENGTH
);
2082 JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH
== JSOP_CALLUPVAR_DBG_LENGTH
);
2083 JS_STATIC_ASSERT(JSOP_GETFCSLOT_LENGTH
== JSOP_CALLFCSLOT_LENGTH
);
2084 JS_STATIC_ASSERT(JSOP_GETARG_LENGTH
== JSOP_CALLARG_LENGTH
);
2085 JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH
== JSOP_CALLLOCAL_LENGTH
);
2086 JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH
== JSOP_CALLXMLNAME_LENGTH
);
2089 * Same for debuggable flat closures defined at top level in another function
2090 * or program fragment.
2092 JS_STATIC_ASSERT(JSOP_DEFFUN_FC_LENGTH
== JSOP_DEFFUN_DBGFC_LENGTH
);
2095 * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
2096 * remain distinct for the decompiler. Likewise for JSOP_INIT{PROP,METHOD}.
2098 JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH
== JSOP_SETPROP_LENGTH
);
2099 JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH
== JSOP_SETMETHOD_LENGTH
);
2100 JS_STATIC_ASSERT(JSOP_INITPROP_LENGTH
== JSOP_INITMETHOD_LENGTH
);
2102 /* See TRY_BRANCH_AFTER_COND. */
2103 JS_STATIC_ASSERT(JSOP_IFNE_LENGTH
== JSOP_IFEQ_LENGTH
);
2104 JS_STATIC_ASSERT(JSOP_IFNE
== JSOP_IFEQ
+ 1);
2106 /* For the fastest case inder JSOP_INCNAME, etc. */
2107 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH
== JSOP_DECNAME_LENGTH
);
2108 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH
== JSOP_NAMEINC_LENGTH
);
2109 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH
== JSOP_NAMEDEC_LENGTH
);
2112 # define ABORT_RECORDING(cx, reason) \
2114 if (TRACE_RECORDER(cx)) \
2115 AbortRecording(cx, reason); \
2118 # define ABORT_RECORDING(cx, reason) ((void) 0)
2122 * Inline fast paths for iteration. js_IteratorMore and js_IteratorNext handle
2123 * all cases, but we inline the most frequently taken paths here.
2126 IteratorMore(JSContext
*cx
, JSObject
*iterobj
, bool *cond
, Value
*rval
)
2128 if (iterobj
->getClass() == &js_IteratorClass
) {
2129 NativeIterator
*ni
= (NativeIterator
*) iterobj
->getPrivate();
2130 if (ni
->isKeyIter()) {
2131 *cond
= (ni
->props_cursor
< ni
->props_end
);
2135 if (!js_IteratorMore(cx
, iterobj
, rval
))
2137 *cond
= rval
->isTrue();
2142 IteratorNext(JSContext
*cx
, JSObject
*iterobj
, Value
*rval
)
2144 if (iterobj
->getClass() == &js_IteratorClass
) {
2145 NativeIterator
*ni
= (NativeIterator
*) iterobj
->getPrivate();
2146 if (ni
->isKeyIter()) {
2147 JS_ASSERT(ni
->props_cursor
< ni
->props_end
);
2148 jsid id
= *ni
->current();
2149 if (JSID_IS_ATOM(id
)) {
2150 rval
->setString(JSID_TO_STRING(id
));
2154 /* Take the slow path if we have to stringify a numeric property name. */
2157 return js_IteratorNext(cx
, iterobj
, rval
);
2161 ScriptPrologue(JSContext
*cx
, JSStackFrame
*fp
)
2163 if (fp
->isConstructing()) {
2164 JSObject
*obj
= js_CreateThisForFunction(cx
, &fp
->callee());
2167 fp
->functionThis().setObject(*obj
);
2169 if (fp
->isExecuteFrame()) {
2170 if (JSInterpreterHook hook
= cx
->debugHooks
->executeHook
)
2171 fp
->setHookData(hook(cx
, fp
, JS_TRUE
, 0, cx
->debugHooks
->executeHookData
));
2173 if (JSInterpreterHook hook
= cx
->debugHooks
->callHook
)
2174 fp
->setHookData(hook(cx
, fp
, JS_TRUE
, 0, cx
->debugHooks
->callHookData
));
2175 Probes::enterJSFun(cx
, fp
->maybeFun(), fp
->maybeScript());
2183 JS_REQUIRES_STACK JS_NEVER_INLINE
bool
2184 Interpret(JSContext
*cx
, JSStackFrame
*entryFrame
, uintN inlineCallCount
, JSInterpMode interpMode
)
2187 TraceVisStateObj
tvso(cx
, S_INTERP
);
2189 JSAutoResolveFlags
rf(cx
, JSRESOLVE_INFER
);
2193 * We call this macro from BEGIN_CASE in threaded interpreters,
2194 * and before entering the switch in non-threaded interpreters.
2195 * However, reaching such points doesn't mean we've actually
2196 * fetched an OP from the instruction stream: some opcodes use
2197 * 'op=x; DO_OP()' to let another opcode's implementation finish
2198 * their work, and many opcodes share entry points with a run of
2199 * consecutive BEGIN_CASEs.
2201 * Take care to trace OP only when it is the opcode fetched from
2202 * the instruction stream, so the trace matches what one would
2203 * expect from looking at the code. (We do omit POPs after SETs;
2204 * unfortunate, but not worth fixing.)
2206 # define LOG_OPCODE(OP) JS_BEGIN_MACRO \
2207 if (JS_UNLIKELY(cx->logfp != NULL) && \
2212 # define LOG_OPCODE(OP) ((void) 0)
2216 * Macros for threaded interpreter loop
2218 #if JS_THREADED_INTERP
2219 static void *const normalJumpTable
[] = {
2220 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2221 JS_EXTENSION &&L_##op,
2222 # include "jsopcode.tbl"
2226 static void *const interruptJumpTable
[] = {
2227 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2228 JS_EXTENSION &&interrupt,
2229 # include "jsopcode.tbl"
2233 register void * const *jumpTable
= normalJumpTable
;
2235 METER_OP_INIT(op
); /* to nullify first METER_OP_PAIR */
2237 # define ENABLE_INTERRUPTS() ((void) (jumpTable = interruptJumpTable))
2240 # define CHECK_RECORDER() \
2241 JS_ASSERT_IF(TRACE_RECORDER(cx), jumpTable == interruptJumpTable)
2243 # define CHECK_RECORDER() ((void)0)
2246 # define DO_OP() JS_BEGIN_MACRO \
2248 JS_EXTENSION_(goto *jumpTable[op]); \
2250 # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
2251 METER_OP_PAIR(op, JSOp(regs.pc[n])); \
2252 op = (JSOp) *(regs.pc += (n)); \
2257 # define BEGIN_CASE(OP) L_##OP: LOG_OPCODE(OP); CHECK_RECORDER();
2258 # define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH);
2259 # define END_VARLEN_CASE DO_NEXT_OP(len);
2260 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP) \
2261 JS_ASSERT(js_CodeSpec[OP].length == 1); \
2262 op = (JSOp) *++regs.pc; \
2265 # define END_EMPTY_CASES
2267 #else /* !JS_THREADED_INTERP */
2269 register intN switchMask
= 0;
2272 # define ENABLE_INTERRUPTS() ((void) (switchMask = -1))
2275 # define CHECK_RECORDER() \
2276 JS_ASSERT_IF(TRACE_RECORDER(cx), switchMask == -1)
2278 # define CHECK_RECORDER() ((void)0)
2281 # define DO_OP() goto do_op
2282 # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
2283 JS_ASSERT((n) == len); \
2287 # define BEGIN_CASE(OP) case OP: CHECK_RECORDER();
2288 # define END_CASE(OP) END_CASE_LEN(OP##_LENGTH)
2289 # define END_CASE_LEN(n) END_CASE_LENX(n)
2290 # define END_CASE_LENX(n) END_CASE_LEN##n
2293 * To share the code for all len == 1 cases we use the specialized label with
2294 * code that falls through to advance_pc: .
2296 # define END_CASE_LEN1 goto advance_pc_by_one;
2297 # define END_CASE_LEN2 len = 2; goto advance_pc;
2298 # define END_CASE_LEN3 len = 3; goto advance_pc;
2299 # define END_CASE_LEN4 len = 4; goto advance_pc;
2300 # define END_CASE_LEN5 len = 5; goto advance_pc;
2301 # define END_VARLEN_CASE goto advance_pc;
2302 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)
2303 # define END_EMPTY_CASES goto advance_pc_by_one;
2305 #endif /* !JS_THREADED_INTERP */
2307 #define LOAD_ATOM(PCOFF, atom) \
2309 JS_ASSERT(regs.fp->hasImacropc() \
2310 ? atoms == COMMON_ATOMS_START(&rt->atomState) && \
2311 GET_INDEX(regs.pc + PCOFF) < js_common_atom_count \
2312 : (size_t)(atoms - script->atomMap.vector) < \
2313 (size_t)(script->atomMap.length - \
2314 GET_INDEX(regs.pc + PCOFF))); \
2315 atom = atoms[GET_INDEX(regs.pc + PCOFF)]; \
2318 #define GET_FULL_INDEX(PCOFF) \
2319 (atoms - script->atomMap.vector + GET_INDEX(regs.pc + (PCOFF)))
2321 #define LOAD_OBJECT(PCOFF, obj) \
2322 (obj = script->getObject(GET_FULL_INDEX(PCOFF)))
2324 #define LOAD_FUNCTION(PCOFF) \
2325 (fun = script->getFunction(GET_FULL_INDEX(PCOFF)))
2327 #define LOAD_DOUBLE(PCOFF, dbl) \
2328 (dbl = script->getConst(GET_FULL_INDEX(PCOFF)).toDouble())
2331 bool useMethodJIT
= cx
->methodJitEnabled
&& interpMode
== JSINTERP_NORMAL
;
2333 bool useMethodJIT
= false;
2338 #define MONITOR_BRANCH_METHODJIT() \
2340 mjit::CompileStatus status = \
2341 mjit::CanMethodJITAtBranch(cx, script, regs.fp, regs.pc); \
2342 if (status == mjit::Compile_Error) \
2344 if (status == mjit::Compile_Okay) { \
2346 script->nativeCodeForPC(regs.fp->isConstructing(), regs.pc); \
2347 interpReturnOK = mjit::JaegerShotAtSafePoint(cx, ncode); \
2348 if (inlineCallCount) \
2350 goto leave_on_safe_point; \
2352 if (status == mjit::Compile_Abort) { \
2353 useMethodJIT = false; \
2359 #define MONITOR_BRANCH_METHODJIT() ((void) 0)
2366 #if JS_THREADED_INTERP
2367 #define MONITOR_BRANCH_TRACEVIS \
2369 if (jumpTable != interruptJumpTable) \
2370 EnterTraceVisState(cx, S_RECORD, R_NONE); \
2372 #else /* !JS_THREADED_INTERP */
2373 #define MONITOR_BRANCH_TRACEVIS \
2375 EnterTraceVisState(cx, S_RECORD, R_NONE); \
2379 #define MONITOR_BRANCH_TRACEVIS
2382 #define RESTORE_INTERP_VARS() \
2384 script = regs.fp->script(); \
2385 argv = regs.fp->maybeFormalArgs(); \
2386 atoms = FrameAtomBase(cx, regs.fp); \
2387 JS_ASSERT(cx->regs == ®s); \
2388 if (cx->isExceptionPending()) \
2392 #define MONITOR_BRANCH() \
2394 if (TRACING_ENABLED(cx)) { \
2395 if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && useMethodJIT) { \
2396 MONITOR_BRANCH_METHODJIT(); \
2398 MonitorResult r = MonitorLoopEdge(cx, inlineCallCount, interpMode); \
2399 if (r == MONITOR_RECORDING) { \
2400 JS_ASSERT(TRACE_RECORDER(cx)); \
2401 JS_ASSERT(!TRACE_PROFILER(cx)); \
2402 MONITOR_BRANCH_TRACEVIS; \
2403 ENABLE_INTERRUPTS(); \
2404 CLEAR_LEAVE_ON_TRACE_POINT(); \
2406 RESTORE_INTERP_VARS(); \
2407 JS_ASSERT_IF(cx->isExceptionPending(), r == MONITOR_ERROR); \
2408 if (r == MONITOR_ERROR) \
2412 MONITOR_BRANCH_METHODJIT(); \
2416 #else /* !JS_TRACER */
2418 #define MONITOR_BRANCH() \
2420 MONITOR_BRANCH_METHODJIT(); \
2423 #endif /* !JS_TRACER */
2426 * Prepare to call a user-supplied branch handler, and abort the script
2427 * if it returns false.
2429 #define CHECK_BRANCH() \
2431 if (JS_THREAD_DATA(cx)->interruptFlags && !js_HandleExecutionInterrupt(cx)) \
2435 #if defined(JS_TRACER) && defined(JS_METHODJIT)
2436 # define LEAVE_ON_SAFE_POINT() \
2438 JS_ASSERT_IF(leaveOnSafePoint, !TRACE_RECORDER(cx)); \
2439 JS_ASSERT_IF(leaveOnSafePoint, !TRACE_PROFILER(cx)); \
2440 JS_ASSERT_IF(leaveOnSafePoint, interpMode != JSINTERP_NORMAL); \
2441 if (leaveOnSafePoint && !regs.fp->hasImacropc() && \
2442 script->maybeNativeCodeForPC(regs.fp->isConstructing(), regs.pc)) { \
2443 JS_ASSERT(!TRACE_RECORDER(cx)); \
2444 interpReturnOK = true; \
2445 goto leave_on_safe_point; \
2449 # define LEAVE_ON_SAFE_POINT() /* nop */
2455 op = (JSOp) *regs.pc; \
2458 if (op == JSOP_NOTRACE) { \
2459 if (TRACE_RECORDER(cx) || TRACE_PROFILER(cx)) { \
2461 op = (JSOp) *regs.pc; \
2463 } else if (op == JSOP_TRACE) { \
2465 op = (JSOp) *regs.pc; \
2468 LEAVE_ON_SAFE_POINT(); \
2472 #define CHECK_INTERRUPT_HANDLER() \
2474 if (cx->debugHooks->interruptHook) \
2475 ENABLE_INTERRUPTS(); \
2478 JSFrameRegs regs
= *cx
->regs
;
2480 /* Repoint cx->regs to a local variable for faster access. */
2481 struct InterpExitGuard
{
2483 const JSFrameRegs
®s
;
2484 JSFrameRegs
*prevContextRegs
;
2485 InterpExitGuard(JSContext
*cx
, JSFrameRegs
®s
)
2486 : cx(cx
), regs(regs
), prevContextRegs(cx
->regs
) {
2487 cx
->setCurrentRegs(®s
);
2490 ~InterpExitGuard() {
2492 JS_ASSERT(cx
->regs
== ®s
);
2493 *prevContextRegs
= regs
;
2494 cx
->setCurrentRegs(prevContextRegs
);
2496 } interpGuard(cx
, regs
);
2498 /* Copy in hot values that change infrequently. */
2499 JSRuntime
*const rt
= cx
->runtime
;
2500 JSScript
*script
= regs
.fp
->script();
2501 Value
*argv
= regs
.fp
->maybeFormalArgs();
2502 CHECK_INTERRUPT_HANDLER();
2504 #if defined(JS_TRACER) && defined(JS_METHODJIT)
2505 bool leaveOnSafePoint
= (interpMode
== JSINTERP_SAFEPOINT
);
2506 # define CLEAR_LEAVE_ON_TRACE_POINT() ((void) (leaveOnSafePoint = false))
2508 # define CLEAR_LEAVE_ON_TRACE_POINT() ((void) 0)
2512 entryFrame
= regs
.fp
;
2515 * Initialize the index segment register used by LOAD_ATOM and
2516 * GET_FULL_INDEX macros below. As a register we use a pointer based on
2517 * the atom map to turn frequently executed LOAD_ATOM into simple array
2518 * access. For less frequent object and regexp loads we have to recover
2519 * the segment from atoms pointer first.
2521 JSAtom
**atoms
= script
->atomMap
.vector
;
2523 #if JS_HAS_GENERATORS
2524 if (JS_UNLIKELY(regs
.fp
->isGeneratorFrame())) {
2525 JS_ASSERT(interpGuard
.prevContextRegs
== &cx
->generatorFor(regs
.fp
)->regs
);
2526 JS_ASSERT((size_t) (regs
.pc
- script
->code
) <= script
->length
);
2527 JS_ASSERT((size_t) (regs
.sp
- regs
.fp
->base()) <= StackDepth(script
));
2530 * To support generator_throw and to catch ignored exceptions,
2531 * fail if cx->isExceptionPending() is true.
2533 if (cx
->isExceptionPending())
2540 * The method JIT may have already initiated a recording, in which case
2541 * there should already be a valid recorder. Otherwise...
2542 * we cannot reenter the interpreter while recording.
2544 if (interpMode
== JSINTERP_RECORD
) {
2545 JS_ASSERT(TRACE_RECORDER(cx
));
2546 JS_ASSERT(!TRACE_PROFILER(cx
));
2547 ENABLE_INTERRUPTS();
2548 } else if (interpMode
== JSINTERP_PROFILE
) {
2549 ENABLE_INTERRUPTS();
2550 } else if (TRACE_RECORDER(cx
)) {
2551 AbortRecording(cx
, "attempt to reenter interpreter while recording");
2554 if (regs
.fp
->hasImacropc())
2555 atoms
= COMMON_ATOMS_START(&rt
->atomState
);
2558 /* Don't call the script prologue if executing between Method and Trace JIT. */
2559 if (interpMode
== JSINTERP_NORMAL
) {
2560 JS_ASSERT_IF(!regs
.fp
->isGeneratorFrame(), regs
.pc
== script
->code
);
2561 if (!ScriptPrologue(cx
, regs
.fp
))
2565 CHECK_INTERRUPT_HANDLER();
2567 /* State communicated between non-local jumps: */
2568 JSBool interpReturnOK
;
2569 JSAtom
*atomNotDefined
;
2572 * It is important that "op" be initialized before calling DO_OP because
2573 * it is possible for "op" to be specially assigned during the normal
2574 * processing of an opcode while looping. We rely on DO_NEXT_OP to manage
2575 * "op" correctly in all other cases.
2581 /* Check for too deep of a native thread stack. */
2584 JS_CHECK_RECURSION(cx
, do {
2585 if (TRACE_RECORDER(cx
))
2586 AbortRecording(cx
, "too much recursion");
2587 if (TRACE_PROFILER(cx
))
2592 JS_CHECK_RECURSION(cx
, do {
2593 if (TRACE_RECORDER(cx
))
2594 AbortRecording(cx
, "too much recursion");
2599 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
2602 #if JS_THREADED_INTERP
2608 #if JS_THREADED_INTERP
2610 * This is a loop, but it does not look like a loop. The loop-closing
2611 * jump is distributed throughout goto *jumpTable[op] inside of DO_OP.
2612 * When interrupts are enabled, jumpTable is set to interruptJumpTable
2613 * where all jumps point to the interrupt label. The latter, after
2614 * calling the interrupt handler, dispatches through normalJumpTable to
2615 * continue the normal bytecode processing.
2618 #else /* !JS_THREADED_INTERP */
2621 JS_ASSERT(js_CodeSpec
[op
].length
== 1);
2625 op
= (JSOp
) *regs
.pc
;
2630 switchOp
= intN(op
) | switchMask
;
2635 #if JS_THREADED_INTERP
2637 #else /* !JS_THREADED_INTERP */
2639 JS_ASSERT(switchMask
== -1);
2640 #endif /* !JS_THREADED_INTERP */
2642 bool moreInterrupts
= false;
2643 JSInterruptHook hook
= cx
->debugHooks
->interruptHook
;
2646 if (TRACE_RECORDER(cx
))
2647 AbortRecording(cx
, "interrupt hook");
2649 if (TRACE_PROFILER(cx
))
2654 switch (hook(cx
, script
, regs
.pc
, Jsvalify(&rval
),
2655 cx
->debugHooks
->interruptHookData
)) {
2658 case JSTRAP_CONTINUE
:
2661 regs
.fp
->setReturnValue(rval
);
2662 interpReturnOK
= JS_TRUE
;
2665 cx
->setPendingException(rval
);
2669 moreInterrupts
= true;
2674 if (TRACE_PROFILER(cx
) && interpMode
== JSINTERP_PROFILE
) {
2675 LoopProfile
*prof
= TRACE_PROFILER(cx
);
2676 JS_ASSERT(!TRACE_RECORDER(cx
));
2677 LoopProfile::ProfileAction act
= prof
->profileOperation(cx
, op
);
2679 case LoopProfile::ProfComplete
:
2680 if (interpMode
!= JSINTERP_NORMAL
) {
2681 leaveOnSafePoint
= true;
2682 LEAVE_ON_SAFE_POINT();
2686 moreInterrupts
= true;
2691 if (TraceRecorder
* tr
= TRACE_RECORDER(cx
)) {
2692 JS_ASSERT(!TRACE_PROFILER(cx
));
2693 AbortableRecordingStatus status
= tr
->monitorRecording(op
);
2694 JS_ASSERT_IF(cx
->isExceptionPending(), status
== ARECORD_ERROR
);
2696 if (interpMode
!= JSINTERP_NORMAL
) {
2697 JS_ASSERT(interpMode
== JSINTERP_RECORD
|| JSINTERP_SAFEPOINT
);
2699 case ARECORD_IMACRO_ABORTED
:
2700 case ARECORD_ABORTED
:
2701 case ARECORD_COMPLETED
:
2704 leaveOnSafePoint
= true;
2705 LEAVE_ON_SAFE_POINT();
2714 case ARECORD_CONTINUE
:
2715 moreInterrupts
= true;
2717 case ARECORD_IMACRO
:
2718 case ARECORD_IMACRO_ABORTED
:
2719 atoms
= COMMON_ATOMS_START(&rt
->atomState
);
2720 op
= JSOp(*regs
.pc
);
2721 CLEAR_LEAVE_ON_TRACE_POINT();
2722 if (status
== ARECORD_IMACRO
)
2723 DO_OP(); /* keep interrupting for op. */
2726 // The code at 'error:' aborts the recording.
2728 case ARECORD_ABORTED
:
2729 case ARECORD_COMPLETED
:
2732 /* A 'stop' error should have already aborted recording. */
2734 JS_NOT_REACHED("Bad recording status");
2737 #endif /* !JS_TRACER */
2739 #if JS_THREADED_INTERP
2741 if (!moreInterrupts
)
2742 ExitTraceVisState(cx
, R_ABORT
);
2744 jumpTable
= moreInterrupts
? interruptJumpTable
: normalJumpTable
;
2745 JS_EXTENSION_(goto *normalJumpTable
[op
]);
2747 switchMask
= moreInterrupts
? -1 : 0;
2748 switchOp
= intN(op
);
2753 /* No-ops for ease of decompilation. */
2754 ADD_EMPTY_CASE(JSOP_NOP
)
2755 ADD_EMPTY_CASE(JSOP_CONDSWITCH
)
2756 ADD_EMPTY_CASE(JSOP_TRY
)
2757 #if JS_HAS_XML_SUPPORT
2758 ADD_EMPTY_CASE(JSOP_STARTXML
)
2759 ADD_EMPTY_CASE(JSOP_STARTXMLEXPR
)
2761 ADD_EMPTY_CASE(JSOP_NULLBLOCKCHAIN
)
2764 BEGIN_CASE(JSOP_TRACE
)
2765 BEGIN_CASE(JSOP_NOTRACE
)
2766 LEAVE_ON_SAFE_POINT();
2767 END_CASE(JSOP_TRACE
)
2769 /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
2770 BEGIN_CASE(JSOP_LINENO
)
2771 END_CASE(JSOP_LINENO
)
2773 BEGIN_CASE(JSOP_BLOCKCHAIN
)
2774 END_CASE(JSOP_BLOCKCHAIN
)
2776 BEGIN_CASE(JSOP_PUSH
)
2780 BEGIN_CASE(JSOP_POP
)
2784 BEGIN_CASE(JSOP_POPN
)
2786 regs
.sp
-= GET_UINT16(regs
.pc
);
2788 JS_ASSERT(regs
.fp
->base() <= regs
.sp
);
2789 JSObject
*obj
= GetBlockChain(cx
, regs
.fp
);
2791 OBJ_BLOCK_DEPTH(cx
, obj
) + OBJ_BLOCK_COUNT(cx
, obj
)
2792 <= (size_t) (regs
.sp
- regs
.fp
->base()));
2793 for (obj
= ®s
.fp
->scopeChain(); obj
; obj
= obj
->getParent()) {
2794 Class
*clasp
= obj
->getClass();
2795 if (clasp
!= &js_BlockClass
&& clasp
!= &js_WithClass
)
2797 if (obj
->getPrivate() != js_FloatingFrameIfGenerator(cx
, regs
.fp
))
2799 JS_ASSERT(regs
.fp
->base() + OBJ_BLOCK_DEPTH(cx
, obj
)
2800 + ((clasp
== &js_BlockClass
)
2801 ? OBJ_BLOCK_COUNT(cx
, obj
)
2809 BEGIN_CASE(JSOP_SETRVAL
)
2810 BEGIN_CASE(JSOP_POPV
)
2814 BEGIN_CASE(JSOP_ENTERWITH
)
2815 if (!js_EnterWith(cx
, -1, JSOP_ENTERWITH
, JSOP_ENTERWITH_LENGTH
))
2819 * We must ensure that different "with" blocks have different stack depth
2820 * associated with them. This allows the try handler search to properly
2821 * recover the scope chain. Thus we must keep the stack at least at the
2824 * We set sp[-1] to the current "with" object to help asserting the
2825 * enter/leave balance in [leavewith].
2827 regs
.sp
[-1].setObject(regs
.fp
->scopeChain());
2828 END_CASE(JSOP_ENTERWITH
)
2830 BEGIN_CASE(JSOP_LEAVEWITH
)
2831 JS_ASSERT(®s
.sp
[-1].toObject() == ®s
.fp
->scopeChain());
2834 END_CASE(JSOP_LEAVEWITH
)
2836 BEGIN_CASE(JSOP_RETURN
)
2840 BEGIN_CASE(JSOP_RETRVAL
) /* fp return value already set */
2841 BEGIN_CASE(JSOP_STOP
)
2844 * When the inlined frame exits with an exception or an error, ok will be
2845 * false after the inline_return label.
2850 if (regs
.fp
->hasImacropc()) {
2852 * If we are at the end of an imacro, return to its caller in the
2855 JS_ASSERT(op
== JSOP_STOP
);
2856 JS_ASSERT((uintN
)(regs
.sp
- regs
.fp
->slots()) <= script
->nslots
);
2857 jsbytecode
*imacpc
= regs
.fp
->imacropc();
2858 regs
.pc
= imacpc
+ js_CodeSpec
[*imacpc
].length
;
2859 regs
.fp
->clearImacropc();
2860 LEAVE_ON_SAFE_POINT();
2861 atoms
= script
->atomMap
.vector
;
2862 op
= JSOp(*regs
.pc
);
2867 interpReturnOK
= true;
2868 if (entryFrame
!= regs
.fp
)
2871 JS_ASSERT(!js_IsActiveWithOrBlock(cx
, ®s
.fp
->scopeChain(), 0));
2872 interpReturnOK
= ScriptEpilogue(cx
, regs
.fp
, interpReturnOK
);
2873 CHECK_INTERRUPT_HANDLER();
2875 /* The JIT inlines ScriptEpilogue. */
2877 Value
*newsp
= regs
.fp
->actualArgs() - 1;
2878 newsp
[-1] = regs
.fp
->returnValue();
2879 cx
->stack().popInlineFrame(cx
, regs
.fp
->prev(), newsp
);
2881 /* Sync interpreter registers. */
2882 script
= regs
.fp
->script();
2883 argv
= regs
.fp
->maybeFormalArgs();
2884 atoms
= FrameAtomBase(cx
, regs
.fp
);
2886 /* Resume execution in the calling frame. */
2887 JS_ASSERT(inlineCallCount
);
2889 if (JS_LIKELY(interpReturnOK
)) {
2890 JS_ASSERT(js_CodeSpec
[js_GetOpcode(cx
, script
, regs
.pc
)].length
2891 == JSOP_CALL_LENGTH
);
2892 TRACE_0(LeaveFrame
);
2893 len
= JSOP_CALL_LENGTH
;
2898 JS_ASSERT(regs
.sp
== regs
.fp
->base());
2900 interpReturnOK
= true;
2904 BEGIN_CASE(JSOP_DEFAULT
)
2907 BEGIN_CASE(JSOP_GOTO
)
2909 len
= GET_JUMP_OFFSET(regs
.pc
);
2914 BEGIN_CASE(JSOP_IFEQ
)
2918 POP_BOOLEAN(cx
, _
, cond
);
2919 if (cond
== false) {
2920 len
= GET_JUMP_OFFSET(regs
.pc
);
2926 BEGIN_CASE(JSOP_IFNE
)
2930 POP_BOOLEAN(cx
, _
, cond
);
2931 if (cond
!= false) {
2932 len
= GET_JUMP_OFFSET(regs
.pc
);
2942 POP_BOOLEAN(cx
, vp
, cond
);
2944 len
= GET_JUMP_OFFSET(regs
.pc
);
2951 BEGIN_CASE(JSOP_AND
)
2955 POP_BOOLEAN(cx
, vp
, cond
);
2956 if (cond
== false) {
2957 len
= GET_JUMP_OFFSET(regs
.pc
);
2964 BEGIN_CASE(JSOP_DEFAULTX
)
2967 BEGIN_CASE(JSOP_GOTOX
)
2969 len
= GET_JUMPX_OFFSET(regs
.pc
);
2972 END_CASE(JSOP_GOTOX
);
2974 BEGIN_CASE(JSOP_IFEQX
)
2978 POP_BOOLEAN(cx
, _
, cond
);
2979 if (cond
== false) {
2980 len
= GET_JUMPX_OFFSET(regs
.pc
);
2984 END_CASE(JSOP_IFEQX
)
2986 BEGIN_CASE(JSOP_IFNEX
)
2990 POP_BOOLEAN(cx
, _
, cond
);
2991 if (cond
!= false) {
2992 len
= GET_JUMPX_OFFSET(regs
.pc
);
2996 END_CASE(JSOP_IFNEX
)
2998 BEGIN_CASE(JSOP_ORX
)
3002 POP_BOOLEAN(cx
, vp
, cond
);
3004 len
= GET_JUMPX_OFFSET(regs
.pc
);
3011 BEGIN_CASE(JSOP_ANDX
)
3015 POP_BOOLEAN(cx
, vp
, cond
);
3016 if (cond
== JS_FALSE
) {
3017 len
= GET_JUMPX_OFFSET(regs
.pc
);
3025 * If the index value at sp[n] is not an int that fits in a jsval, it could
3026 * be an object (an XML QName, AttributeName, or AnyName), but only if we are
3027 * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a
3030 #define FETCH_ELEMENT_ID(obj, n, id) \
3032 const Value &idval_ = regs.sp[n]; \
3034 if (ValueFitsInInt32(idval_, &i_) && INT_FITS_IN_JSID(i_)) { \
3035 id = INT_TO_JSID(i_); \
3037 if (!js_InternNonIntElementId(cx, obj, idval_, &id, ®s.sp[n])) \
3042 #define TRY_BRANCH_AFTER_COND(cond,spdec) \
3044 JS_ASSERT(js_CodeSpec[op].length == 1); \
3045 uintN diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ; \
3048 if (cond == (diff_ != 0)) { \
3050 len = GET_JUMP_OFFSET(regs.pc); \
3053 len = 1 + JSOP_IFEQ_LENGTH; \
3060 const Value
&rref
= regs
.sp
[-1];
3061 if (!rref
.isObject()) {
3062 js_ReportValueError(cx
, JSMSG_IN_NOT_OBJECT
, -1, rref
, NULL
);
3065 JSObject
*obj
= &rref
.toObject();
3067 FETCH_ELEMENT_ID(obj
, -2, id
);
3070 if (!obj
->lookupProperty(cx
, id
, &obj2
, &prop
))
3072 bool cond
= prop
!= NULL
;
3073 TRY_BRANCH_AFTER_COND(cond
, 2);
3075 regs
.sp
[-1].setBoolean(cond
);
3079 BEGIN_CASE(JSOP_ITER
)
3081 JS_ASSERT(regs
.sp
> regs
.fp
->base());
3082 uintN flags
= regs
.pc
[1];
3083 if (!js_ValueToIterator(cx
, flags
, ®s
.sp
[-1]))
3085 CHECK_INTERRUPT_HANDLER();
3086 JS_ASSERT(!regs
.sp
[-1].isPrimitive());
3090 BEGIN_CASE(JSOP_MOREITER
)
3092 JS_ASSERT(regs
.sp
- 1 >= regs
.fp
->base());
3093 JS_ASSERT(regs
.sp
[-1].isObject());
3096 if (!IteratorMore(cx
, ®s
.sp
[-2].toObject(), &cond
, ®s
.sp
[-1]))
3098 CHECK_INTERRUPT_HANDLER();
3099 regs
.sp
[-1].setBoolean(cond
);
3101 END_CASE(JSOP_MOREITER
)
3103 BEGIN_CASE(JSOP_ENDITER
)
3105 JS_ASSERT(regs
.sp
- 1 >= regs
.fp
->base());
3106 bool ok
= !!js_CloseIterator(cx
, ®s
.sp
[-1].toObject());
3111 END_CASE(JSOP_ENDITER
)
3113 BEGIN_CASE(JSOP_FORARG
)
3115 JS_ASSERT(regs
.sp
- 1 >= regs
.fp
->base());
3116 uintN slot
= GET_ARGNO(regs
.pc
);
3117 JS_ASSERT(slot
< regs
.fp
->numFormalArgs());
3118 JS_ASSERT(regs
.sp
[-1].isObject());
3119 if (!IteratorNext(cx
, ®s
.sp
[-1].toObject(), &argv
[slot
]))
3122 END_CASE(JSOP_FORARG
)
3124 BEGIN_CASE(JSOP_FORLOCAL
)
3126 JS_ASSERT(regs
.sp
- 1 >= regs
.fp
->base());
3127 uintN slot
= GET_SLOTNO(regs
.pc
);
3128 JS_ASSERT(slot
< regs
.fp
->numSlots());
3129 JS_ASSERT(regs
.sp
[-1].isObject());
3130 if (!IteratorNext(cx
, ®s
.sp
[-1].toObject(), ®s
.fp
->slots()[slot
]))
3133 END_CASE(JSOP_FORLOCAL
)
3135 BEGIN_CASE(JSOP_FORNAME
)
3136 BEGIN_CASE(JSOP_FORGNAME
)
3138 JS_ASSERT(regs
.sp
- 1 >= regs
.fp
->base());
3141 jsid id
= ATOM_TO_JSID(atom
);
3142 JSObject
*obj
, *obj2
;
3144 if (!js_FindProperty(cx
, id
, &obj
, &obj2
, &prop
))
3148 AutoValueRooter
tvr(cx
);
3149 JS_ASSERT(regs
.sp
[-1].isObject());
3150 if (!IteratorNext(cx
, ®s
.sp
[-1].toObject(), tvr
.addr()))
3152 if (!obj
->setProperty(cx
, id
, tvr
.addr(), script
->strictModeCode
))
3156 END_CASE(JSOP_FORNAME
)
3158 BEGIN_CASE(JSOP_FORPROP
)
3160 JS_ASSERT(regs
.sp
- 2 >= regs
.fp
->base());
3163 jsid id
= ATOM_TO_JSID(atom
);
3165 FETCH_OBJECT(cx
, -1, obj
);
3167 AutoValueRooter
tvr(cx
);
3168 JS_ASSERT(regs
.sp
[-2].isObject());
3169 if (!IteratorNext(cx
, ®s
.sp
[-2].toObject(), tvr
.addr()))
3171 if (!obj
->setProperty(cx
, id
, tvr
.addr(), script
->strictModeCode
))
3176 END_CASE(JSOP_FORPROP
)
3178 BEGIN_CASE(JSOP_FORELEM
)
3180 * JSOP_FORELEM simply dups the property identifier at top of stack and
3181 * lets the subsequent JSOP_ENUMELEM opcode sequence handle the left-hand
3182 * side expression evaluation and assignment. This opcode exists solely to
3183 * help the decompiler.
3185 JS_ASSERT(regs
.sp
- 1 >= regs
.fp
->base());
3186 JS_ASSERT(regs
.sp
[-1].isObject());
3188 if (!IteratorNext(cx
, ®s
.sp
[-2].toObject(), ®s
.sp
[-1]))
3190 END_CASE(JSOP_FORELEM
)
3192 BEGIN_CASE(JSOP_DUP
)
3194 JS_ASSERT(regs
.sp
> regs
.fp
->base());
3195 const Value
&rref
= regs
.sp
[-1];
3200 BEGIN_CASE(JSOP_DUP2
)
3202 JS_ASSERT(regs
.sp
- 2 >= regs
.fp
->base());
3203 const Value
&lref
= regs
.sp
[-2];
3204 const Value
&rref
= regs
.sp
[-1];
3210 BEGIN_CASE(JSOP_SWAP
)
3212 JS_ASSERT(regs
.sp
- 2 >= regs
.fp
->base());
3213 Value
&lref
= regs
.sp
[-2];
3214 Value
&rref
= regs
.sp
[-1];
3219 BEGIN_CASE(JSOP_PICK
)
3221 jsint i
= regs
.pc
[1];
3222 JS_ASSERT(regs
.sp
- (i
+1) >= regs
.fp
->base());
3223 Value lval
= regs
.sp
[-(i
+1)];
3224 memmove(regs
.sp
- (i
+1), regs
.sp
- i
, sizeof(Value
)*i
);
3229 #define NATIVE_GET(cx,obj,pobj,shape,getHow,vp) \
3231 if (shape->isDataDescriptor() && shape->hasDefaultGetter()) { \
3232 /* Fast path for Object instance properties. */ \
3233 JS_ASSERT((shape)->slot != SHAPE_INVALID_SLOT || \
3234 !shape->hasDefaultSetter()); \
3235 if (((shape)->slot != SHAPE_INVALID_SLOT)) \
3236 *(vp) = (pobj)->nativeGetSlot((shape)->slot); \
3238 (vp)->setUndefined(); \
3240 if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp)) \
3245 #define NATIVE_SET(cx,obj,shape,entry,strict,vp) \
3247 if (shape->hasDefaultSetter() && \
3248 (shape)->slot != SHAPE_INVALID_SLOT && \
3249 !(obj)->brandedOrHasMethodBarrier()) { \
3250 /* Fast path for, e.g., plain Object instance properties. */ \
3251 (obj)->nativeSetSlot((shape)->slot, *vp); \
3253 if (!js_NativeSet(cx, obj, shape, false, strict, vp)) \
3259 * Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is
3260 * the constant length of the SET opcode sequence, and spdec is the constant
3261 * by which to decrease the stack pointer to pop all of the SET op's operands.
3263 * NB: unlike macros that could conceivably be replaced by functions (ignoring
3264 * goto error), where a call should not have to be braced in order to expand
3265 * correctly (e.g., in if (cond) FOO(); else BAR()), these three macros lack
3266 * JS_{BEGIN,END}_MACRO brackets. They are also indented so as to align with
3267 * nearby opcode code.
3269 #define SKIP_POP_AFTER_SET(oplen,spdec) \
3270 if (regs.pc[oplen] == JSOP_POP) { \
3272 regs.pc += oplen + JSOP_POP_LENGTH; \
3273 op = (JSOp) *regs.pc; \
3277 #define END_SET_CASE(OP) \
3278 SKIP_POP_AFTER_SET(OP##_LENGTH, 1); \
3281 #define END_SET_CASE_STORE_RVAL(OP,spdec) \
3282 SKIP_POP_AFTER_SET(OP##_LENGTH, spdec); \
3284 Value *newsp = regs.sp - ((spdec) - 1); \
3285 newsp[-1] = regs.sp[-1]; \
3290 BEGIN_CASE(JSOP_SETCONST
)
3294 JSObject
&obj
= regs
.fp
->varobj(cx
);
3295 const Value
&ref
= regs
.sp
[-1];
3296 if (!obj
.defineProperty(cx
, ATOM_TO_JSID(atom
), ref
,
3297 PropertyStub
, StrictPropertyStub
,
3298 JSPROP_ENUMERATE
| JSPROP_PERMANENT
| JSPROP_READONLY
)) {
3302 END_SET_CASE(JSOP_SETCONST
);
3304 #if JS_HAS_DESTRUCTURING
3305 BEGIN_CASE(JSOP_ENUMCONSTELEM
)
3307 const Value
&ref
= regs
.sp
[-3];
3309 FETCH_OBJECT(cx
, -2, obj
);
3311 FETCH_ELEMENT_ID(obj
, -1, id
);
3312 if (!obj
->defineProperty(cx
, id
, ref
,
3313 PropertyStub
, StrictPropertyStub
,
3314 JSPROP_ENUMERATE
| JSPROP_PERMANENT
| JSPROP_READONLY
)) {
3319 END_CASE(JSOP_ENUMCONSTELEM
)
3322 BEGIN_CASE(JSOP_BINDGNAME
)
3323 PUSH_OBJECT(*regs
.fp
->scopeChain().getGlobal());
3324 END_CASE(JSOP_BINDGNAME
)
3326 BEGIN_CASE(JSOP_BINDNAME
)
3331 * We can skip the property lookup for the global object. If the
3332 * property does not exist anywhere on the scope chain, JSOP_SETNAME
3333 * adds the property to the global.
3335 * As a consequence of this optimization for the global object we run
3336 * its JSRESOLVE_ASSIGNING-tolerant resolve hooks only in JSOP_SETNAME,
3337 * after the interpreter evaluates the right- hand-side of the
3338 * assignment, and not here.
3340 * This should be transparent to the hooks because the script, instead
3341 * of name = rhs, could have used global.name = rhs given a global
3342 * object reference, which also calls the hooks only after evaluating
3343 * the rhs. We desire such resolve hook equivalence between the two
3346 obj
= ®s
.fp
->scopeChain();
3347 if (!obj
->getParent())
3350 PropertyCacheEntry
*entry
;
3353 JS_PROPERTY_CACHE(cx
).test(cx
, regs
.pc
, obj
, obj2
, entry
, atom
);
3355 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj
, obj2
, entry
);
3359 jsid id
= ATOM_TO_JSID(atom
);
3360 obj
= js_FindIdentifierBase(cx
, ®s
.fp
->scopeChain(), id
);
3366 END_CASE(JSOP_BINDNAME
)
3368 BEGIN_CASE(JSOP_IMACOP
)
3369 JS_ASSERT(JS_UPTRDIFF(regs
.fp
->imacropc(), script
->code
) < script
->length
);
3370 op
= JSOp(*regs
.fp
->imacropc());
3373 #define BITWISE_OP(OP) \
3376 if (!ValueToECMAInt32(cx, regs.sp[-2], &i)) \
3378 if (!ValueToECMAInt32(cx, regs.sp[-1], &j)) \
3382 regs.sp[-1].setInt32(i); \
3385 BEGIN_CASE(JSOP_BITOR
)
3387 END_CASE(JSOP_BITOR
)
3389 BEGIN_CASE(JSOP_BITXOR
)
3391 END_CASE(JSOP_BITXOR
)
3393 BEGIN_CASE(JSOP_BITAND
)
3395 END_CASE(JSOP_BITAND
)
3400 * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies
3401 * because they begin if/else chains, so callers must not put semicolons after
3402 * the call expressions!
3404 #if JS_HAS_XML_SUPPORT
3405 #define XML_EQUALITY_OP(OP) \
3406 if ((lval.isObject() && lval.toObject().isXML()) || \
3407 (rval.isObject() && rval.toObject().isXML())) { \
3408 if (!js_TestXMLEquality(cx, lval, rval, &cond)) \
3410 cond = cond OP JS_TRUE; \
3413 #define XML_EQUALITY_OP(OP) /* nothing */
3416 #define EQUALITY_OP(OP, IFNAN) \
3419 Value rval = regs.sp[-1]; \
3420 Value lval = regs.sp[-2]; \
3421 XML_EQUALITY_OP(OP) \
3422 if (SameType(lval, rval)) { \
3423 if (lval.isString()) { \
3424 JSString *l = lval.toString(), *r = rval.toString(); \
3426 if (!EqualStrings(cx, l, r, &equal)) \
3428 cond = equal OP JS_TRUE; \
3429 } else if (lval.isDouble()) { \
3430 double l = lval.toDouble(), r = rval.toDouble(); \
3431 cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN); \
3432 } else if (lval.isObject()) { \
3433 JSObject *l = &lval.toObject(), *r = &rval.toObject(); \
3434 l->assertSpecialEqualitySynced(); \
3435 if (EqualityOp eq = l->getClass()->ext.equality) { \
3436 if (!eq(cx, l, &rval, &cond)) \
3438 cond = cond OP JS_TRUE; \
3443 cond = lval.payloadAsRawUint32() OP rval.payloadAsRawUint32();\
3446 if (lval.isNullOrUndefined()) { \
3447 cond = rval.isNullOrUndefined() OP true; \
3448 } else if (rval.isNullOrUndefined()) { \
3449 cond = true OP false; \
3451 if (lval.isObject()) \
3452 DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval); \
3453 if (rval.isObject()) \
3454 DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); \
3455 if (lval.isString() && rval.isString()) { \
3456 JSString *l = lval.toString(), *r = rval.toString(); \
3458 if (!EqualStrings(cx, l, r, &equal)) \
3460 cond = equal OP JS_TRUE; \
3463 if (!ValueToNumber(cx, lval, &l) || \
3464 !ValueToNumber(cx, rval, &r)) { \
3467 cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN); \
3471 TRY_BRANCH_AFTER_COND(cond, 2); \
3473 regs.sp[-1].setBoolean(cond); \
3477 EQUALITY_OP(==, false);
3481 EQUALITY_OP(!=, true);
3485 #undef XML_EQUALITY_OP
3486 #undef EXTENDED_EQUALITY_OP
3488 #define STRICT_EQUALITY_OP(OP, COND) \
3490 const Value &rref = regs.sp[-1]; \
3491 const Value &lref = regs.sp[-2]; \
3493 if (!StrictlyEqual(cx, lref, rref, &equal)) \
3495 COND = equal OP JS_TRUE; \
3499 BEGIN_CASE(JSOP_STRICTEQ
)
3502 STRICT_EQUALITY_OP(==, cond
);
3503 regs
.sp
[-1].setBoolean(cond
);
3505 END_CASE(JSOP_STRICTEQ
)
3507 BEGIN_CASE(JSOP_STRICTNE
)
3510 STRICT_EQUALITY_OP(!=, cond
);
3511 regs
.sp
[-1].setBoolean(cond
);
3513 END_CASE(JSOP_STRICTNE
)
3515 BEGIN_CASE(JSOP_CASE
)
3518 STRICT_EQUALITY_OP(==, cond
);
3521 len
= GET_JUMP_OFFSET(regs
.pc
);
3527 BEGIN_CASE(JSOP_CASEX
)
3530 STRICT_EQUALITY_OP(==, cond
);
3533 len
= GET_JUMPX_OFFSET(regs
.pc
);
3537 END_CASE(JSOP_CASEX
)
3539 #undef STRICT_EQUALITY_OP
3541 #define RELATIONAL_OP(OP) \
3543 Value rval = regs.sp[-1]; \
3544 Value lval = regs.sp[-2]; \
3546 /* Optimize for two int-tagged operands (typical loop control). */ \
3547 if (lval.isInt32() && rval.isInt32()) { \
3548 cond = lval.toInt32() OP rval.toInt32(); \
3550 if (lval.isObject()) \
3551 DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \
3552 if (rval.isObject()) \
3553 DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \
3554 if (lval.isString() && rval.isString()) { \
3555 JSString *l = lval.toString(), *r = rval.toString(); \
3557 if (!CompareStrings(cx, l, r, &result)) \
3559 cond = result OP 0; \
3562 if (!ValueToNumber(cx, lval, &l) || \
3563 !ValueToNumber(cx, rval, &r)) { \
3566 cond = JSDOUBLE_COMPARE(l, OP, r, false); \
3569 TRY_BRANCH_AFTER_COND(cond, 2); \
3571 regs.sp[-1].setBoolean(cond); \
3590 #undef RELATIONAL_OP
3592 #define SIGNED_SHIFT_OP(OP) \
3595 if (!ValueToECMAInt32(cx, regs.sp[-2], &i)) \
3597 if (!ValueToECMAInt32(cx, regs.sp[-1], &j)) \
3599 i = i OP (j & 31); \
3601 regs.sp[-1].setInt32(i); \
3604 BEGIN_CASE(JSOP_LSH
)
3605 SIGNED_SHIFT_OP(<<);
3608 BEGIN_CASE(JSOP_RSH
)
3609 SIGNED_SHIFT_OP(>>);
3612 #undef SIGNED_SHIFT_OP
3614 BEGIN_CASE(JSOP_URSH
)
3617 if (!ValueToECMAUint32(cx
, regs
.sp
[-2], &u
))
3620 if (!ValueToECMAInt32(cx
, regs
.sp
[-1], &j
))
3626 regs
.sp
[-1].setNumber(uint32(u
));
3630 BEGIN_CASE(JSOP_ADD
)
3632 Value rval
= regs
.sp
[-1];
3633 Value lval
= regs
.sp
[-2];
3635 if (lval
.isInt32() && rval
.isInt32()) {
3636 int32_t l
= lval
.toInt32(), r
= rval
.toInt32();
3637 int32_t sum
= l
+ r
;
3639 if (JS_UNLIKELY(bool((l
^ sum
) & (r
^ sum
) & 0x80000000)))
3640 regs
.sp
[-1].setDouble(double(l
) + double(r
));
3642 regs
.sp
[-1].setInt32(sum
);
3644 #if JS_HAS_XML_SUPPORT
3645 if (IsXML(lval
) && IsXML(rval
)) {
3646 if (!js_ConcatenateXML(cx
, &lval
.toObject(), &rval
.toObject(), &rval
))
3653 if (lval
.isObject())
3654 DEFAULT_VALUE(cx
, -2, JSTYPE_VOID
, lval
);
3655 if (rval
.isObject())
3656 DEFAULT_VALUE(cx
, -1, JSTYPE_VOID
, rval
);
3657 bool lIsString
, rIsString
;
3658 if ((lIsString
= lval
.isString()) | (rIsString
= rval
.isString())) {
3659 JSString
*lstr
, *rstr
;
3661 lstr
= lval
.toString();
3663 lstr
= js_ValueToString(cx
, lval
);
3666 regs
.sp
[-2].setString(lstr
);
3669 rstr
= rval
.toString();
3671 rstr
= js_ValueToString(cx
, rval
);
3674 regs
.sp
[-1].setString(rstr
);
3676 JSString
*str
= js_ConcatStrings(cx
, lstr
, rstr
);
3680 regs
.sp
[-1].setString(str
);
3683 if (!ValueToNumber(cx
, lval
, &l
) || !ValueToNumber(cx
, rval
, &r
))
3687 regs
.sp
[-1].setNumber(l
);
3693 #define BINARY_OP(OP) \
3696 if (!ValueToNumber(cx, regs.sp[-2], &d1) || \
3697 !ValueToNumber(cx, regs.sp[-1], &d2)) { \
3700 double d = d1 OP d2; \
3702 regs.sp[-1].setNumber(d); \
3705 BEGIN_CASE(JSOP_SUB
)
3709 BEGIN_CASE(JSOP_MUL
)
3715 BEGIN_CASE(JSOP_DIV
)
3718 if (!ValueToNumber(cx
, regs
.sp
[-2], &d1
) ||
3719 !ValueToNumber(cx
, regs
.sp
[-1], &d2
)) {
3726 /* XXX MSVC miscompiles such that (NaN == 0) */
3727 if (JSDOUBLE_IS_NaN(d2
))
3731 if (d1
== 0 || JSDOUBLE_IS_NaN(d1
))
3733 else if (JSDOUBLE_IS_NEG(d1
) != JSDOUBLE_IS_NEG(d2
))
3734 vp
= &rt
->negativeInfinityValue
;
3736 vp
= &rt
->positiveInfinityValue
;
3740 regs
.sp
[-1].setNumber(d1
);
3745 BEGIN_CASE(JSOP_MOD
)
3747 Value
&lref
= regs
.sp
[-2];
3748 Value
&rref
= regs
.sp
[-1];
3750 if (lref
.isInt32() && rref
.isInt32() &&
3751 (l
= lref
.toInt32()) >= 0 && (r
= rref
.toInt32()) > 0) {
3752 int32_t mod
= l
% r
;
3754 regs
.sp
[-1].setInt32(mod
);
3757 if (!ValueToNumber(cx
, regs
.sp
[-2], &d1
) ||
3758 !ValueToNumber(cx
, regs
.sp
[-1], &d2
)) {
3763 regs
.sp
[-1].setDouble(js_NaN
);
3765 d1
= js_fmod(d1
, d2
);
3766 regs
.sp
[-1].setDouble(d1
);
3772 BEGIN_CASE(JSOP_NOT
)
3776 POP_BOOLEAN(cx
, _
, cond
);
3777 PUSH_BOOLEAN(!cond
);
3781 BEGIN_CASE(JSOP_BITNOT
)
3784 if (!ValueToECMAInt32(cx
, regs
.sp
[-1], &i
))
3787 regs
.sp
[-1].setInt32(i
);
3789 END_CASE(JSOP_BITNOT
)
3791 BEGIN_CASE(JSOP_NEG
)
3794 * When the operand is int jsval, INT32_FITS_IN_JSVAL(i) implies
3795 * INT32_FITS_IN_JSVAL(-i) unless i is 0 or INT32_MIN when the
3796 * results, -0.0 or INT32_MAX + 1, are jsdouble values.
3798 const Value
&ref
= regs
.sp
[-1];
3800 if (ref
.isInt32() && (i
= ref
.toInt32()) != 0 && i
!= INT32_MIN
) {
3802 regs
.sp
[-1].setInt32(i
);
3805 if (!ValueToNumber(cx
, regs
.sp
[-1], &d
))
3808 regs
.sp
[-1].setDouble(d
);
3813 BEGIN_CASE(JSOP_POS
)
3814 if (!ValueToNumber(cx
, ®s
.sp
[-1]))
3818 BEGIN_CASE(JSOP_DELNAME
)
3822 jsid id
= ATOM_TO_JSID(atom
);
3823 JSObject
*obj
, *obj2
;
3825 if (!js_FindProperty(cx
, id
, &obj
, &obj2
, &prop
))
3828 /* Strict mode code should never contain JSOP_DELNAME opcodes. */
3829 JS_ASSERT(!script
->strictModeCode
);
3831 /* ECMA says to return true if name is undefined or inherited. */
3834 if (!obj
->deleteProperty(cx
, id
, ®s
.sp
[-1], false))
3838 END_CASE(JSOP_DELNAME
)
3840 BEGIN_CASE(JSOP_DELPROP
)
3844 jsid id
= ATOM_TO_JSID(atom
);
3847 FETCH_OBJECT(cx
, -1, obj
);
3850 if (!obj
->deleteProperty(cx
, id
, &rval
, script
->strictModeCode
))
3855 END_CASE(JSOP_DELPROP
)
3857 BEGIN_CASE(JSOP_DELELEM
)
3859 /* Fetch the left part and resolve it to a non-null object. */
3861 FETCH_OBJECT(cx
, -2, obj
);
3863 /* Fetch index and convert it to id suitable for use with obj. */
3865 FETCH_ELEMENT_ID(obj
, -1, id
);
3867 /* Get or set the element. */
3868 if (!obj
->deleteProperty(cx
, id
, ®s
.sp
[-2], script
->strictModeCode
))
3873 END_CASE(JSOP_DELELEM
)
3875 BEGIN_CASE(JSOP_TYPEOFEXPR
)
3876 BEGIN_CASE(JSOP_TYPEOF
)
3878 const Value
&ref
= regs
.sp
[-1];
3879 JSType type
= JS_TypeOfValue(cx
, Jsvalify(ref
));
3880 JSAtom
*atom
= rt
->atomState
.typeAtoms
[type
];
3881 regs
.sp
[-1].setString(ATOM_TO_STRING(atom
));
3883 END_CASE(JSOP_TYPEOF
)
3885 BEGIN_CASE(JSOP_VOID
)
3886 regs
.sp
[-1].setUndefined();
3895 BEGIN_CASE(JSOP_INCELEM
)
3896 BEGIN_CASE(JSOP_DECELEM
)
3897 BEGIN_CASE(JSOP_ELEMINC
)
3898 BEGIN_CASE(JSOP_ELEMDEC
)
3901 * Delay fetching of id until we have the object to ensure the proper
3902 * evaluation order. See bug 372331.
3906 goto fetch_incop_obj
;
3908 BEGIN_CASE(JSOP_INCPROP
)
3909 BEGIN_CASE(JSOP_DECPROP
)
3910 BEGIN_CASE(JSOP_PROPINC
)
3911 BEGIN_CASE(JSOP_PROPDEC
)
3913 id
= ATOM_TO_JSID(atom
);
3917 FETCH_OBJECT(cx
, i
, obj
);
3918 if (JSID_IS_VOID(id
))
3919 FETCH_ELEMENT_ID(obj
, -1, id
);
3922 BEGIN_CASE(JSOP_INCNAME
)
3923 BEGIN_CASE(JSOP_DECNAME
)
3924 BEGIN_CASE(JSOP_NAMEINC
)
3925 BEGIN_CASE(JSOP_NAMEDEC
)
3926 BEGIN_CASE(JSOP_INCGNAME
)
3927 BEGIN_CASE(JSOP_DECGNAME
)
3928 BEGIN_CASE(JSOP_GNAMEINC
)
3929 BEGIN_CASE(JSOP_GNAMEDEC
)
3931 obj
= ®s
.fp
->scopeChain();
3932 if (js_CodeSpec
[op
].format
& JOF_GNAME
)
3933 obj
= obj
->getGlobal();
3936 PropertyCacheEntry
*entry
;
3937 JS_PROPERTY_CACHE(cx
).test(cx
, regs
.pc
, obj
, obj2
, entry
, atom
);
3939 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj
, obj2
, entry
);
3940 if (obj
== obj2
&& entry
->vword
.isSlot()) {
3941 uint32 slot
= entry
->vword
.toSlot();
3942 Value
&rref
= obj
->nativeGetSlotRef(slot
);
3944 if (JS_LIKELY(rref
.isInt32() && CanIncDecWithoutOverflow(tmp
= rref
.toInt32()))) {
3945 int32_t inc
= tmp
+ ((js_CodeSpec
[op
].format
& JOF_INC
) ? 1 : -1);
3946 if (!(js_CodeSpec
[op
].format
& JOF_POST
))
3948 rref
.getInt32Ref() = inc
;
3950 len
= JSOP_INCNAME_LENGTH
;
3957 id
= ATOM_TO_JSID(atom
);
3959 if (!js_FindPropertyHelper(cx
, id
, true, &obj
, &obj2
, &prop
))
3962 atomNotDefined
= atom
;
3963 goto atom_not_defined
;
3970 * We need a root to store the value to leave on the stack until
3971 * we have done with obj->setProperty.
3974 if (!obj
->getProperty(cx
, id
, ®s
.sp
[-1]))
3977 const JSCodeSpec
*cs
= &js_CodeSpec
[op
];
3978 JS_ASSERT(cs
->ndefs
== 1);
3979 JS_ASSERT((cs
->format
& JOF_TMPSLOT_MASK
) >= JOF_TMPSLOT2
);
3980 Value
&ref
= regs
.sp
[-1];
3982 if (JS_LIKELY(ref
.isInt32() && CanIncDecWithoutOverflow(tmp
= ref
.toInt32()))) {
3983 int incr
= (cs
->format
& JOF_INC
) ? 1 : -1;
3984 if (cs
->format
& JOF_POST
)
3985 ref
.getInt32Ref() = tmp
+ incr
;
3987 ref
.getInt32Ref() = tmp
+= incr
;
3988 regs
.fp
->setAssigning();
3989 JSBool ok
= obj
->setProperty(cx
, id
, &ref
, script
->strictModeCode
);
3990 regs
.fp
->clearAssigning();
3995 * We must set regs.sp[-1] to tmp for both post and pre increments
3996 * as the setter overwrites regs.sp[-1].
4000 /* We need an extra root for the result. */
4002 if (!js_DoIncDec(cx
, cs
, ®s
.sp
[-2], ®s
.sp
[-1]))
4004 regs
.fp
->setAssigning();
4005 JSBool ok
= obj
->setProperty(cx
, id
, ®s
.sp
[-1], script
->strictModeCode
);
4006 regs
.fp
->clearAssigning();
4012 if (cs
->nuses
== 0) {
4013 /* regs.sp[-1] already contains the result of name increment. */
4015 regs
.sp
[-1 - cs
->nuses
] = regs
.sp
[-1];
4016 regs
.sp
-= cs
->nuses
;
4028 /* Position cases so the most frequent i++ does not need a jump. */
4029 BEGIN_CASE(JSOP_DECARG
)
4030 incr
= -1; incr2
= -1; goto do_arg_incop
;
4031 BEGIN_CASE(JSOP_ARGDEC
)
4032 incr
= -1; incr2
= 0; goto do_arg_incop
;
4033 BEGIN_CASE(JSOP_INCARG
)
4034 incr
= 1; incr2
= 1; goto do_arg_incop
;
4035 BEGIN_CASE(JSOP_ARGINC
)
4036 incr
= 1; incr2
= 0;
4039 slot
= GET_ARGNO(regs
.pc
);
4040 JS_ASSERT(slot
< regs
.fp
->numFormalArgs());
4041 METER_SLOT_OP(op
, slot
);
4043 goto do_int_fast_incop
;
4045 BEGIN_CASE(JSOP_DECLOCAL
)
4046 incr
= -1; incr2
= -1; goto do_local_incop
;
4047 BEGIN_CASE(JSOP_LOCALDEC
)
4048 incr
= -1; incr2
= 0; goto do_local_incop
;
4049 BEGIN_CASE(JSOP_INCLOCAL
)
4050 incr
= 1; incr2
= 1; goto do_local_incop
;
4051 BEGIN_CASE(JSOP_LOCALINC
)
4052 incr
= 1; incr2
= 0;
4055 * do_local_incop comes right before do_int_fast_incop as we want to
4056 * avoid an extra jump for variable cases as local++ is more frequent
4060 slot
= GET_SLOTNO(regs
.pc
);
4061 JS_ASSERT(slot
< regs
.fp
->numSlots());
4062 METER_SLOT_OP(op
, slot
);
4063 vp
= regs
.fp
->slots() + slot
;
4067 if (JS_LIKELY(vp
->isInt32() && CanIncDecWithoutOverflow(tmp
= vp
->toInt32()))) {
4068 vp
->getInt32Ref() = tmp
+ incr
;
4069 JS_ASSERT(JSOP_INCARG_LENGTH
== js_CodeSpec
[op
].length
);
4070 SKIP_POP_AFTER_SET(JSOP_INCARG_LENGTH
, 0);
4071 PUSH_INT32(tmp
+ incr2
);
4074 if (!js_DoIncDec(cx
, &js_CodeSpec
[op
], ®s
.sp
[-1], vp
))
4077 len
= JSOP_INCARG_LENGTH
;
4078 JS_ASSERT(len
== js_CodeSpec
[op
].length
);
4082 BEGIN_CASE(JSOP_THIS
)
4083 if (!regs
.fp
->computeThis(cx
))
4085 PUSH_COPY(regs
.fp
->thisValue());
4088 BEGIN_CASE(JSOP_UNBRANDTHIS
)
4090 if (!regs
.fp
->computeThis(cx
))
4092 Value
&thisv
= regs
.fp
->thisValue();
4093 if (thisv
.isObject()) {
4094 JSObject
*obj
= &thisv
.toObject();
4095 if (obj
->isNative())
4099 END_CASE(JSOP_UNBRANDTHIS
)
4106 BEGIN_CASE(JSOP_GETTHISPROP
)
4107 if (!regs
.fp
->computeThis(cx
))
4110 PUSH_COPY(regs
.fp
->thisValue());
4111 goto do_getprop_body
;
4113 BEGIN_CASE(JSOP_GETARGPROP
)
4116 uint32 slot
= GET_ARGNO(regs
.pc
);
4117 JS_ASSERT(slot
< regs
.fp
->numFormalArgs());
4118 PUSH_COPY(argv
[slot
]);
4119 goto do_getprop_body
;
4122 BEGIN_CASE(JSOP_GETLOCALPROP
)
4125 uint32 slot
= GET_SLOTNO(regs
.pc
);
4126 JS_ASSERT(slot
< script
->nslots
);
4127 PUSH_COPY(regs
.fp
->slots()[slot
]);
4128 goto do_getprop_body
;
4131 BEGIN_CASE(JSOP_GETPROP
)
4132 BEGIN_CASE(JSOP_GETXPROP
)
4138 do_getprop_with_lval
:
4139 VALUE_TO_OBJECT(cx
, vp
, obj
);
4145 * We do not impose the method read barrier if in an imacro,
4146 * assuming any property gets it does (e.g., for 'toString'
4147 * from JSOP_NEW) will not be leaked to the calling script.
4149 JSObject
*aobj
= js_GetProtoIfDenseArray(obj
);
4151 PropertyCacheEntry
*entry
;
4154 JS_PROPERTY_CACHE(cx
).test(cx
, regs
.pc
, aobj
, obj2
, entry
, atom
);
4156 ASSERT_VALID_PROPERTY_CACHE_HIT(i
, aobj
, obj2
, entry
);
4157 if (entry
->vword
.isFunObj()) {
4158 rval
.setObject(entry
->vword
.toFunObj());
4159 } else if (entry
->vword
.isSlot()) {
4160 uint32 slot
= entry
->vword
.toSlot();
4161 rval
= obj2
->nativeGetSlot(slot
);
4163 JS_ASSERT(entry
->vword
.isShape());
4164 const Shape
*shape
= entry
->vword
.toShape();
4165 NATIVE_GET(cx
, obj
, obj2
, shape
,
4166 regs
.fp
->hasImacropc() ? JSGET_NO_METHOD_BARRIER
: JSGET_METHOD_BARRIER
,
4172 jsid id
= ATOM_TO_JSID(atom
);
4173 if (JS_LIKELY(!aobj
->getOps()->getProperty
)
4174 ? !js_GetPropertyHelper(cx
, obj
, id
,
4175 (regs
.fp
->hasImacropc() ||
4176 regs
.pc
[JSOP_GETPROP_LENGTH
+ i
] == JSOP_IFEQ
)
4177 ? JSGET_CACHE_RESULT
| JSGET_NO_METHOD_BARRIER
4178 : JSGET_CACHE_RESULT
| JSGET_METHOD_BARRIER
,
4180 : !obj
->getProperty(cx
, id
, &rval
)) {
4186 assertSameCompartment(cx
, regs
.sp
[-1]);
4187 JS_ASSERT(JSOP_GETPROP_LENGTH
+ i
== js_CodeSpec
[op
].length
);
4188 len
= JSOP_GETPROP_LENGTH
+ i
;
4192 BEGIN_CASE(JSOP_LENGTH
)
4194 if (vp
->isString()) {
4195 vp
->setInt32(vp
->toString()->length());
4196 } else if (vp
->isObject()) {
4197 JSObject
*obj
= &vp
->toObject();
4198 if (obj
->isArray()) {
4199 jsuint length
= obj
->getArrayLength();
4200 regs
.sp
[-1].setNumber(length
);
4201 } else if (obj
->isArguments() && !obj
->isArgsLengthOverridden()) {
4202 uint32 length
= obj
->getArgsInitialLength();
4203 JS_ASSERT(length
< INT32_MAX
);
4204 regs
.sp
[-1].setInt32(int32_t(length
));
4207 goto do_getprop_with_lval
;
4211 goto do_getprop_with_lval
;
4213 END_CASE(JSOP_LENGTH
)
4217 BEGIN_CASE(JSOP_CALLPROP
)
4219 Value lval
= regs
.sp
[-1];
4222 if (lval
.isObject()) {
4225 JSProtoKey protoKey
;
4226 if (lval
.isString()) {
4227 protoKey
= JSProto_String
;
4228 } else if (lval
.isNumber()) {
4229 protoKey
= JSProto_Number
;
4230 } else if (lval
.isBoolean()) {
4231 protoKey
= JSProto_Boolean
;
4233 JS_ASSERT(lval
.isNull() || lval
.isUndefined());
4234 js_ReportIsNullOrUndefined(cx
, -1, lval
, NULL
);
4238 if (!js_GetClassPrototype(cx
, NULL
, protoKey
, &pobj
))
4240 objv
.setObject(*pobj
);
4243 JSObject
*aobj
= js_GetProtoIfDenseArray(&objv
.toObject());
4246 PropertyCacheEntry
*entry
;
4249 JS_PROPERTY_CACHE(cx
).test(cx
, regs
.pc
, aobj
, obj2
, entry
, atom
);
4251 ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj
, obj2
, entry
);
4252 if (entry
->vword
.isFunObj()) {
4253 rval
.setObject(entry
->vword
.toFunObj());
4254 } else if (entry
->vword
.isSlot()) {
4255 uint32 slot
= entry
->vword
.toSlot();
4256 rval
= obj2
->nativeGetSlot(slot
);
4258 JS_ASSERT(entry
->vword
.isShape());
4259 const Shape
*shape
= entry
->vword
.toShape();
4260 NATIVE_GET(cx
, &objv
.toObject(), obj2
, shape
, JSGET_NO_METHOD_BARRIER
, &rval
);
4263 assertSameCompartment(cx
, regs
.sp
[-1]);
4267 * Cache miss: use the immediate atom that was loaded for us under
4268 * PropertyCache::test.
4271 id
= ATOM_TO_JSID(atom
);
4274 if (lval
.isObject()) {
4275 if (!js_GetMethod(cx
, &objv
.toObject(), id
,
4276 JS_LIKELY(!aobj
->getOps()->getProperty
)
4277 ? JSGET_CACHE_RESULT
| JSGET_NO_METHOD_BARRIER
4278 : JSGET_NO_METHOD_BARRIER
,
4284 assertSameCompartment(cx
, regs
.sp
[-1], regs
.sp
[-2]);
4286 JS_ASSERT(!objv
.toObject().getOps()->getProperty
);
4287 if (!js_GetPropertyHelper(cx
, &objv
.toObject(), id
,
4288 JSGET_CACHE_RESULT
| JSGET_NO_METHOD_BARRIER
,
4294 assertSameCompartment(cx
, regs
.sp
[-1], regs
.sp
[-2]);
4297 #if JS_HAS_NO_SUCH_METHOD
4298 if (JS_UNLIKELY(rval
.isUndefined()) && regs
.sp
[-1].isObject()) {
4300 regs
.sp
[-2].setString(ATOM_TO_STRING(atom
));
4301 if (!js_OnUnknownMethod(cx
, regs
.sp
- 2))
4306 END_CASE(JSOP_CALLPROP
)
4308 BEGIN_CASE(JSOP_UNBRAND
)
4309 JS_ASSERT(regs
.sp
- regs
.fp
->slots() >= 1);
4310 regs
.sp
[-1].toObject().unbrand(cx
);
4311 END_CASE(JSOP_UNBRAND
)
4313 BEGIN_CASE(JSOP_SETGNAME
)
4314 BEGIN_CASE(JSOP_SETNAME
)
4315 BEGIN_CASE(JSOP_SETPROP
)
4316 BEGIN_CASE(JSOP_SETMETHOD
)
4318 Value rval
= regs
.sp
[-1];
4319 JS_ASSERT_IF(op
== JSOP_SETMETHOD
, IsFunctionObject(rval
));
4320 Value
&lref
= regs
.sp
[-2];
4321 JS_ASSERT_IF(op
== JSOP_SETNAME
, lref
.isObject());
4323 VALUE_TO_OBJECT(cx
, &lref
, obj
);
4325 JS_ASSERT_IF(op
== JSOP_SETGNAME
, obj
== regs
.fp
->scopeChain().getGlobal());
4328 PropertyCache
*cache
= &JS_PROPERTY_CACHE(cx
);
4331 * Probe the property cache, specializing for two important
4332 * set-property cases. First:
4334 * function f(a, b, c) {
4335 * var o = {p:a, q:b, r:c};
4339 * or similar real-world cases, which evolve a newborn native
4340 * object predicatably through some bounded number of property
4341 * additions. And second:
4345 * in a frequently executed method or loop body, where p will
4346 * (possibly after the first iteration) always exist in native
4349 PropertyCacheEntry
*entry
;
4352 if (cache
->testForSet(cx
, regs
.pc
, obj
, &entry
, &obj2
, &atom
)) {
4354 * Property cache hit, only partially confirmed by testForSet. We
4355 * know that the entry applies to regs.pc and that obj's shape
4358 * The entry predicts either a new property to be added directly to
4359 * obj by this set, or on an existing "own" property, or on a
4360 * prototype property that has a setter.
4362 const Shape
*shape
= entry
->vword
.toShape();
4363 JS_ASSERT_IF(shape
->isDataDescriptor(), shape
->writable());
4364 JS_ASSERT_IF(shape
->hasSlot(), entry
->vcapTag() == 0);
4367 * Fastest path: check whether obj already has the cached shape and
4368 * call NATIVE_SET and break to get out of the do-while(0). But we
4369 * can call NATIVE_SET only for a direct or proto-setter hit.
4371 if (!entry
->adding()) {
4372 if (entry
->vcapTag() == 0 ||
4373 ((obj2
= obj
->getProto()) && obj2
->shape() == entry
->vshape()))
4376 if (entry
->directHit()) {
4377 JS_ASSERT(obj
->nativeContains(*shape
));
4379 JS_ASSERT(obj2
->nativeContains(*shape
));
4380 JS_ASSERT(entry
->vcapTag() == 1);
4381 JS_ASSERT(entry
->kshape
!= entry
->vshape());
4382 JS_ASSERT(!shape
->hasSlot());
4386 PCMETER(cache
->pchits
++);
4387 PCMETER(cache
->setpchits
++);
4388 NATIVE_SET(cx
, obj
, shape
, entry
, script
->strictModeCode
, &rval
);
4392 JS_ASSERT(obj
->isExtensible());
4394 if (obj
->nativeEmpty()) {
4395 if (!obj
->ensureClassReservedSlotsForEmptyObject(cx
))
4400 if (shape
->previous() == obj
->lastProperty() &&
4401 entry
->vshape() == rt
->protoHazardShape
&&
4402 shape
->hasDefaultSetter()) {
4404 JS_ASSERT(slot
== obj
->slotSpan());
4407 * Fast path: adding a plain old property that was once at
4408 * the frontier of the property tree, whose slot is next to
4409 * claim among the already-allocated slots in obj, where
4410 * shape->table has not been created yet.
4412 PCMETER(cache
->pchits
++);
4413 PCMETER(cache
->addpchits
++);
4415 if (slot
< obj
->numSlots()) {
4416 JS_ASSERT(obj
->getSlot(slot
).isUndefined());
4418 if (!obj
->allocSlot(cx
, &slot
))
4420 JS_ASSERT(slot
== shape
->slot
);
4423 /* Simply extend obj's property tree path with shape! */
4424 obj
->extend(cx
, shape
);
4427 * No method change check here because here we are adding a
4428 * new property, not updating an existing slot's value that
4429 * might contain a method of a branded shape.
4431 TRACE_1(AddProperty
, obj
);
4432 obj
->nativeSetSlot(slot
, rval
);
4435 * Purge the property cache of the id we may have just
4436 * shadowed in obj's scope and proto chains.
4438 js_PurgeScopeChain(cx
, obj
, shape
->id
);
4442 PCMETER(cache
->setpcmisses
++);
4449 jsid id
= ATOM_TO_JSID(atom
);
4450 if (entry
&& JS_LIKELY(!obj
->getOps()->setProperty
)) {
4452 if (op
== JSOP_SETMETHOD
)
4453 defineHow
= JSDNP_CACHE_RESULT
| JSDNP_SET_METHOD
;
4454 else if (op
== JSOP_SETNAME
)
4455 defineHow
= JSDNP_CACHE_RESULT
| JSDNP_UNQUALIFIED
;
4457 defineHow
= JSDNP_CACHE_RESULT
;
4458 if (!js_SetPropertyHelper(cx
, obj
, id
, defineHow
, &rval
, script
->strictModeCode
))
4461 if (!obj
->setProperty(cx
, id
, &rval
, script
->strictModeCode
))
4463 ABORT_RECORDING(cx
, "Non-native set");
4467 END_SET_CASE_STORE_RVAL(JSOP_SETPROP
, 2);
4469 BEGIN_CASE(JSOP_GETELEM
)
4471 Value
&lref
= regs
.sp
[-2];
4472 Value
&rref
= regs
.sp
[-1];
4473 if (lref
.isString() && rref
.isInt32()) {
4474 JSString
*str
= lref
.toString();
4475 int32_t i
= rref
.toInt32();
4476 if (size_t(i
) < str
->length()) {
4477 str
= JSString::getUnitString(cx
, str
, size_t(i
));
4481 regs
.sp
[-1].setString(str
);
4482 len
= JSOP_GETELEM_LENGTH
;
4488 VALUE_TO_OBJECT(cx
, &lref
, obj
);
4490 const Value
*copyFrom
;
4493 if (rref
.isInt32()) {
4494 int32_t i
= rref
.toInt32();
4495 if (obj
->isDenseArray()) {
4496 jsuint idx
= jsuint(i
);
4498 if (idx
< obj
->getDenseArrayCapacity()) {
4499 copyFrom
= obj
->addressOfDenseArrayElement(idx
);
4500 if (!copyFrom
->isMagic())
4503 } else if (obj
->isArguments()) {
4504 uint32 arg
= uint32(i
);
4506 if (arg
< obj
->getArgsInitialLength()) {
4507 copyFrom
= obj
->addressOfArgsElement(arg
);
4508 if (!copyFrom
->isMagic(JS_ARGS_HOLE
)) {
4509 if (JSStackFrame
*afp
= (JSStackFrame
*) obj
->getPrivate())
4510 copyFrom
= &afp
->canonicalActualArg(arg
);
4515 if (JS_LIKELY(INT_FITS_IN_JSID(i
)))
4516 id
= INT_TO_JSID(i
);
4518 goto intern_big_int
;
4521 if (ValueFitsInInt32(rref
, &i
) && INT_FITS_IN_JSID(i
)) {
4522 id
= INT_TO_JSID(i
);
4525 if (!js_InternNonIntElementId(cx
, obj
, rref
, &id
))
4530 if (!obj
->getProperty(cx
, id
, &rval
))
4536 regs
.sp
[-1] = *copyFrom
;
4537 assertSameCompartment(cx
, regs
.sp
[-1]);
4539 END_CASE(JSOP_GETELEM
)
4541 BEGIN_CASE(JSOP_CALLELEM
)
4543 /* Find the object on which to look for |this|'s properties. */
4544 Value thisv
= regs
.sp
[-2];
4545 JSObject
*thisObj
= ValuePropertyBearer(cx
, thisv
, -2);
4549 /* Fetch index and convert it to id suitable for use with obj. */
4551 FETCH_ELEMENT_ID(thisObj
, -1, id
);
4553 /* Get the method. */
4554 if (!js_GetMethod(cx
, thisObj
, id
, JSGET_NO_METHOD_BARRIER
, ®s
.sp
[-2]))
4557 #if JS_HAS_NO_SUCH_METHOD
4558 if (JS_UNLIKELY(regs
.sp
[-2].isUndefined()) && thisv
.isObject()) {
4559 /* For js_OnUnknownMethod, sp[-2] is the index, and sp[-1] is the object missing it. */
4560 regs
.sp
[-2] = regs
.sp
[-1];
4561 regs
.sp
[-1].setObject(*thisObj
);
4562 if (!js_OnUnknownMethod(cx
, regs
.sp
- 2))
4567 regs
.sp
[-1] = thisv
;
4570 END_CASE(JSOP_CALLELEM
)
4572 BEGIN_CASE(JSOP_SETELEM
)
4575 FETCH_OBJECT(cx
, -3, obj
);
4577 FETCH_ELEMENT_ID(obj
, -2, id
);
4580 if (obj
->isDenseArray() && JSID_IS_INT(id
)) {
4581 jsuint length
= obj
->getDenseArrayCapacity();
4582 jsint i
= JSID_TO_INT(id
);
4583 if ((jsuint
)i
< length
) {
4584 if (obj
->getDenseArrayElement(i
).isMagic(JS_ARRAY_HOLE
)) {
4585 if (js_PrototypeHasIndexedProperties(cx
, obj
))
4587 if ((jsuint
)i
>= obj
->getArrayLength())
4588 obj
->setArrayLength(i
+ 1);
4590 obj
->setDenseArrayElement(i
, regs
.sp
[-1]);
4596 if (!obj
->setProperty(cx
, id
, &rval
, script
->strictModeCode
))
4600 END_SET_CASE_STORE_RVAL(JSOP_SETELEM
, 3)
4602 BEGIN_CASE(JSOP_ENUMELEM
)
4604 /* Funky: the value to set is under the [obj, id] pair. */
4606 FETCH_OBJECT(cx
, -2, obj
);
4608 FETCH_ELEMENT_ID(obj
, -1, id
);
4609 Value rval
= regs
.sp
[-3];
4610 if (!obj
->setProperty(cx
, id
, &rval
, script
->strictModeCode
))
4614 END_CASE(JSOP_ENUMELEM
)
4616 { // begin block around calling opcodes
4623 BEGIN_CASE(JSOP_NEW
)
4625 /* Get immediate argc and find the constructor function. */
4626 argc
= GET_ARGC(regs
.pc
);
4627 vp
= regs
.sp
- (2 + argc
);
4628 JS_ASSERT(vp
>= regs
.fp
->base());
4631 * Assign lval, callee, and newfun exactly as the code at inline_call: expects to
4632 * find them, to avoid nesting a js_Interpret call via js_InvokeConstructor.
4634 if (IsFunctionObject(vp
[0], &callee
)) {
4635 newfun
= callee
->getFunctionPrivate();
4636 if (newfun
->isInterpreted()) {
4637 if (newfun
->u
.i
.script
->isEmpty()) {
4638 JSObject
*obj2
= js_CreateThisForFunction(cx
, callee
);
4641 vp
[0].setObject(*obj2
);
4646 flags
= JSFRAME_CONSTRUCTING
;
4651 if (!InvokeConstructor(cx
, InvokeArgsAlreadyOnTheStack(vp
, argc
)))
4654 CHECK_INTERRUPT_HANDLER();
4655 TRACE_0(NativeCallComplete
);
4661 BEGIN_CASE(JSOP_EVAL
)
4663 argc
= GET_ARGC(regs
.pc
);
4664 vp
= regs
.sp
- (argc
+ 2);
4666 if (!IsFunctionObject(*vp
, &callee
))
4667 goto call_using_invoke
;
4669 newfun
= callee
->getFunctionPrivate();
4670 if (!IsBuiltinEvalFunction(newfun
))
4671 goto call_using_invoke
;
4673 if (!DirectEval(cx
, newfun
, argc
, vp
))
4678 BEGIN_CASE(JSOP_CALL
)
4679 BEGIN_CASE(JSOP_FUNAPPLY
)
4680 BEGIN_CASE(JSOP_FUNCALL
)
4682 argc
= GET_ARGC(regs
.pc
);
4683 vp
= regs
.sp
- (argc
+ 2);
4685 if (IsFunctionObject(*vp
, &callee
)) {
4686 newfun
= callee
->getFunctionPrivate();
4688 /* Clear frame flags since this is not a constructor call. */
4690 if (newfun
->isInterpreted())
4693 JSScript
*newscript
= newfun
->script();
4694 if (JS_UNLIKELY(newscript
->isEmpty())) {
4700 /* Restrict recursion of lightweight functions. */
4701 if (JS_UNLIKELY(inlineCallCount
>= JS_MAX_INLINE_CALL_COUNT
)) {
4702 js_ReportOverRecursed(cx
);
4706 /* Get pointer to new frame/slots, prepare arguments. */
4707 StackSpace
&stack
= cx
->stack();
4708 JSStackFrame
*newfp
= stack
.getInlineFrame(cx
, regs
.sp
, argc
, newfun
,
4710 if (JS_UNLIKELY(!newfp
))
4713 /* Initialize frame, locals. */
4714 newfp
->initCallFrame(cx
, *callee
, newfun
, argc
, flags
);
4715 SetValueRangeToUndefined(newfp
->slots(), newscript
->nfixed
);
4717 /* Officially push the frame. */
4718 stack
.pushInlineFrame(cx
, newscript
, newfp
, ®s
);
4720 /* Refresh interpreter locals. */
4721 JS_ASSERT(newfp
== regs
.fp
);
4723 argv
= regs
.fp
->formalArgsEnd() - newfun
->nargs
;
4724 atoms
= script
->atomMap
.vector
;
4726 /* Now that the new frame is rooted, maybe create a call object. */
4727 if (newfun
->isHeavyweight() && !js_GetCallObject(cx
, regs
.fp
))
4731 JS_RUNTIME_METER(rt
, inlineCalls
);
4733 TRACE_0(EnterFrame
);
4735 CHECK_INTERRUPT_HANDLER();
4738 /* Try to ensure methods are method JIT'd. */
4739 mjit::CompileRequest request
= (interpMode
== JSINTERP_NORMAL
)
4740 ? mjit::CompileRequest_Interpreter
4741 : mjit::CompileRequest_JIT
;
4742 mjit::CompileStatus status
= mjit::CanMethodJIT(cx
, script
, regs
.fp
, request
);
4743 if (status
== mjit::Compile_Error
)
4745 if (!TRACE_RECORDER(cx
) && !TRACE_PROFILER(cx
) && status
== mjit::Compile_Okay
) {
4746 interpReturnOK
= mjit::JaegerShot(cx
);
4747 CHECK_INTERRUPT_HANDLER();
4752 if (!ScriptPrologue(cx
, regs
.fp
))
4755 CHECK_INTERRUPT_HANDLER();
4757 /* Load first op and dispatch it (safe since JSOP_STOP). */
4758 op
= (JSOp
) *regs
.pc
;
4762 Probes::enterJSFun(cx
, newfun
, script
);
4763 JSBool ok
= CallJSNative(cx
, newfun
->u
.n
.native
, argc
, vp
);
4764 Probes::exitJSFun(cx
, newfun
, script
);
4768 TRACE_0(NativeCallComplete
);
4774 ok
= Invoke(cx
, InvokeArgsAlreadyOnTheStack(vp
, argc
), 0);
4776 CHECK_INTERRUPT_HANDLER();
4779 JS_RUNTIME_METER(rt
, nonInlineCalls
);
4780 TRACE_0(NativeCallComplete
);
4786 } // end block around calling opcodes
4788 BEGIN_CASE(JSOP_SETCALL
)
4790 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_LEFTSIDE_OF_ASS
);
4793 END_CASE(JSOP_SETCALL
)
4795 #define SLOW_PUSH_THISV(cx, obj) \
4798 JSObject *thisp = obj; \
4799 if (!thisp->getParent() || \
4800 (clasp = thisp->getClass()) == &js_CallClass || \
4801 clasp == &js_BlockClass || \
4802 clasp == &js_DeclEnvClass) { \
4803 /* Push the ImplicitThisValue for the Environment Record */ \
4804 /* associated with obj. See ES5 sections 10.2.1.1.6 and */ \
4805 /* 10.2.1.2.6 (ImplicitThisValue) and section 11.2.3 */ \
4806 /* (Function Calls). */ \
4809 thisp = thisp->thisObject(cx); \
4812 PUSH_OBJECT(*thisp); \
4816 BEGIN_CASE(JSOP_GETGNAME
)
4817 BEGIN_CASE(JSOP_CALLGNAME
)
4818 BEGIN_CASE(JSOP_NAME
)
4819 BEGIN_CASE(JSOP_CALLNAME
)
4821 JSObject
*obj
= ®s
.fp
->scopeChain();
4826 PropertyCacheEntry
*entry
;
4829 JS_PROPERTY_CACHE(cx
).test(cx
, regs
.pc
, obj
, obj2
, entry
, atom
);
4831 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj
, obj2
, entry
);
4832 if (entry
->vword
.isFunObj()) {
4833 PUSH_OBJECT(entry
->vword
.toFunObj());
4834 } else if (entry
->vword
.isSlot()) {
4835 uintN slot
= entry
->vword
.toSlot();
4836 PUSH_COPY(obj2
->nativeGetSlot(slot
));
4838 JS_ASSERT(entry
->vword
.isShape());
4839 shape
= entry
->vword
.toShape();
4840 NATIVE_GET(cx
, obj
, obj2
, shape
, JSGET_METHOD_BARRIER
, &rval
);
4845 * Push results, the same as below, but with a prop$ hit there
4846 * is no need to test for the unusual and uncacheable case where
4847 * the caller determines |this|.
4851 JS_ASSERT(!obj
->getParent() ||
4852 (clasp
= obj
->getClass()) == &js_CallClass
||
4853 clasp
== &js_BlockClass
||
4854 clasp
== &js_DeclEnvClass
);
4856 if (op
== JSOP_CALLNAME
|| op
== JSOP_CALLGNAME
)
4858 len
= JSOP_NAME_LENGTH
;
4863 id
= ATOM_TO_JSID(atom
);
4865 if (!js_FindPropertyHelper(cx
, id
, true, &obj
, &obj2
, &prop
))
4868 /* Kludge to allow (typeof foo == "undefined") tests. */
4869 JSOp op2
= js_GetOpcode(cx
, script
, regs
.pc
+ JSOP_NAME_LENGTH
);
4870 if (op2
== JSOP_TYPEOF
) {
4872 len
= JSOP_NAME_LENGTH
;
4875 atomNotDefined
= atom
;
4876 goto atom_not_defined
;
4879 /* Take the slow path if prop was not found in a native object. */
4880 if (!obj
->isNative() || !obj2
->isNative()) {
4881 if (!obj
->getProperty(cx
, id
, &rval
))
4884 shape
= (Shape
*)prop
;
4885 JSObject
*normalized
= obj
;
4886 if (normalized
->getClass() == &js_WithClass
&& !shape
->hasDefaultGetter())
4887 normalized
= js_UnwrapWithObject(cx
, normalized
);
4888 NATIVE_GET(cx
, normalized
, obj2
, shape
, JSGET_METHOD_BARRIER
, &rval
);
4893 /* obj must be on the scope chain, thus not a function. */
4894 if (op
== JSOP_CALLNAME
|| op
== JSOP_CALLGNAME
)
4895 SLOW_PUSH_THISV(cx
, obj
);
4899 BEGIN_CASE(JSOP_UINT16
)
4900 PUSH_INT32((int32_t) GET_UINT16(regs
.pc
));
4901 END_CASE(JSOP_UINT16
)
4903 BEGIN_CASE(JSOP_UINT24
)
4904 PUSH_INT32((int32_t) GET_UINT24(regs
.pc
));
4905 END_CASE(JSOP_UINT24
)
4907 BEGIN_CASE(JSOP_INT8
)
4908 PUSH_INT32(GET_INT8(regs
.pc
));
4911 BEGIN_CASE(JSOP_INT32
)
4912 PUSH_INT32(GET_INT32(regs
.pc
));
4913 END_CASE(JSOP_INT32
)
4915 BEGIN_CASE(JSOP_INDEXBASE
)
4917 * Here atoms can exceed script->atomMap.length as we use atoms as a
4918 * segment register for object literals as well.
4920 atoms
+= GET_INDEXBASE(regs
.pc
);
4921 END_CASE(JSOP_INDEXBASE
)
4923 BEGIN_CASE(JSOP_INDEXBASE1
)
4924 BEGIN_CASE(JSOP_INDEXBASE2
)
4925 BEGIN_CASE(JSOP_INDEXBASE3
)
4926 atoms
+= (op
- JSOP_INDEXBASE1
+ 1) << 16;
4927 END_CASE(JSOP_INDEXBASE3
)
4929 BEGIN_CASE(JSOP_RESETBASE0
)
4930 BEGIN_CASE(JSOP_RESETBASE
)
4931 atoms
= script
->atomMap
.vector
;
4932 END_CASE(JSOP_RESETBASE
)
4934 BEGIN_CASE(JSOP_DOUBLE
)
4936 JS_ASSERT(!regs
.fp
->hasImacropc());
4938 LOAD_DOUBLE(0, dbl
);
4941 END_CASE(JSOP_DOUBLE
)
4943 BEGIN_CASE(JSOP_STRING
)
4947 PUSH_STRING(ATOM_TO_STRING(atom
));
4949 END_CASE(JSOP_STRING
)
4951 BEGIN_CASE(JSOP_OBJECT
)
4954 LOAD_OBJECT(0, obj
);
4957 END_CASE(JSOP_OBJECT
)
4959 BEGIN_CASE(JSOP_REGEXP
)
4962 * Push a regexp object cloned from the regexp literal object mapped by the
4963 * bytecode at pc. ES5 finally fixed this bad old ES3 design flaw which was
4964 * flouted by many browser-based implementations.
4966 * We avoid the GetScopeChain call here and pass fp->scopeChain as
4967 * js_GetClassPrototype uses the latter only to locate the global.
4969 jsatomid index
= GET_FULL_INDEX(0);
4971 if (!js_GetClassPrototype(cx
, ®s
.fp
->scopeChain(), JSProto_RegExp
, &proto
))
4974 JSObject
*obj
= js_CloneRegExpObject(cx
, script
->getRegExp(index
), proto
);
4979 END_CASE(JSOP_REGEXP
)
4981 BEGIN_CASE(JSOP_ZERO
)
4985 BEGIN_CASE(JSOP_ONE
)
4989 BEGIN_CASE(JSOP_NULL
)
4993 BEGIN_CASE(JSOP_FALSE
)
4994 PUSH_BOOLEAN(false);
4995 END_CASE(JSOP_FALSE
)
4997 BEGIN_CASE(JSOP_TRUE
)
5002 BEGIN_CASE(JSOP_TABLESWITCH
)
5004 jsbytecode
*pc2
= regs
.pc
;
5005 len
= GET_JUMP_OFFSET(pc2
);
5008 * ECMAv2+ forbids conversion of discriminant, so we will skip to the
5009 * default case if the discriminant isn't already an int jsval. (This
5010 * opcode is emitted only for dense jsint-domain switches.)
5012 const Value
&rref
= *--regs
.sp
;
5014 if (rref
.isInt32()) {
5018 /* Don't use JSDOUBLE_IS_INT32; treat -0 (double) as 0. */
5019 if (!rref
.isDouble() || (d
= rref
.toDouble()) != (i
= int32_t(rref
.toDouble())))
5023 pc2
+= JUMP_OFFSET_LEN
;
5024 jsint low
= GET_JUMP_OFFSET(pc2
);
5025 pc2
+= JUMP_OFFSET_LEN
;
5026 jsint high
= GET_JUMP_OFFSET(pc2
);
5029 if ((jsuint
)i
< (jsuint
)(high
- low
+ 1)) {
5030 pc2
+= JUMP_OFFSET_LEN
+ JUMP_OFFSET_LEN
* i
;
5031 jsint off
= (jsint
) GET_JUMP_OFFSET(pc2
);
5040 BEGIN_CASE(JSOP_TABLESWITCHX
)
5042 jsbytecode
*pc2
= regs
.pc
;
5043 len
= GET_JUMPX_OFFSET(pc2
);
5046 * ECMAv2+ forbids conversion of discriminant, so we will skip to the
5047 * default case if the discriminant isn't already an int jsval. (This
5048 * opcode is emitted only for dense jsint-domain switches.)
5050 const Value
&rref
= *--regs
.sp
;
5052 if (rref
.isInt32()) {
5054 } else if (rref
.isDouble() && rref
.toDouble() == 0) {
5055 /* Treat -0 (double) as 0. */
5061 pc2
+= JUMPX_OFFSET_LEN
;
5062 jsint low
= GET_JUMP_OFFSET(pc2
);
5063 pc2
+= JUMP_OFFSET_LEN
;
5064 jsint high
= GET_JUMP_OFFSET(pc2
);
5067 if ((jsuint
)i
< (jsuint
)(high
- low
+ 1)) {
5068 pc2
+= JUMP_OFFSET_LEN
+ JUMPX_OFFSET_LEN
* i
;
5069 jsint off
= (jsint
) GET_JUMPX_OFFSET(pc2
);
5078 BEGIN_CASE(JSOP_LOOKUPSWITCHX
)
5081 off
= JUMPX_OFFSET_LEN
;
5082 goto do_lookup_switch
;
5084 BEGIN_CASE(JSOP_LOOKUPSWITCH
)
5085 off
= JUMP_OFFSET_LEN
;
5089 * JSOP_LOOKUPSWITCH and JSOP_LOOKUPSWITCHX are never used if any atom
5090 * index in it would exceed 64K limit.
5092 JS_ASSERT(!regs
.fp
->hasImacropc());
5093 JS_ASSERT(atoms
== script
->atomMap
.vector
);
5094 jsbytecode
*pc2
= regs
.pc
;
5096 Value lval
= regs
.sp
[-1];
5099 if (!lval
.isPrimitive())
5100 goto end_lookup_switch
;
5104 npairs
= (jsint
) GET_UINT16(pc2
);
5106 JS_ASSERT(npairs
); /* empty switch uses JSOP_TABLESWITCH */
5109 #define SEARCH_PAIRS(MATCH_CODE) \
5111 Value rval = script->getConst(GET_INDEX(pc2)); \
5117 if (--npairs == 0) { \
5123 if (lval
.isString()) {
5124 JSLinearString
*str
= lval
.toString()->ensureLinear(cx
);
5127 JSLinearString
*str2
;
5129 match
= (rval
.isString() &&
5130 ((str2
= rval
.toString()->assertIsLinear()) == str
||
5131 EqualStrings(str2
, str
)));
5133 } else if (lval
.isNumber()) {
5134 double ldbl
= lval
.toNumber();
5136 match
= rval
.isNumber() && ldbl
== rval
.toNumber();
5140 match
= (lval
== rval
);
5146 len
= (op
== JSOP_LOOKUPSWITCH
)
5147 ? GET_JUMP_OFFSET(pc2
)
5148 : GET_JUMPX_OFFSET(pc2
);
5153 BEGIN_CASE(JSOP_TRAP
)
5156 JSTrapStatus status
= JS_HandleTrap(cx
, script
, regs
.pc
, Jsvalify(&rval
));
5161 regs
.fp
->setReturnValue(rval
);
5162 interpReturnOK
= JS_TRUE
;
5165 cx
->setPendingException(rval
);
5170 JS_ASSERT(status
== JSTRAP_CONTINUE
);
5171 CHECK_INTERRUPT_HANDLER();
5172 JS_ASSERT(rval
.isInt32());
5173 op
= (JSOp
) rval
.toInt32();
5174 JS_ASSERT((uintN
)op
< (uintN
)JSOP_LIMIT
);
5178 BEGIN_CASE(JSOP_ARGUMENTS
)
5181 if (!js_GetArgsValue(cx
, regs
.fp
, &rval
))
5185 END_CASE(JSOP_ARGUMENTS
)
5187 BEGIN_CASE(JSOP_ARGSUB
)
5189 jsid id
= INT_TO_JSID(GET_ARGNO(regs
.pc
));
5191 if (!js_GetArgsProperty(cx
, regs
.fp
, id
, &rval
))
5195 END_CASE(JSOP_ARGSUB
)
5197 BEGIN_CASE(JSOP_ARGCNT
)
5199 jsid id
= ATOM_TO_JSID(rt
->atomState
.lengthAtom
);
5201 if (!js_GetArgsProperty(cx
, regs
.fp
, id
, &rval
))
5205 END_CASE(JSOP_ARGCNT
)
5207 BEGIN_CASE(JSOP_GETARG
)
5208 BEGIN_CASE(JSOP_CALLARG
)
5210 uint32 slot
= GET_ARGNO(regs
.pc
);
5211 JS_ASSERT(slot
< regs
.fp
->numFormalArgs());
5212 METER_SLOT_OP(op
, slot
);
5213 PUSH_COPY(argv
[slot
]);
5214 if (op
== JSOP_CALLARG
)
5217 END_CASE(JSOP_GETARG
)
5219 BEGIN_CASE(JSOP_SETARG
)
5221 uint32 slot
= GET_ARGNO(regs
.pc
);
5222 JS_ASSERT(slot
< regs
.fp
->numFormalArgs());
5223 METER_SLOT_OP(op
, slot
);
5224 argv
[slot
] = regs
.sp
[-1];
5226 END_SET_CASE(JSOP_SETARG
)
5228 BEGIN_CASE(JSOP_GETLOCAL
)
5230 uint32 slot
= GET_SLOTNO(regs
.pc
);
5231 JS_ASSERT(slot
< script
->nslots
);
5232 PUSH_COPY(regs
.fp
->slots()[slot
]);
5234 END_CASE(JSOP_GETLOCAL
)
5236 BEGIN_CASE(JSOP_CALLLOCAL
)
5238 uint32 slot
= GET_SLOTNO(regs
.pc
);
5239 JS_ASSERT(slot
< script
->nslots
);
5240 PUSH_COPY(regs
.fp
->slots()[slot
]);
5243 END_CASE(JSOP_CALLLOCAL
)
5245 BEGIN_CASE(JSOP_SETLOCAL
)
5247 uint32 slot
= GET_SLOTNO(regs
.pc
);
5248 JS_ASSERT(slot
< script
->nslots
);
5249 regs
.fp
->slots()[slot
] = regs
.sp
[-1];
5251 END_SET_CASE(JSOP_SETLOCAL
)
5253 BEGIN_CASE(JSOP_GETUPVAR_DBG
)
5254 BEGIN_CASE(JSOP_CALLUPVAR_DBG
)
5256 JSFunction
*fun
= regs
.fp
->fun();
5257 JS_ASSERT(FUN_KIND(fun
) == JSFUN_INTERPRETED
);
5258 JS_ASSERT(fun
->u
.i
.wrapper
);
5260 /* Scope for tempPool mark and local names allocation in it. */
5261 JSObject
*obj
, *obj2
;
5266 AutoLocalNameArray
names(cx
, fun
);
5270 uintN index
= fun
->script()->bindings
.countArgsAndVars() + GET_UINT16(regs
.pc
);
5271 atom
= JS_LOCAL_NAME_TO_ATOM(names
[index
]);
5272 id
= ATOM_TO_JSID(atom
);
5274 if (!js_FindProperty(cx
, id
, &obj
, &obj2
, &prop
))
5279 atomNotDefined
= atom
;
5280 goto atom_not_defined
;
5283 /* Minimize footprint with generic code instead of NATIVE_GET. */
5284 Value
*vp
= regs
.sp
;
5286 if (!obj
->getProperty(cx
, id
, vp
))
5289 if (op
== JSOP_CALLUPVAR_DBG
)
5292 END_CASE(JSOP_GETUPVAR_DBG
)
5294 BEGIN_CASE(JSOP_GETFCSLOT
)
5295 BEGIN_CASE(JSOP_CALLFCSLOT
)
5297 JS_ASSERT(regs
.fp
->isFunctionFrame() && !regs
.fp
->isEvalFrame());
5298 uintN index
= GET_UINT16(regs
.pc
);
5299 JSObject
*obj
= &argv
[-2].toObject();
5301 JS_ASSERT(index
< obj
->getFunctionPrivate()->script()->bindings
.countUpvars());
5302 PUSH_COPY(obj
->getFlatClosureUpvar(index
));
5303 if (op
== JSOP_CALLFCSLOT
)
5306 END_CASE(JSOP_GETFCSLOT
)
5308 BEGIN_CASE(JSOP_GETGLOBAL
)
5309 BEGIN_CASE(JSOP_CALLGLOBAL
)
5311 uint32 slot
= GET_SLOTNO(regs
.pc
);
5312 slot
= script
->getGlobalSlot(slot
);
5313 JSObject
*obj
= regs
.fp
->scopeChain().getGlobal();
5314 JS_ASSERT(obj
->containsSlot(slot
));
5315 PUSH_COPY(obj
->getSlot(slot
));
5316 if (op
== JSOP_CALLGLOBAL
)
5319 END_CASE(JSOP_GETGLOBAL
)
5321 BEGIN_CASE(JSOP_DEFCONST
)
5322 BEGIN_CASE(JSOP_DEFVAR
)
5324 uint32 index
= GET_INDEX(regs
.pc
);
5325 JSAtom
*atom
= atoms
[index
];
5327 JSObject
*obj
= ®s
.fp
->varobj(cx
);
5328 JS_ASSERT(!obj
->getOps()->defineProperty
);
5329 uintN attrs
= JSPROP_ENUMERATE
;
5330 if (!regs
.fp
->isEvalFrame())
5331 attrs
|= JSPROP_PERMANENT
;
5333 /* Lookup id in order to check for redeclaration problems. */
5334 jsid id
= ATOM_TO_JSID(atom
);
5336 if (op
== JSOP_DEFVAR
) {
5338 * Redundant declaration of a |var|, even one for a non-writable
5339 * property like |undefined| in ES5, does nothing.
5343 if (!obj
->lookupProperty(cx
, id
, &obj2
, &prop
))
5345 shouldDefine
= (!prop
|| obj2
!= obj
);
5347 JS_ASSERT(op
== JSOP_DEFCONST
);
5348 attrs
|= JSPROP_READONLY
;
5349 if (!CheckRedeclaration(cx
, obj
, id
, attrs
))
5353 * As attrs includes readonly, CheckRedeclaration can succeed only
5354 * if prop does not exist.
5356 shouldDefine
= true;
5359 /* Bind a variable only if it's not yet defined. */
5361 !js_DefineNativeProperty(cx
, obj
, id
, UndefinedValue(),
5362 PropertyStub
, StrictPropertyStub
, attrs
, 0, 0, NULL
)) {
5366 END_CASE(JSOP_DEFVAR
)
5368 BEGIN_CASE(JSOP_DEFFUN
)
5371 * A top-level function defined in Global or Eval code (see ECMA-262
5372 * Ed. 3), or else a SpiderMonkey extension: a named function statement in
5373 * a compound statement (not at the top statement level of global code, or
5374 * at the top level of a function body).
5378 JSObject
*obj
= FUN_OBJECT(fun
);
5381 if (FUN_NULL_CLOSURE(fun
)) {
5383 * Even a null closure needs a parent for principals finding.
5384 * FIXME: bug 476950, although debugger users may also demand some kind
5385 * of scope link for debugger-assisted eval-in-frame.
5387 obj2
= ®s
.fp
->scopeChain();
5389 JS_ASSERT(!fun
->isFlatClosure());
5391 obj2
= GetScopeChainFast(cx
, regs
.fp
, JSOP_DEFFUN
, JSOP_DEFFUN_LENGTH
);
5397 * If static link is not current scope, clone fun's object to link to the
5398 * current scope via parent. We do this to enable sharing of compiled
5399 * functions among multiple equivalent scopes, amortizing the cost of
5400 * compilation over a number of executions. Examples include XUL scripts
5401 * and event handlers shared among Firefox or other Mozilla app chrome
5402 * windows, and user-defined JS functions precompiled and then shared among
5403 * requests in server-side JS.
5405 if (obj
->getParent() != obj2
) {
5406 obj
= CloneFunctionObject(cx
, fun
, obj2
);
5412 * ECMA requires functions defined when entering Eval code to be
5415 uintN attrs
= regs
.fp
->isEvalFrame()
5417 : JSPROP_ENUMERATE
| JSPROP_PERMANENT
;
5420 * We define the function as a property of the variable object and not the
5421 * current scope chain even for the case of function expression statements
5422 * and functions defined by eval inside let or with blocks.
5424 JSObject
*parent
= ®s
.fp
->varobj(cx
);
5426 /* ES5 10.5 (NB: with subsequent errata). */
5427 jsid id
= ATOM_TO_JSID(fun
->atom
);
5428 JSProperty
*prop
= NULL
;
5430 if (!parent
->lookupProperty(cx
, id
, &pobj
, &prop
))
5433 Value rval
= ObjectValue(*obj
);
5437 if (!prop
|| pobj
!= parent
) {
5438 if (!parent
->defineProperty(cx
, id
, rval
, PropertyStub
, StrictPropertyStub
, attrs
))
5444 JS_ASSERT(parent
->isNative());
5445 Shape
*shape
= reinterpret_cast<Shape
*>(prop
);
5446 if (parent
->isGlobal()) {
5447 if (shape
->configurable()) {
5448 if (!parent
->defineProperty(cx
, id
, rval
, PropertyStub
, StrictPropertyStub
, attrs
))
5453 if (shape
->isAccessorDescriptor() || !shape
->writable() || !shape
->enumerable()) {
5454 JSAutoByteString bytes
;
5455 if (const char *name
= js_ValueToPrintable(cx
, IdToValue(id
), &bytes
)) {
5456 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
5457 JSMSG_CANT_REDEFINE_PROP
, name
);
5464 * Non-global properties, and global properties which we aren't simply
5465 * redefining, must be set. First, this preserves their attributes.
5466 * Second, this will produce warnings and/or errors as necessary if the
5467 * specified Call object property is not writable (const).
5471 if (!parent
->setProperty(cx
, id
, &rval
, script
->strictModeCode
))
5475 END_CASE(JSOP_DEFFUN
)
5477 BEGIN_CASE(JSOP_DEFFUN_FC
)
5478 BEGIN_CASE(JSOP_DEFFUN_DBGFC
)
5483 JSObject
*obj
= (op
== JSOP_DEFFUN_FC
)
5484 ? js_NewFlatClosure(cx
, fun
, JSOP_DEFFUN_FC
, JSOP_DEFFUN_FC_LENGTH
)
5485 : js_NewDebuggableFlatClosure(cx
, fun
);
5489 Value rval
= ObjectValue(*obj
);
5491 uintN attrs
= regs
.fp
->isEvalFrame()
5493 : JSPROP_ENUMERATE
| JSPROP_PERMANENT
;
5495 JSObject
&parent
= regs
.fp
->varobj(cx
);
5497 jsid id
= ATOM_TO_JSID(fun
->atom
);
5498 if (!CheckRedeclaration(cx
, &parent
, id
, attrs
))
5501 if ((attrs
== JSPROP_ENUMERATE
)
5502 ? !parent
.setProperty(cx
, id
, &rval
, script
->strictModeCode
)
5503 : !parent
.defineProperty(cx
, id
, rval
, PropertyStub
, StrictPropertyStub
, attrs
)) {
5507 END_CASE(JSOP_DEFFUN_FC
)
5509 BEGIN_CASE(JSOP_DEFLOCALFUN
)
5512 * Define a local function (i.e., one nested at the top level of another
5513 * function), parented by the current scope chain, stored in a local
5514 * variable slot that the compiler allocated. This is an optimization over
5515 * JSOP_DEFFUN that avoids requiring a call object for the outer function's
5519 LOAD_FUNCTION(SLOTNO_LEN
);
5520 JS_ASSERT(fun
->isInterpreted());
5521 JS_ASSERT(!FUN_FLAT_CLOSURE(fun
));
5522 JSObject
*obj
= FUN_OBJECT(fun
);
5524 if (FUN_NULL_CLOSURE(fun
)) {
5525 obj
= CloneFunctionObject(cx
, fun
, ®s
.fp
->scopeChain());
5529 JSObject
*parent
= GetScopeChainFast(cx
, regs
.fp
, JSOP_DEFLOCALFUN
,
5530 JSOP_DEFLOCALFUN_LENGTH
);
5534 if (obj
->getParent() != parent
) {
5536 if (TRACE_RECORDER(cx
))
5537 AbortRecording(cx
, "DEFLOCALFUN for closure");
5539 obj
= CloneFunctionObject(cx
, fun
, parent
);
5545 uint32 slot
= GET_SLOTNO(regs
.pc
);
5546 TRACE_2(DefLocalFunSetSlot
, slot
, obj
);
5548 regs
.fp
->slots()[slot
].setObject(*obj
);
5550 END_CASE(JSOP_DEFLOCALFUN
)
5552 BEGIN_CASE(JSOP_DEFLOCALFUN_FC
)
5555 LOAD_FUNCTION(SLOTNO_LEN
);
5557 JSObject
*obj
= js_NewFlatClosure(cx
, fun
, JSOP_DEFLOCALFUN_FC
, JSOP_DEFLOCALFUN_FC_LENGTH
);
5561 uint32 slot
= GET_SLOTNO(regs
.pc
);
5562 TRACE_2(DefLocalFunSetSlot
, slot
, obj
);
5564 regs
.fp
->slots()[slot
].setObject(*obj
);
5566 END_CASE(JSOP_DEFLOCALFUN_FC
)
5568 BEGIN_CASE(JSOP_DEFLOCALFUN_DBGFC
)
5571 LOAD_FUNCTION(SLOTNO_LEN
);
5573 JSObject
*obj
= js_NewDebuggableFlatClosure(cx
, fun
);
5577 uint32 slot
= GET_SLOTNO(regs
.pc
);
5578 regs
.fp
->slots()[slot
].setObject(*obj
);
5580 END_CASE(JSOP_DEFLOCALFUN_DBGFC
)
5582 BEGIN_CASE(JSOP_LAMBDA
)
5584 /* Load the specified function object literal. */
5587 JSObject
*obj
= FUN_OBJECT(fun
);
5589 /* do-while(0) so we can break instead of using a goto. */
5592 if (FUN_NULL_CLOSURE(fun
)) {
5593 parent
= ®s
.fp
->scopeChain();
5595 if (obj
->getParent() == parent
) {
5596 jsbytecode
*pc2
= AdvanceOverBlockchainOp(regs
.pc
+ JSOP_LAMBDA_LENGTH
);
5597 JSOp op2
= JSOp(*pc2
);
5600 * Optimize var obj = {method: function () { ... }, ...},
5601 * this.method = function () { ... }; and other significant
5602 * single-use-of-null-closure bytecode sequences.
5604 * WARNING: code in TraceRecorder::record_JSOP_LAMBDA must
5605 * match the optimization cases in the following code that
5606 * break from the outer do-while(0).
5608 if (op2
== JSOP_INITMETHOD
) {
5610 const Value
&lref
= regs
.sp
[-1];
5611 JS_ASSERT(lref
.isObject());
5612 JSObject
*obj2
= &lref
.toObject();
5613 JS_ASSERT(obj2
->getClass() == &js_ObjectClass
);
5616 fun
->setMethodAtom(script
->getAtom(GET_FULL_INDEX(pc2
- regs
.pc
)));
5617 JS_FUNCTION_METER(cx
, joinedinitmethod
);
5621 if (op2
== JSOP_SETMETHOD
) {
5623 op2
= JSOp(pc2
[JSOP_SETMETHOD_LENGTH
]);
5624 JS_ASSERT(op2
== JSOP_POP
|| op2
== JSOP_POPV
);
5626 const Value
&lref
= regs
.sp
[-1];
5627 if (lref
.isObject() && lref
.toObject().canHaveMethodBarrier()) {
5628 fun
->setMethodAtom(script
->getAtom(GET_FULL_INDEX(pc2
- regs
.pc
)));
5629 JS_FUNCTION_METER(cx
, joinedsetmethod
);
5632 } else if (fun
->joinable()) {
5633 if (op2
== JSOP_CALL
) {
5635 * Array.prototype.sort and String.prototype.replace are
5636 * optimized as if they are special form. We know that they
5637 * won't leak the joined function object in obj, therefore
5638 * we don't need to clone that compiler- created function
5639 * object for identity/mutation reasons.
5641 int iargc
= GET_ARGC(pc2
);
5644 * Note that we have not yet pushed obj as the final argument,
5645 * so regs.sp[1 - (iargc + 2)], and not regs.sp[-(iargc + 2)],
5646 * is the callee for this JSOP_CALL.
5648 const Value
&cref
= regs
.sp
[1 - (iargc
+ 2)];
5651 if (IsFunctionObject(cref
, &callee
)) {
5652 JSFunction
*calleeFun
= GET_FUNCTION_PRIVATE(cx
, callee
);
5653 if (Native native
= calleeFun
->maybeNative()) {
5654 if (iargc
== 1 && native
== array_sort
) {
5655 JS_FUNCTION_METER(cx
, joinedsort
);
5658 if (iargc
== 2 && native
== str_replace
) {
5659 JS_FUNCTION_METER(cx
, joinedreplace
);
5664 } else if (op2
== JSOP_NULL
) {
5665 pc2
+= JSOP_NULL_LENGTH
;
5668 if (op2
== JSOP_CALL
&& GET_ARGC(pc2
) == 0) {
5669 JS_FUNCTION_METER(cx
, joinedmodulepat
);
5677 if (rt
->functionMeterFilename
) {
5678 // No locking, this is mainly for js shell testing.
5679 ++rt
->functionMeter
.unjoined
;
5681 typedef JSRuntime::FunctionCountMap HM
;
5682 HM
&h
= rt
->unjoinedFunctionCountMap
;
5683 HM::AddPtr p
= h
.lookupForAdd(fun
);
5687 JS_ASSERT(p
->key
== fun
);
5693 parent
= GetScopeChainFast(cx
, regs
.fp
, JSOP_LAMBDA
, JSOP_LAMBDA_LENGTH
);
5698 obj
= CloneFunctionObject(cx
, fun
, parent
);
5705 END_CASE(JSOP_LAMBDA
)
5707 BEGIN_CASE(JSOP_LAMBDA_FC
)
5712 JSObject
*obj
= js_NewFlatClosure(cx
, fun
, JSOP_LAMBDA_FC
, JSOP_LAMBDA_FC_LENGTH
);
5718 END_CASE(JSOP_LAMBDA_FC
)
5720 BEGIN_CASE(JSOP_LAMBDA_DBGFC
)
5725 JSObject
*obj
= js_NewDebuggableFlatClosure(cx
, fun
);
5731 END_CASE(JSOP_LAMBDA_DBGFC
)
5733 BEGIN_CASE(JSOP_CALLEE
)
5734 JS_ASSERT(regs
.fp
->isFunctionFrame() && !regs
.fp
->isEvalFrame());
5735 PUSH_COPY(argv
[-2]);
5736 END_CASE(JSOP_CALLEE
)
5738 BEGIN_CASE(JSOP_GETTER
)
5739 BEGIN_CASE(JSOP_SETTER
)
5742 JSOp op2
= (JSOp
) *++regs
.pc
;
5748 case JSOP_INDEXBASE
:
5749 atoms
+= GET_INDEXBASE(regs
.pc
);
5750 regs
.pc
+= JSOP_INDEXBASE_LENGTH
- 1;
5751 goto do_getter_setter
;
5752 case JSOP_INDEXBASE1
:
5753 case JSOP_INDEXBASE2
:
5754 case JSOP_INDEXBASE3
:
5755 atoms
+= (op2
- JSOP_INDEXBASE1
+ 1) << 16;
5756 goto do_getter_setter
;
5763 id
= ATOM_TO_JSID(atom
);
5773 FETCH_OBJECT(cx
, i
- 1, obj
);
5778 JS_ASSERT(regs
.sp
- regs
.fp
->base() >= 2);
5783 id
= ATOM_TO_JSID(atom
);
5787 JS_ASSERT(op2
== JSOP_INITELEM
);
5789 JS_ASSERT(regs
.sp
- regs
.fp
->base() >= 3);
5795 const Value
&lref
= regs
.sp
[i
-1];
5796 JS_ASSERT(lref
.isObject());
5797 obj
= &lref
.toObject();
5802 /* Ensure that id has a type suitable for use with obj. */
5803 if (JSID_IS_VOID(id
))
5804 FETCH_ELEMENT_ID(obj
, i
, id
);
5806 if (!js_IsCallable(rval
)) {
5807 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
5808 JSMSG_BAD_GETTER_OR_SETTER
,
5816 * Getters and setters are just like watchpoints from an access control
5821 if (!CheckAccess(cx
, obj
, id
, JSACC_WATCH
, &rtmp
, &attrs
))
5825 StrictPropertyOp setter
;
5826 if (op
== JSOP_GETTER
) {
5827 getter
= CastAsPropertyOp(&rval
.toObject());
5828 setter
= StrictPropertyStub
;
5829 attrs
= JSPROP_GETTER
;
5831 getter
= PropertyStub
;
5832 setter
= CastAsStrictPropertyOp(&rval
.toObject());
5833 attrs
= JSPROP_SETTER
;
5835 attrs
|= JSPROP_ENUMERATE
| JSPROP_SHARED
;
5837 /* Check for a readonly or permanent property of the same name. */
5838 if (!CheckRedeclaration(cx
, obj
, id
, attrs
))
5841 if (!obj
->defineProperty(cx
, id
, UndefinedValue(), getter
, setter
, attrs
))
5845 if (js_CodeSpec
[op2
].ndefs
> js_CodeSpec
[op2
].nuses
) {
5846 JS_ASSERT(js_CodeSpec
[op2
].ndefs
== js_CodeSpec
[op2
].nuses
+ 1);
5848 assertSameCompartment(cx
, regs
.sp
[-1]);
5850 len
= js_CodeSpec
[op2
].length
;
5854 BEGIN_CASE(JSOP_HOLE
)
5858 BEGIN_CASE(JSOP_NEWINIT
)
5860 jsint i
= regs
.pc
[1];
5862 JS_ASSERT(i
== JSProto_Array
|| i
== JSProto_Object
);
5865 if (i
== JSProto_Array
) {
5866 obj
= NewDenseEmptyArray(cx
);
5868 gc::FinalizeKind kind
= GuessObjectGCKind(0, false);
5869 obj
= NewBuiltinClassInstance(cx
, &js_ObjectClass
, kind
);
5876 CHECK_INTERRUPT_HANDLER();
5878 END_CASE(JSOP_NEWINIT
)
5880 BEGIN_CASE(JSOP_NEWARRAY
)
5882 unsigned count
= GET_UINT24(regs
.pc
);
5883 JSObject
*obj
= NewDenseAllocatedArray(cx
, count
);
5888 CHECK_INTERRUPT_HANDLER();
5890 END_CASE(JSOP_NEWARRAY
)
5892 BEGIN_CASE(JSOP_NEWOBJECT
)
5895 LOAD_OBJECT(0, baseobj
);
5897 JSObject
*obj
= CopyInitializerObject(cx
, baseobj
);
5903 CHECK_INTERRUPT_HANDLER();
5905 END_CASE(JSOP_NEWOBJECT
)
5907 BEGIN_CASE(JSOP_ENDINIT
)
5909 /* FIXME remove JSOP_ENDINIT bug 588522 */
5910 JS_ASSERT(regs
.sp
- regs
.fp
->base() >= 1);
5911 JS_ASSERT(regs
.sp
[-1].isObject());
5913 END_CASE(JSOP_ENDINIT
)
5915 BEGIN_CASE(JSOP_INITPROP
)
5916 BEGIN_CASE(JSOP_INITMETHOD
)
5918 /* Load the property's initial value into rval. */
5919 JS_ASSERT(regs
.sp
- regs
.fp
->base() >= 2);
5920 Value rval
= regs
.sp
[-1];
5922 /* Load the object being initialized into lval/obj. */
5923 JSObject
*obj
= ®s
.sp
[-2].toObject();
5924 JS_ASSERT(obj
->isObject());
5927 * Probe the property cache.
5929 * On a hit, if the cached shape has a non-default setter, it must be
5930 * __proto__. If shape->previous() != obj->lastProperty(), there must be a
5931 * repeated property name. The fast path does not handle these two cases.
5933 PropertyCacheEntry
*entry
;
5935 if (JS_PROPERTY_CACHE(cx
).testForInit(rt
, regs
.pc
, obj
, &shape
, &entry
) &&
5936 shape
->hasDefaultSetter() &&
5937 shape
->previous() == obj
->lastProperty())
5939 /* Fast path. Property cache hit. */
5940 uint32 slot
= shape
->slot
;
5942 JS_ASSERT(slot
== obj
->slotSpan());
5943 JS_ASSERT(slot
>= JSSLOT_FREE(obj
->getClass()));
5944 if (slot
< obj
->numSlots()) {
5945 JS_ASSERT(obj
->getSlot(slot
).isUndefined());
5947 if (!obj
->allocSlot(cx
, &slot
))
5949 JS_ASSERT(slot
== shape
->slot
);
5952 /* A new object, or one we just extended in a recent initprop op. */
5953 JS_ASSERT(!obj
->lastProperty() ||
5954 obj
->shape() == obj
->lastProperty()->shape
);
5955 obj
->extend(cx
, shape
);
5958 * No method change check here because here we are adding a new
5959 * property, not updating an existing slot's value that might
5960 * contain a method of a branded shape.
5962 TRACE_1(AddProperty
, obj
);
5963 obj
->nativeSetSlot(slot
, rval
);
5965 PCMETER(JS_PROPERTY_CACHE(cx
).inipcmisses
++);
5967 /* Get the immediate property name into id. */
5970 jsid id
= ATOM_TO_JSID(atom
);
5972 uintN defineHow
= (op
== JSOP_INITMETHOD
)
5973 ? JSDNP_CACHE_RESULT
| JSDNP_SET_METHOD
5974 : JSDNP_CACHE_RESULT
;
5975 if (!(JS_UNLIKELY(atom
== cx
->runtime
->atomState
.protoAtom
)
5976 ? js_SetPropertyHelper(cx
, obj
, id
, defineHow
, &rval
, script
->strictModeCode
)
5977 : js_DefineNativeProperty(cx
, obj
, id
, rval
, NULL
, NULL
,
5978 JSPROP_ENUMERATE
, 0, 0, NULL
,
5984 /* Common tail for property cache hit and miss cases. */
5987 END_CASE(JSOP_INITPROP
);
5989 BEGIN_CASE(JSOP_INITELEM
)
5991 /* Pop the element's value into rval. */
5992 JS_ASSERT(regs
.sp
- regs
.fp
->base() >= 3);
5993 const Value
&rref
= regs
.sp
[-1];
5995 /* Find the object being initialized at top of stack. */
5996 const Value
&lref
= regs
.sp
[-3];
5997 JS_ASSERT(lref
.isObject());
5998 JSObject
*obj
= &lref
.toObject();
6000 /* Fetch id now that we have obj. */
6002 FETCH_ELEMENT_ID(obj
, -2, id
);
6005 * If rref is a hole, do not call JSObject::defineProperty. In this case,
6006 * obj must be an array, so if the current op is the last element
6007 * initialiser, set the array length to one greater than id.
6009 if (rref
.isMagic(JS_ARRAY_HOLE
)) {
6010 JS_ASSERT(obj
->isArray());
6011 JS_ASSERT(JSID_IS_INT(id
));
6012 JS_ASSERT(jsuint(JSID_TO_INT(id
)) < JS_ARGS_LENGTH_MAX
);
6013 if (js_GetOpcode(cx
, script
, regs
.pc
+ JSOP_INITELEM_LENGTH
) == JSOP_ENDINIT
&&
6014 !js_SetLengthProperty(cx
, obj
, (jsuint
) (JSID_TO_INT(id
) + 1))) {
6018 if (!obj
->defineProperty(cx
, id
, rref
, NULL
, NULL
, JSPROP_ENUMERATE
))
6023 END_CASE(JSOP_INITELEM
)
6025 #if JS_HAS_SHARP_VARS
6027 BEGIN_CASE(JSOP_DEFSHARP
)
6029 uint32 slot
= GET_UINT16(regs
.pc
);
6030 JS_ASSERT(slot
+ 1 < regs
.fp
->numFixed());
6031 const Value
&lref
= regs
.fp
->slots()[slot
];
6033 if (lref
.isObject()) {
6034 obj
= &lref
.toObject();
6036 JS_ASSERT(lref
.isUndefined());
6037 obj
= NewDenseEmptyArray(cx
);
6040 regs
.fp
->slots()[slot
].setObject(*obj
);
6042 jsint i
= (jsint
) GET_UINT16(regs
.pc
+ UINT16_LEN
);
6043 jsid id
= INT_TO_JSID(i
);
6044 const Value
&rref
= regs
.sp
[-1];
6045 if (rref
.isPrimitive()) {
6047 JS_snprintf(numBuf
, sizeof numBuf
, "%u", (unsigned) i
);
6048 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6049 JSMSG_BAD_SHARP_DEF
, numBuf
);
6052 if (!obj
->defineProperty(cx
, id
, rref
, NULL
, NULL
, JSPROP_ENUMERATE
))
6055 END_CASE(JSOP_DEFSHARP
)
6057 BEGIN_CASE(JSOP_USESHARP
)
6059 uint32 slot
= GET_UINT16(regs
.pc
);
6060 JS_ASSERT(slot
+ 1 < regs
.fp
->numFixed());
6061 const Value
&lref
= regs
.fp
->slots()[slot
];
6062 jsint i
= (jsint
) GET_UINT16(regs
.pc
+ UINT16_LEN
);
6064 if (lref
.isUndefined()) {
6065 rval
.setUndefined();
6067 JSObject
*obj
= ®s
.fp
->slots()[slot
].toObject();
6068 jsid id
= INT_TO_JSID(i
);
6069 if (!obj
->getProperty(cx
, id
, &rval
))
6072 if (!rval
.isObjectOrNull()) {
6075 JS_snprintf(numBuf
, sizeof numBuf
, "%u", (unsigned) i
);
6076 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6077 JSMSG_BAD_SHARP_USE
, numBuf
);
6082 END_CASE(JSOP_USESHARP
)
6084 BEGIN_CASE(JSOP_SHARPINIT
)
6086 uint32 slot
= GET_UINT16(regs
.pc
);
6087 JS_ASSERT(slot
+ 1 < regs
.fp
->numFixed());
6088 Value
*vp
= ®s
.fp
->slots()[slot
];
6092 * We peek ahead safely here because empty initialisers get zero
6093 * JSOP_SHARPINIT ops, and non-empty ones get two: the first comes
6094 * immediately after JSOP_NEWINIT followed by one or more property
6095 * initialisers; and the second comes directly before JSOP_ENDINIT.
6097 if (regs
.pc
[JSOP_SHARPINIT_LENGTH
] != JSOP_ENDINIT
) {
6098 rval
.setInt32(rval
.isUndefined() ? 1 : rval
.toInt32() + 1);
6100 JS_ASSERT(rval
.isInt32());
6101 rval
.getInt32Ref() -= 1;
6102 if (rval
.toInt32() == 0)
6103 vp
[0].setUndefined();
6107 END_CASE(JSOP_SHARPINIT
)
6109 #endif /* JS_HAS_SHARP_VARS */
6112 BEGIN_CASE(JSOP_GOSUB
)
6113 PUSH_BOOLEAN(false);
6114 jsint i
= (regs
.pc
- script
->main
) + JSOP_GOSUB_LENGTH
;
6116 len
= GET_JUMP_OFFSET(regs
.pc
);
6121 BEGIN_CASE(JSOP_GOSUBX
)
6122 PUSH_BOOLEAN(false);
6123 jsint i
= (regs
.pc
- script
->main
) + JSOP_GOSUBX_LENGTH
;
6124 len
= GET_JUMPX_OFFSET(regs
.pc
);
6130 BEGIN_CASE(JSOP_RETSUB
)
6131 /* Pop [exception or hole, retsub pc-index]. */
6135 JS_ASSERT(lval
.isBoolean());
6136 if (lval
.toBoolean()) {
6138 * Exception was pending during finally, throw it *before* we adjust
6139 * pc, because pc indexes into script->trynotes. This turns out not to
6140 * be necessary, but it seems clearer. And it points out a FIXME:
6141 * 350509, due to Igor Bukanov.
6143 cx
->setPendingException(rval
);
6146 JS_ASSERT(rval
.isInt32());
6147 len
= rval
.toInt32();
6148 regs
.pc
= script
->main
;
6152 BEGIN_CASE(JSOP_EXCEPTION
)
6153 PUSH_COPY(cx
->getPendingException());
6154 cx
->clearPendingException();
6155 #if defined(JS_TRACER) && defined(JS_METHODJIT)
6156 if (interpMode
== JSINTERP_PROFILE
) {
6157 leaveOnSafePoint
= true;
6158 LEAVE_ON_SAFE_POINT();
6162 END_CASE(JSOP_EXCEPTION
)
6164 BEGIN_CASE(JSOP_FINALLY
)
6166 END_CASE(JSOP_FINALLY
)
6168 BEGIN_CASE(JSOP_THROWING
)
6170 JS_ASSERT(!cx
->isExceptionPending());
6173 cx
->setPendingException(v
);
6175 END_CASE(JSOP_THROWING
)
6177 BEGIN_CASE(JSOP_THROW
)
6179 JS_ASSERT(!cx
->isExceptionPending());
6183 cx
->setPendingException(v
);
6184 /* let the code at error try to catch the exception. */
6187 BEGIN_CASE(JSOP_SETLOCALPOP
)
6190 * The stack must have a block with at least one local slot below the
6193 JS_ASSERT((size_t) (regs
.sp
- regs
.fp
->base()) >= 2);
6194 uint32 slot
= GET_UINT16(regs
.pc
);
6195 JS_ASSERT(slot
+ 1 < script
->nslots
);
6196 POP_COPY_TO(regs
.fp
->slots()[slot
]);
6198 END_CASE(JSOP_SETLOCALPOP
)
6200 BEGIN_CASE(JSOP_IFPRIMTOP
)
6202 * If the top of stack is of primitive type, jump to our target. Otherwise
6203 * advance to the next opcode.
6205 JS_ASSERT(regs
.sp
> regs
.fp
->base());
6206 if (regs
.sp
[-1].isPrimitive()) {
6207 len
= GET_JUMP_OFFSET(regs
.pc
);
6210 END_CASE(JSOP_IFPRIMTOP
)
6212 BEGIN_CASE(JSOP_PRIMTOP
)
6213 JS_ASSERT(regs
.sp
> regs
.fp
->base());
6214 if (regs
.sp
[-1].isObject()) {
6215 jsint i
= GET_INT8(regs
.pc
);
6216 js_ReportValueError2(cx
, JSMSG_CANT_CONVERT_TO
, -2, regs
.sp
[-2], NULL
,
6217 (i
== JSTYPE_VOID
) ? "primitive type" : JS_TYPE_STR(i
));
6220 END_CASE(JSOP_PRIMTOP
)
6222 BEGIN_CASE(JSOP_OBJTOP
)
6223 if (regs
.sp
[-1].isPrimitive()) {
6224 js_ReportValueError(cx
, GET_UINT16(regs
.pc
), -1, regs
.sp
[-1], NULL
);
6227 END_CASE(JSOP_OBJTOP
)
6229 BEGIN_CASE(JSOP_INSTANCEOF
)
6231 const Value
&rref
= regs
.sp
[-1];
6232 if (rref
.isPrimitive()) {
6233 js_ReportValueError(cx
, JSMSG_BAD_INSTANCEOF_RHS
, -1, rref
, NULL
);
6236 JSObject
*obj
= &rref
.toObject();
6237 const Value
&lref
= regs
.sp
[-2];
6238 JSBool cond
= JS_FALSE
;
6239 if (!HasInstance(cx
, obj
, &lref
, &cond
))
6242 regs
.sp
[-1].setBoolean(cond
);
6244 END_CASE(JSOP_INSTANCEOF
)
6246 BEGIN_CASE(JSOP_DEBUGGER
)
6248 JSDebuggerHandler handler
= cx
->debugHooks
->debuggerHandler
;
6251 switch (handler(cx
, script
, regs
.pc
, Jsvalify(&rval
), cx
->debugHooks
->debuggerHandlerData
)) {
6254 case JSTRAP_CONTINUE
:
6257 regs
.fp
->setReturnValue(rval
);
6258 interpReturnOK
= JS_TRUE
;
6261 cx
->setPendingException(rval
);
6265 CHECK_INTERRUPT_HANDLER();
6268 END_CASE(JSOP_DEBUGGER
)
6270 #if JS_HAS_XML_SUPPORT
6271 BEGIN_CASE(JSOP_DEFXMLNS
)
6273 if (!js_SetDefaultXMLNamespace(cx
, regs
.sp
[-1]))
6277 END_CASE(JSOP_DEFXMLNS
)
6279 BEGIN_CASE(JSOP_ANYNAME
)
6282 if (!js_GetAnyName(cx
, &id
))
6284 PUSH_COPY(IdToValue(id
));
6286 END_CASE(JSOP_ANYNAME
)
6288 BEGIN_CASE(JSOP_QNAMEPART
)
6292 PUSH_STRING(ATOM_TO_STRING(atom
));
6294 END_CASE(JSOP_QNAMEPART
)
6296 BEGIN_CASE(JSOP_QNAMECONST
)
6300 Value rval
= StringValue(ATOM_TO_STRING(atom
));
6301 Value lval
= regs
.sp
[-1];
6302 JSObject
*obj
= js_ConstructXMLQNameObject(cx
, lval
, rval
);
6305 regs
.sp
[-1].setObject(*obj
);
6307 END_CASE(JSOP_QNAMECONST
)
6309 BEGIN_CASE(JSOP_QNAME
)
6311 Value rval
= regs
.sp
[-1];
6312 Value lval
= regs
.sp
[-2];
6313 JSObject
*obj
= js_ConstructXMLQNameObject(cx
, lval
, rval
);
6317 regs
.sp
[-1].setObject(*obj
);
6319 END_CASE(JSOP_QNAME
)
6321 BEGIN_CASE(JSOP_TOATTRNAME
)
6325 if (!js_ToAttributeName(cx
, &rval
))
6329 END_CASE(JSOP_TOATTRNAME
)
6331 BEGIN_CASE(JSOP_TOATTRVAL
)
6335 JS_ASSERT(rval
.isString());
6336 JSString
*str
= js_EscapeAttributeValue(cx
, rval
.toString(), JS_FALSE
);
6339 regs
.sp
[-1].setString(str
);
6341 END_CASE(JSOP_TOATTRVAL
)
6343 BEGIN_CASE(JSOP_ADDATTRNAME
)
6344 BEGIN_CASE(JSOP_ADDATTRVAL
)
6346 Value rval
= regs
.sp
[-1];
6347 Value lval
= regs
.sp
[-2];
6348 JSString
*str
= lval
.toString();
6349 JSString
*str2
= rval
.toString();
6350 str
= js_AddAttributePart(cx
, op
== JSOP_ADDATTRNAME
, str
, str2
);
6354 regs
.sp
[-1].setString(str
);
6356 END_CASE(JSOP_ADDATTRNAME
)
6358 BEGIN_CASE(JSOP_BINDXMLNAME
)
6364 if (!js_FindXMLProperty(cx
, lval
, &obj
, &id
))
6366 regs
.sp
[-1].setObjectOrNull(obj
);
6367 PUSH_COPY(IdToValue(id
));
6369 END_CASE(JSOP_BINDXMLNAME
)
6371 BEGIN_CASE(JSOP_SETXMLNAME
)
6373 JSObject
*obj
= ®s
.sp
[-3].toObject();
6374 Value rval
= regs
.sp
[-1];
6376 FETCH_ELEMENT_ID(obj
, -2, id
);
6377 if (!obj
->setProperty(cx
, id
, &rval
, script
->strictModeCode
))
6383 END_CASE(JSOP_SETXMLNAME
)
6385 BEGIN_CASE(JSOP_CALLXMLNAME
)
6386 BEGIN_CASE(JSOP_XMLNAME
)
6388 Value lval
= regs
.sp
[-1];
6391 if (!js_FindXMLProperty(cx
, lval
, &obj
, &id
))
6394 if (!obj
->getProperty(cx
, id
, &rval
))
6397 if (op
== JSOP_CALLXMLNAME
)
6398 SLOW_PUSH_THISV(cx
, obj
);
6400 END_CASE(JSOP_XMLNAME
)
6402 BEGIN_CASE(JSOP_DESCENDANTS
)
6403 BEGIN_CASE(JSOP_DELDESC
)
6406 FETCH_OBJECT(cx
, -2, obj
);
6407 jsval rval
= Jsvalify(regs
.sp
[-1]);
6408 if (!js_GetXMLDescendants(cx
, obj
, rval
, &rval
))
6411 if (op
== JSOP_DELDESC
) {
6412 regs
.sp
[-1] = Valueify(rval
); /* set local root */
6413 if (!js_DeleteXMLListElements(cx
, JSVAL_TO_OBJECT(rval
)))
6415 rval
= JSVAL_TRUE
; /* always succeed */
6419 regs
.sp
[-1] = Valueify(rval
);
6421 END_CASE(JSOP_DESCENDANTS
)
6424 BEGIN_CASE(JSOP_FILTER
)
6426 * We push the hole value before jumping to [enditer] so we can detect the
6427 * first iteration and direct js_StepXMLListFilter to initialize filter's
6431 len
= GET_JUMP_OFFSET(regs
.pc
);
6436 BEGIN_CASE(JSOP_ENDFILTER
)
6438 bool cond
= !regs
.sp
[-1].isMagic();
6440 /* Exit the "with" block left from the previous iteration. */
6443 if (!js_StepXMLListFilter(cx
, cond
))
6445 if (!regs
.sp
[-1].isNull()) {
6447 * Decrease sp after EnterWith returns as we use sp[-1] there to root
6450 JS_ASSERT(IsXML(regs
.sp
[-1]));
6451 if (!js_EnterWith(cx
, -2, JSOP_ENDFILTER
, JSOP_ENDFILTER_LENGTH
))
6454 len
= GET_JUMP_OFFSET(regs
.pc
);
6460 END_CASE(JSOP_ENDFILTER
);
6462 BEGIN_CASE(JSOP_TOXML
)
6464 Value rval
= regs
.sp
[-1];
6465 JSObject
*obj
= js_ValueToXMLObject(cx
, rval
);
6468 regs
.sp
[-1].setObject(*obj
);
6470 END_CASE(JSOP_TOXML
)
6472 BEGIN_CASE(JSOP_TOXMLLIST
)
6474 Value rval
= regs
.sp
[-1];
6475 JSObject
*obj
= js_ValueToXMLListObject(cx
, rval
);
6478 regs
.sp
[-1].setObject(*obj
);
6480 END_CASE(JSOP_TOXMLLIST
)
6482 BEGIN_CASE(JSOP_XMLTAGEXPR
)
6484 Value rval
= regs
.sp
[-1];
6485 JSString
*str
= js_ValueToString(cx
, rval
);
6488 regs
.sp
[-1].setString(str
);
6490 END_CASE(JSOP_XMLTAGEXPR
)
6492 BEGIN_CASE(JSOP_XMLELTEXPR
)
6494 Value rval
= regs
.sp
[-1];
6497 str
= js_ValueToXMLString(cx
, rval
);
6499 str
= js_ValueToString(cx
, rval
);
6501 str
= js_EscapeElementValue(cx
, str
);
6505 regs
.sp
[-1].setString(str
);
6507 END_CASE(JSOP_XMLELTEXPR
)
6509 BEGIN_CASE(JSOP_XMLCDATA
)
6513 JSString
*str
= ATOM_TO_STRING(atom
);
6514 JSObject
*obj
= js_NewXMLSpecialObject(cx
, JSXML_CLASS_TEXT
, NULL
, str
);
6519 END_CASE(JSOP_XMLCDATA
)
6521 BEGIN_CASE(JSOP_XMLCOMMENT
)
6525 JSString
*str
= ATOM_TO_STRING(atom
);
6526 JSObject
*obj
= js_NewXMLSpecialObject(cx
, JSXML_CLASS_COMMENT
, NULL
, str
);
6531 END_CASE(JSOP_XMLCOMMENT
)
6533 BEGIN_CASE(JSOP_XMLPI
)
6537 JSString
*str
= ATOM_TO_STRING(atom
);
6538 Value rval
= regs
.sp
[-1];
6539 JSString
*str2
= rval
.toString();
6540 JSObject
*obj
= js_NewXMLSpecialObject(cx
, JSXML_CLASS_PROCESSING_INSTRUCTION
, str
, str2
);
6543 regs
.sp
[-1].setObject(*obj
);
6545 END_CASE(JSOP_XMLPI
)
6547 BEGIN_CASE(JSOP_GETFUNNS
)
6550 if (!js_GetFunctionNamespace(cx
, &rval
))
6554 END_CASE(JSOP_GETFUNNS
)
6555 #endif /* JS_HAS_XML_SUPPORT */
6557 BEGIN_CASE(JSOP_ENTERBLOCK
)
6560 LOAD_OBJECT(0, obj
);
6561 JS_ASSERT(obj
->isStaticBlock());
6562 JS_ASSERT(regs
.fp
->base() + OBJ_BLOCK_DEPTH(cx
, obj
) == regs
.sp
);
6563 Value
*vp
= regs
.sp
+ OBJ_BLOCK_COUNT(cx
, obj
);
6564 JS_ASSERT(regs
.sp
< vp
);
6565 JS_ASSERT(vp
<= regs
.fp
->slots() + script
->nslots
);
6566 SetValueRangeToUndefined(regs
.sp
, vp
);
6571 * The young end of fp->scopeChain may omit blocks if we haven't closed
6572 * over them, but if there are any closure blocks on fp->scopeChain, they'd
6573 * better be (clones of) ancestors of the block we're entering now;
6574 * anything else we should have popped off fp->scopeChain when we left its
6577 JSObject
*obj2
= ®s
.fp
->scopeChain();
6579 while ((clasp
= obj2
->getClass()) == &js_WithClass
)
6580 obj2
= obj2
->getParent();
6581 if (clasp
== &js_BlockClass
&&
6582 obj2
->getPrivate() == js_FloatingFrameIfGenerator(cx
, regs
.fp
)) {
6583 JSObject
*youngestProto
= obj2
->getProto();
6584 JS_ASSERT(youngestProto
->isStaticBlock());
6585 JSObject
*parent
= obj
;
6586 while ((parent
= parent
->getParent()) != youngestProto
)
6591 END_CASE(JSOP_ENTERBLOCK
)
6593 BEGIN_CASE(JSOP_LEAVEBLOCKEXPR
)
6594 BEGIN_CASE(JSOP_LEAVEBLOCK
)
6596 JSObject
*blockChain
;
6597 LOAD_OBJECT(UINT16_LEN
, blockChain
);
6599 JS_ASSERT(blockChain
->isStaticBlock());
6600 uintN blockDepth
= OBJ_BLOCK_DEPTH(cx
, blockChain
);
6601 JS_ASSERT(blockDepth
<= StackDepth(script
));
6604 * If we're about to leave the dynamic scope of a block that has been
6605 * cloned onto fp->scopeChain, clear its private data, move its locals from
6606 * the stack into the clone, and pop it off the chain.
6608 JSObject
&obj
= regs
.fp
->scopeChain();
6609 if (obj
.getProto() == blockChain
) {
6610 JS_ASSERT(obj
.isClonedBlock());
6611 if (!js_PutBlockObject(cx
, JS_TRUE
))
6615 /* Move the result of the expression to the new topmost stack slot. */
6616 Value
*vp
= NULL
; /* silence GCC warnings */
6617 if (op
== JSOP_LEAVEBLOCKEXPR
)
6619 regs
.sp
-= GET_UINT16(regs
.pc
);
6620 if (op
== JSOP_LEAVEBLOCKEXPR
) {
6621 JS_ASSERT(regs
.fp
->base() + blockDepth
== regs
.sp
- 1);
6624 JS_ASSERT(regs
.fp
->base() + blockDepth
== regs
.sp
);
6627 END_CASE(JSOP_LEAVEBLOCK
)
6629 #if JS_HAS_GENERATORS
6630 BEGIN_CASE(JSOP_GENERATOR
)
6632 JS_ASSERT(!cx
->isExceptionPending());
6633 regs
.pc
+= JSOP_GENERATOR_LENGTH
;
6634 JSObject
*obj
= js_NewGenerator(cx
);
6637 JS_ASSERT(!regs
.fp
->hasCallObj() && !regs
.fp
->hasArgsObj());
6638 regs
.fp
->setReturnValue(ObjectValue(*obj
));
6639 interpReturnOK
= true;
6640 if (entryFrame
!= regs
.fp
)
6645 BEGIN_CASE(JSOP_YIELD
)
6646 JS_ASSERT(!cx
->isExceptionPending());
6647 JS_ASSERT(regs
.fp
->isFunctionFrame() && !regs
.fp
->isEvalFrame());
6648 if (cx
->generatorFor(regs
.fp
)->state
== JSGEN_CLOSING
) {
6649 js_ReportValueError(cx
, JSMSG_BAD_GENERATOR_YIELD
,
6650 JSDVG_SEARCH_STACK
, argv
[-2], NULL
);
6653 regs
.fp
->setReturnValue(regs
.sp
[-1]);
6654 regs
.fp
->setYielding();
6655 regs
.pc
+= JSOP_YIELD_LENGTH
;
6656 interpReturnOK
= JS_TRUE
;
6659 BEGIN_CASE(JSOP_ARRAYPUSH
)
6661 uint32 slot
= GET_UINT16(regs
.pc
);
6662 JS_ASSERT(script
->nfixed
<= slot
);
6663 JS_ASSERT(slot
< script
->nslots
);
6664 JSObject
*obj
= ®s
.fp
->slots()[slot
].toObject();
6665 if (!js_ArrayCompPush(cx
, obj
, regs
.sp
[-1]))
6669 END_CASE(JSOP_ARRAYPUSH
)
6670 #endif /* JS_HAS_GENERATORS */
6672 #if JS_THREADED_INTERP
6674 L_JSOP_BACKPATCH_POP
:
6676 # if !JS_HAS_GENERATORS
6682 # if !JS_HAS_SHARP_VARS
6688 # if !JS_HAS_DESTRUCTURING
6689 L_JSOP_ENUMCONSTELEM
:
6692 # if !JS_HAS_XML_SUPPORT
6694 L_JSOP_STARTXMLEXPR
:
6722 #endif /* !JS_THREADED_INTERP */
6723 #if !JS_THREADED_INTERP
6728 JS_snprintf(numBuf
, sizeof numBuf
, "%d", op
);
6729 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6730 JSMSG_BAD_BYTECODE
, numBuf
);
6734 #if !JS_THREADED_INTERP
6737 #endif /* !JS_THREADED_INTERP */
6740 JS_ASSERT(cx
->regs
== ®s
);
6742 if (regs
.fp
->hasImacropc() && cx
->isExceptionPending()) {
6743 // Handle exceptions as if they came from the imacro-calling pc.
6744 regs
.pc
= regs
.fp
->imacropc();
6745 regs
.fp
->clearImacropc();
6749 JS_ASSERT(size_t((regs
.fp
->hasImacropc() ? regs
.fp
->imacropc() : regs
.pc
) - script
->code
) <
6754 * This abort could be weakened to permit tracing through exceptions that
6755 * are thrown and caught within a loop, with the co-operation of the tracer.
6756 * For now just bail on any sign of trouble.
6758 if (TRACE_RECORDER(cx
))
6759 AbortRecording(cx
, "error or exception while recording");
6760 # ifdef JS_METHODJIT
6761 if (TRACE_PROFILER(cx
))
6766 if (!cx
->isExceptionPending()) {
6767 /* This is an error, not a catchable exception, quit the frame ASAP. */
6768 interpReturnOK
= JS_FALSE
;
6770 JSThrowHook handler
;
6771 JSTryNote
*tn
, *tnlimit
;
6774 /* Restore atoms local in case we will resume. */
6775 atoms
= script
->atomMap
.vector
;
6777 /* Call debugger throw hook if set. */
6778 handler
= cx
->debugHooks
->throwHook
;
6781 switch (handler(cx
, script
, regs
.pc
, Jsvalify(&rval
),
6782 cx
->debugHooks
->throwHookData
)) {
6784 cx
->clearPendingException();
6787 cx
->clearPendingException();
6788 regs
.fp
->setReturnValue(rval
);
6789 interpReturnOK
= JS_TRUE
;
6792 cx
->setPendingException(rval
);
6793 case JSTRAP_CONTINUE
:
6796 CHECK_INTERRUPT_HANDLER();
6800 * Look for a try block in script that can catch this exception.
6802 if (!JSScript::isValidOffset(script
->trynotesOffset
))
6805 offset
= (uint32
)(regs
.pc
- script
->main
);
6806 tn
= script
->trynotes()->vector
;
6807 tnlimit
= tn
+ script
->trynotes()->length
;
6809 if (offset
- tn
->start
>= tn
->length
)
6813 * We have a note that covers the exception pc but we must check
6814 * whether the interpreter has already executed the corresponding
6815 * handler. This is possible when the executed bytecode
6816 * implements break or return from inside a for-in loop.
6818 * In this case the emitter generates additional [enditer] and
6819 * [gosub] opcodes to close all outstanding iterators and execute
6820 * the finally blocks. If such an [enditer] throws an exception,
6821 * its pc can still be inside several nested for-in loops and
6822 * try-finally statements even if we have already closed the
6823 * corresponding iterators and invoked the finally blocks.
6825 * To address this, we make [enditer] always decrease the stack
6826 * even when its implementation throws an exception. Thus already
6827 * executed [enditer] and [gosub] opcodes will have try notes
6828 * with the stack depth exceeding the current one and this
6829 * condition is what we use to filter them out.
6831 if (tn
->stackDepth
> regs
.sp
- regs
.fp
->base())
6835 * Set pc to the first bytecode after the the try note to point
6836 * to the beginning of catch or finally or to [enditer] closing
6839 regs
.pc
= (script
)->main
+ tn
->start
+ tn
->length
;
6841 JSBool ok
= js_UnwindScope(cx
, tn
->stackDepth
, JS_TRUE
);
6842 JS_ASSERT(regs
.sp
== regs
.fp
->base() + tn
->stackDepth
);
6845 * Restart the handler search with updated pc and stack depth
6846 * to properly notify the debugger.
6853 #if JS_HAS_GENERATORS
6854 /* Catch cannot intercept the closing of a generator. */
6855 if (JS_UNLIKELY(cx
->getPendingException().isMagic(JS_GENERATOR_CLOSING
)))
6860 * Don't clear exceptions to save cx->exception from GC
6861 * until it is pushed to the stack via [exception] in the
6869 * Push (true, exception) pair for finally to indicate that
6870 * [retsub] should rethrow the exception.
6873 PUSH_COPY(cx
->getPendingException());
6874 cx
->clearPendingException();
6879 /* This is similar to JSOP_ENDITER in the interpreter loop. */
6880 JS_ASSERT(js_GetOpcode(cx
, regs
.fp
->script(), regs
.pc
) == JSOP_ENDITER
);
6881 Value v
= cx
->getPendingException();
6882 cx
->clearPendingException();
6883 ok
= js_CloseIterator(cx
, ®s
.sp
[-1].toObject());
6887 cx
->setPendingException(v
);
6890 } while (++tn
!= tnlimit
);
6894 * Propagate the exception or error to the caller unless the exception
6895 * is an asynchronous return from a generator.
6897 interpReturnOK
= JS_FALSE
;
6898 #if JS_HAS_GENERATORS
6899 if (JS_UNLIKELY(cx
->isExceptionPending() &&
6900 cx
->getPendingException().isMagic(JS_GENERATOR_CLOSING
))) {
6901 cx
->clearPendingException();
6902 interpReturnOK
= JS_TRUE
;
6903 regs
.fp
->clearReturnValue();
6910 * Unwind the scope making sure that interpReturnOK stays false even when
6911 * js_UnwindScope returns true.
6913 * When a trap handler returns JSTRAP_RETURN, we jump here with
6914 * interpReturnOK set to true bypassing any finally blocks.
6916 interpReturnOK
&= js_UnwindScope(cx
, 0, interpReturnOK
|| cx
->isExceptionPending());
6917 JS_ASSERT(regs
.sp
== regs
.fp
->base());
6920 cx
->logPrevPc
= NULL
;
6923 if (entryFrame
!= regs
.fp
)
6927 interpReturnOK
= ScriptEpilogue(cx
, regs
.fp
, interpReturnOK
);
6928 regs
.fp
->setFinishedInInterpreter();
6931 * At this point we are inevitably leaving an interpreted function or a
6932 * top-level script, and returning to one of:
6933 * (a) an "out of line" call made through js_Invoke;
6934 * (b) a js_Execute activation;
6935 * (c) a generator (SendToGenerator, jsiter.c).
6937 * We must not be in an inline frame. The check above ensures that for the
6938 * error case and for a normal return, the code jumps directly to parent's
6941 JS_ASSERT(entryFrame
== regs
.fp
);
6944 JS_ASSERT_IF(interpReturnOK
&& interpMode
== JSINTERP_RECORD
, !TRACE_RECORDER(cx
));
6945 if (TRACE_RECORDER(cx
))
6946 AbortRecording(cx
, "recording out of Interpret");
6947 # ifdef JS_METHODJIT
6948 if (TRACE_PROFILER(cx
))
6953 JS_ASSERT_IF(!regs
.fp
->isGeneratorFrame(), !js_IsActiveWithOrBlock(cx
, ®s
.fp
->scopeChain(), 0));
6955 return interpReturnOK
;
6959 JSAutoByteString printable
;
6960 if (js_AtomToPrintableString(cx
, atomNotDefined
, &printable
))
6961 js_ReportIsNotDefined(cx
, printable
.ptr());
6966 * This path is used when it's guaranteed the method can be finished
6969 #if defined(JS_METHODJIT)
6970 leave_on_safe_point
:
6972 return interpReturnOK
;
6975 } /* namespace js */
6977 #endif /* !defined jsinvoke_cpp___ */