Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * JavaScript bytecode interpreter.
9 */
11 #include "vm/Interpreter-inl.h"
13 #include "mozilla/DebugOnly.h"
14 #include "mozilla/FloatingPoint.h"
15 #include "mozilla/PodOperations.h"
17 #include <string.h>
19 #include "jsarray.h"
20 #include "jsatom.h"
21 #include "jscntxt.h"
22 #include "jsfun.h"
23 #include "jsgc.h"
24 #include "jsiter.h"
25 #include "jslibmath.h"
26 #include "jsnum.h"
27 #include "jsobj.h"
28 #include "jsopcode.h"
29 #include "jsprf.h"
30 #include "jsscript.h"
31 #include "jsstr.h"
33 #include "builtin/Eval.h"
34 #include "jit/BaselineJIT.h"
35 #include "jit/Ion.h"
36 #include "jit/IonAnalysis.h"
37 #include "js/OldDebugAPI.h"
38 #include "vm/Debugger.h"
39 #include "vm/Opcodes.h"
40 #include "vm/Shape.h"
41 #include "vm/TraceLogging.h"
43 #include "jsatominlines.h"
44 #include "jsboolinlines.h"
45 #include "jsfuninlines.h"
46 #include "jsinferinlines.h"
47 #include "jsscriptinlines.h"
49 #include "jit/IonFrames-inl.h"
50 #include "vm/Probes-inl.h"
51 #include "vm/ScopeObject-inl.h"
52 #include "vm/Stack-inl.h"
54 using namespace js;
55 using namespace js::gc;
56 using namespace js::types;
58 using mozilla::DebugOnly;
59 using mozilla::NumberEqualsInt32;
60 using mozilla::PodCopy;
61 using JS::ForOfIterator;
63 /*
64 * Note: when Clang 3.2 (32-bit) inlines the two functions below in Interpret,
65 * the conservative stack scanner leaks a ton of memory and this negatively
66 * influences performance. The MOZ_NEVER_INLINE is a temporary workaround until
67 * we can remove the conservative scanner. See bug 849526 for more info.
68 */
69 #if defined(__clang__) && defined(JS_CPU_X86)
70 static MOZ_NEVER_INLINE bool
71 #else
72 static bool
73 #endif
74 ToBooleanOp(const InterpreterRegs ®s)
75 {
76 return ToBoolean(regs.stackHandleAt(-1));
77 }
79 template <bool Eq>
80 #if defined(__clang__) && defined(JS_CPU_X86)
81 static MOZ_NEVER_INLINE bool
82 #else
83 static bool
84 #endif
85 LooseEqualityOp(JSContext *cx, InterpreterRegs ®s)
86 {
87 HandleValue rval = regs.stackHandleAt(-1);
88 HandleValue lval = regs.stackHandleAt(-2);
89 bool cond;
90 if (!LooselyEqual(cx, lval, rval, &cond))
91 return false;
92 cond = (cond == Eq);
93 regs.sp--;
94 regs.sp[-1].setBoolean(cond);
95 return true;
96 }
98 JSObject *
99 js::BoxNonStrictThis(JSContext *cx, HandleValue thisv)
100 {
101 /*
102 * Check for SynthesizeFrame poisoning and fast constructors which
103 * didn't check their callee properly.
104 */
105 JS_ASSERT(!thisv.isMagic());
107 if (thisv.isNullOrUndefined()) {
108 Rooted<GlobalObject*> global(cx, cx->global());
109 return JSObject::thisObject(cx, global);
110 }
112 if (thisv.isObject())
113 return &thisv.toObject();
115 return PrimitiveToObject(cx, thisv);
116 }
118 /*
119 * ECMA requires "the global object", but in embeddings such as the browser,
120 * which have multiple top-level objects (windows, frames, etc. in the DOM),
121 * we prefer fun's parent. An example that causes this code to run:
122 *
123 * // in window w1
124 * function f() { return this }
125 * function g() { return f }
126 *
127 * // in window w2
128 * var h = w1.g()
129 * alert(h() == w1)
130 *
131 * The alert should display "true".
132 */
133 bool
134 js::BoxNonStrictThis(JSContext *cx, const CallReceiver &call)
135 {
136 /*
137 * Check for SynthesizeFrame poisoning and fast constructors which
138 * didn't check their callee properly.
139 */
140 JS_ASSERT(!call.thisv().isMagic());
142 #ifdef DEBUG
143 JSFunction *fun = call.callee().is<JSFunction>() ? &call.callee().as<JSFunction>() : nullptr;
144 JS_ASSERT_IF(fun && fun->isInterpreted(), !fun->strict());
145 #endif
147 JSObject *thisObj = BoxNonStrictThis(cx, call.thisv());
148 if (!thisObj)
149 return false;
151 call.setThis(ObjectValue(*thisObj));
152 return true;
153 }
155 #if JS_HAS_NO_SUCH_METHOD
157 static const uint32_t JSSLOT_FOUND_FUNCTION = 0;
158 static const uint32_t JSSLOT_SAVED_ID = 1;
160 static const Class js_NoSuchMethodClass = {
161 "NoSuchMethod",
162 JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
163 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
164 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
165 };
167 /*
168 * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of
169 * the base object, we search for the __noSuchMethod__ method in the base.
170 * If it exists, we store the method and the property's id into an object of
171 * NoSuchMethod class and store this object into the callee's stack slot.
172 * Later, Invoke will recognise such an object and transfer control to
173 * NoSuchMethod that invokes the method like:
174 *
175 * this.__noSuchMethod__(id, args)
176 *
177 * where id is the name of the method that this invocation attempted to
178 * call by name, and args is an Array containing this invocation's actual
179 * parameters.
180 */
181 bool
182 js::OnUnknownMethod(JSContext *cx, HandleObject obj, Value idval_, MutableHandleValue vp)
183 {
184 RootedValue idval(cx, idval_);
186 RootedValue value(cx);
187 if (!JSObject::getProperty(cx, obj, obj, cx->names().noSuchMethod, &value))
188 return false;
190 if (value.isObject()) {
191 JSObject *obj = NewObjectWithClassProto(cx, &js_NoSuchMethodClass, nullptr, nullptr);
192 if (!obj)
193 return false;
195 obj->setSlot(JSSLOT_FOUND_FUNCTION, value);
196 obj->setSlot(JSSLOT_SAVED_ID, idval);
197 vp.setObject(*obj);
198 }
199 return true;
200 }
202 static bool
203 NoSuchMethod(JSContext *cx, unsigned argc, Value *vp)
204 {
205 InvokeArgs args(cx);
206 if (!args.init(2))
207 return false;
209 JS_ASSERT(vp[0].isObject());
210 JS_ASSERT(vp[1].isObject());
211 JSObject *obj = &vp[0].toObject();
212 JS_ASSERT(obj->getClass() == &js_NoSuchMethodClass);
214 args.setCallee(obj->getReservedSlot(JSSLOT_FOUND_FUNCTION));
215 args.setThis(vp[1]);
216 args[0].set(obj->getReservedSlot(JSSLOT_SAVED_ID));
217 JSObject *argsobj = NewDenseCopiedArray(cx, argc, vp + 2);
218 if (!argsobj)
219 return false;
220 args[1].setObject(*argsobj);
221 bool ok = Invoke(cx, args);
222 vp[0] = args.rval();
223 return ok;
224 }
226 #endif /* JS_HAS_NO_SUCH_METHOD */
228 static inline bool
229 GetPropertyOperation(JSContext *cx, InterpreterFrame *fp, HandleScript script, jsbytecode *pc,
230 MutableHandleValue lval, MutableHandleValue vp)
231 {
232 JSOp op = JSOp(*pc);
234 if (op == JSOP_LENGTH) {
235 if (IsOptimizedArguments(fp, lval.address())) {
236 vp.setInt32(fp->numActualArgs());
237 return true;
238 }
240 if (GetLengthProperty(lval, vp))
241 return true;
242 }
244 Rooted<GlobalObject*> global(cx, &fp->global());
245 RootedId id(cx, NameToId(script->getName(pc)));
246 RootedObject obj(cx);
248 /* Optimize (.1).toString(). */
249 if (lval.isNumber() && id == NameToId(cx->names().toString)) {
250 JSObject *proto = GlobalObject::getOrCreateNumberPrototype(cx, global);
251 if (!proto)
252 return false;
253 if (ClassMethodIsNative(cx, proto, &NumberObject::class_, id, js_num_toString))
254 obj = proto;
255 }
257 if (!obj) {
258 obj = ToObjectFromStack(cx, lval);
259 if (!obj)
260 return false;
261 }
263 bool wasObject = lval.isObject();
265 if (!JSObject::getGeneric(cx, obj, obj, id, vp))
266 return false;
268 #if JS_HAS_NO_SUCH_METHOD
269 if (op == JSOP_CALLPROP &&
270 MOZ_UNLIKELY(vp.isUndefined()) &&
271 wasObject)
272 {
273 if (!OnUnknownMethod(cx, obj, IdToValue(id), vp))
274 return false;
275 }
276 #endif
278 return true;
279 }
281 static inline bool
282 NameOperation(JSContext *cx, InterpreterFrame *fp, jsbytecode *pc, MutableHandleValue vp)
283 {
284 JSObject *obj = fp->scopeChain();
285 PropertyName *name = fp->script()->getName(pc);
287 /*
288 * Skip along the scope chain to the enclosing global object. This is
289 * used for GNAME opcodes where the bytecode emitter has determined a
290 * name access must be on the global. It also insulates us from bugs
291 * in the emitter: type inference will assume that GNAME opcodes are
292 * accessing the global object, and the inferred behavior should match
293 * the actual behavior even if the id could be found on the scope chain
294 * before the global object.
295 */
296 if (IsGlobalOp(JSOp(*pc)))
297 obj = &obj->global();
299 Shape *shape = nullptr;
300 JSObject *scope = nullptr, *pobj = nullptr;
301 if (LookupNameNoGC(cx, name, obj, &scope, &pobj, &shape)) {
302 if (FetchNameNoGC(pobj, shape, vp))
303 return true;
304 }
306 RootedObject objRoot(cx, obj), scopeRoot(cx), pobjRoot(cx);
307 RootedPropertyName nameRoot(cx, name);
308 RootedShape shapeRoot(cx);
310 if (!LookupName(cx, nameRoot, objRoot, &scopeRoot, &pobjRoot, &shapeRoot))
311 return false;
313 /* Kludge to allow (typeof foo == "undefined") tests. */
314 JSOp op2 = JSOp(pc[JSOP_NAME_LENGTH]);
315 if (op2 == JSOP_TYPEOF)
316 return FetchName<true>(cx, scopeRoot, pobjRoot, nameRoot, shapeRoot, vp);
317 return FetchName<false>(cx, scopeRoot, pobjRoot, nameRoot, shapeRoot, vp);
318 }
320 static inline bool
321 SetPropertyOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue lval,
322 HandleValue rval)
323 {
324 JS_ASSERT(*pc == JSOP_SETPROP);
326 RootedObject obj(cx, ToObjectFromStack(cx, lval));
327 if (!obj)
328 return false;
330 RootedValue rref(cx, rval);
332 RootedId id(cx, NameToId(script->getName(pc)));
333 if (MOZ_LIKELY(!obj->getOps()->setProperty)) {
334 if (!baseops::SetPropertyHelper<SequentialExecution>(cx, obj, obj, id, baseops::Qualified,
335 &rref, script->strict()))
336 {
337 return false;
338 }
339 } else {
340 if (!JSObject::setGeneric(cx, obj, obj, id, &rref, script->strict()))
341 return false;
342 }
344 return true;
345 }
347 bool
348 js::ReportIsNotFunction(JSContext *cx, HandleValue v, int numToSkip, MaybeConstruct construct)
349 {
350 unsigned error = construct ? JSMSG_NOT_CONSTRUCTOR : JSMSG_NOT_FUNCTION;
351 int spIndex = numToSkip >= 0 ? -(numToSkip + 1) : JSDVG_SEARCH_STACK;
353 js_ReportValueError3(cx, error, spIndex, v, NullPtr(), nullptr, nullptr);
354 return false;
355 }
357 JSObject *
358 js::ValueToCallable(JSContext *cx, HandleValue v, int numToSkip, MaybeConstruct construct)
359 {
360 if (v.isObject()) {
361 JSObject *callable = &v.toObject();
362 if (callable->isCallable())
363 return callable;
364 }
366 ReportIsNotFunction(cx, v, numToSkip, construct);
367 return nullptr;
368 }
370 static MOZ_NEVER_INLINE bool
371 Interpret(JSContext *cx, RunState &state);
373 InterpreterFrame *
374 InvokeState::pushInterpreterFrame(JSContext *cx)
375 {
376 return cx->runtime()->interpreterStack().pushInvokeFrame(cx, args_, initial_);
377 }
379 InterpreterFrame *
380 ExecuteState::pushInterpreterFrame(JSContext *cx)
381 {
382 return cx->runtime()->interpreterStack().pushExecuteFrame(cx, script_, thisv_, scopeChain_,
383 type_, evalInFrame_);
384 }
386 bool
387 js::RunScript(JSContext *cx, RunState &state)
388 {
389 JS_CHECK_RECURSION(cx, return false);
391 SPSEntryMarker marker(cx->runtime());
393 state.script()->ensureNonLazyCanonicalFunction(cx);
395 #ifdef JS_ION
396 if (jit::IsIonEnabled(cx)) {
397 jit::MethodStatus status = jit::CanEnter(cx, state);
398 if (status == jit::Method_Error)
399 return false;
400 if (status == jit::Method_Compiled) {
401 jit::IonExecStatus status = jit::IonCannon(cx, state);
402 return !IsErrorStatus(status);
403 }
404 }
406 if (jit::IsBaselineEnabled(cx)) {
407 jit::MethodStatus status = jit::CanEnterBaselineMethod(cx, state);
408 if (status == jit::Method_Error)
409 return false;
410 if (status == jit::Method_Compiled) {
411 jit::IonExecStatus status = jit::EnterBaselineMethod(cx, state);
412 return !IsErrorStatus(status);
413 }
414 }
415 #endif
417 if (state.isInvoke()) {
418 InvokeState &invoke = *state.asInvoke();
419 TypeMonitorCall(cx, invoke.args(), invoke.constructing());
420 }
422 return Interpret(cx, state);
423 }
425 struct AutoGCIfNeeded
426 {
427 JSContext *cx_;
428 AutoGCIfNeeded(JSContext *cx) : cx_(cx) {}
429 ~AutoGCIfNeeded() { js::gc::GCIfNeeded(cx_); }
430 };
432 /*
433 * Find a function reference and its 'this' value implicit first parameter
434 * under argc arguments on cx's stack, and call the function. Push missing
435 * required arguments, allocate declared local variables, and pop everything
436 * when done. Then push the return value.
437 */
438 bool
439 js::Invoke(JSContext *cx, CallArgs args, MaybeConstruct construct)
440 {
441 JS_ASSERT(args.length() <= ARGS_LENGTH_MAX);
442 JS_ASSERT(!cx->compartment()->activeAnalysis);
444 /* We should never enter a new script while cx->iterValue is live. */
445 JS_ASSERT(cx->iterValue.isMagic(JS_NO_ITER_VALUE));
447 /* Perform GC if necessary on exit from the function. */
448 AutoGCIfNeeded gcIfNeeded(cx);
450 /* MaybeConstruct is a subset of InitialFrameFlags */
451 InitialFrameFlags initial = (InitialFrameFlags) construct;
453 if (args.calleev().isPrimitive())
454 return ReportIsNotFunction(cx, args.calleev(), args.length() + 1, construct);
456 JSObject &callee = args.callee();
457 const Class *clasp = callee.getClass();
459 /* Invoke non-functions. */
460 if (MOZ_UNLIKELY(clasp != &JSFunction::class_)) {
461 #if JS_HAS_NO_SUCH_METHOD
462 if (MOZ_UNLIKELY(clasp == &js_NoSuchMethodClass))
463 return NoSuchMethod(cx, args.length(), args.base());
464 #endif
465 JS_ASSERT_IF(construct, !clasp->construct);
466 if (!clasp->call)
467 return ReportIsNotFunction(cx, args.calleev(), args.length() + 1, construct);
468 return CallJSNative(cx, clasp->call, args);
469 }
471 /* Invoke native functions. */
472 JSFunction *fun = &callee.as<JSFunction>();
473 JS_ASSERT_IF(construct, !fun->isNativeConstructor());
474 if (fun->isNative())
475 return CallJSNative(cx, fun->native(), args);
477 if (!fun->getOrCreateScript(cx))
478 return false;
480 /* Run function until JSOP_RETRVAL, JSOP_RETURN or error. */
481 InvokeState state(cx, args, initial);
483 // Check to see if useNewType flag should be set for this frame.
484 if (construct) {
485 FrameIter iter(cx);
486 if (!iter.done() && iter.hasScript()) {
487 JSScript *script = iter.script();
488 jsbytecode *pc = iter.pc();
489 if (UseNewType(cx, script, pc))
490 state.setUseNewType();
491 }
492 }
494 bool ok = RunScript(cx, state);
496 JS_ASSERT_IF(ok && construct, !args.rval().isPrimitive());
497 return ok;
498 }
500 bool
501 js::Invoke(JSContext *cx, const Value &thisv, const Value &fval, unsigned argc, const Value *argv,
502 MutableHandleValue rval)
503 {
504 InvokeArgs args(cx);
505 if (!args.init(argc))
506 return false;
508 args.setCallee(fval);
509 args.setThis(thisv);
510 PodCopy(args.array(), argv, argc);
512 if (args.thisv().isObject()) {
513 /*
514 * We must call the thisObject hook in case we are not called from the
515 * interpreter, where a prior bytecode has computed an appropriate
516 * |this| already. But don't do that if fval is a DOM function.
517 */
518 if (!fval.isObject() || !fval.toObject().is<JSFunction>() ||
519 !fval.toObject().as<JSFunction>().isNative() ||
520 !fval.toObject().as<JSFunction>().jitInfo() ||
521 fval.toObject().as<JSFunction>().jitInfo()->needsOuterizedThisObject())
522 {
523 RootedObject thisObj(cx, &args.thisv().toObject());
524 JSObject *thisp = JSObject::thisObject(cx, thisObj);
525 if (!thisp)
526 return false;
527 args.setThis(ObjectValue(*thisp));
528 }
529 }
531 if (!Invoke(cx, args))
532 return false;
534 rval.set(args.rval());
535 return true;
536 }
538 bool
539 js::InvokeConstructor(JSContext *cx, CallArgs args)
540 {
541 JS_ASSERT(!JSFunction::class_.construct);
543 args.setThis(MagicValue(JS_IS_CONSTRUCTING));
545 if (!args.calleev().isObject())
546 return ReportIsNotFunction(cx, args.calleev(), args.length() + 1, CONSTRUCT);
548 JSObject &callee = args.callee();
549 if (callee.is<JSFunction>()) {
550 RootedFunction fun(cx, &callee.as<JSFunction>());
552 if (fun->isNativeConstructor()) {
553 bool ok = CallJSNativeConstructor(cx, fun->native(), args);
554 return ok;
555 }
557 if (!fun->isInterpretedConstructor()) {
558 RootedValue orig(cx, ObjectValue(*fun->originalFunction()));
559 return ReportIsNotFunction(cx, orig, args.length() + 1, CONSTRUCT);
560 }
561 if (!Invoke(cx, args, CONSTRUCT))
562 return false;
564 JS_ASSERT(args.rval().isObject());
565 return true;
566 }
568 const Class *clasp = callee.getClass();
569 if (!clasp->construct)
570 return ReportIsNotFunction(cx, args.calleev(), args.length() + 1, CONSTRUCT);
572 return CallJSNativeConstructor(cx, clasp->construct, args);
573 }
575 bool
576 js::InvokeConstructor(JSContext *cx, Value fval, unsigned argc, Value *argv, Value *rval)
577 {
578 InvokeArgs args(cx);
579 if (!args.init(argc))
580 return false;
582 args.setCallee(fval);
583 args.setThis(MagicValue(JS_THIS_POISON));
584 PodCopy(args.array(), argv, argc);
586 if (!InvokeConstructor(cx, args))
587 return false;
589 *rval = args.rval();
590 return true;
591 }
593 bool
594 js::InvokeGetterOrSetter(JSContext *cx, JSObject *obj, Value fval, unsigned argc,
595 Value *argv, MutableHandleValue rval)
596 {
597 /*
598 * Invoke could result in another try to get or set the same id again, see
599 * bug 355497.
600 */
601 JS_CHECK_RECURSION(cx, return false);
603 return Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval);
604 }
606 bool
607 js::ExecuteKernel(JSContext *cx, HandleScript script, JSObject &scopeChainArg, const Value &thisv,
608 ExecuteType type, AbstractFramePtr evalInFrame, Value *result)
609 {
610 JS_ASSERT_IF(evalInFrame, type == EXECUTE_DEBUG);
611 JS_ASSERT_IF(type == EXECUTE_GLOBAL, !scopeChainArg.is<ScopeObject>());
612 #ifdef DEBUG
613 if (thisv.isObject()) {
614 RootedObject thisObj(cx, &thisv.toObject());
615 AutoSuppressGC nogc(cx);
616 JS_ASSERT(GetOuterObject(cx, thisObj) == thisObj);
617 }
618 #endif
620 if (script->isEmpty()) {
621 if (result)
622 result->setUndefined();
623 return true;
624 }
626 TypeScript::SetThis(cx, script, thisv);
628 probes::StartExecution(script);
629 ExecuteState state(cx, script, thisv, scopeChainArg, type, evalInFrame, result);
630 bool ok = RunScript(cx, state);
631 probes::StopExecution(script);
633 return ok;
634 }
636 bool
637 js::Execute(JSContext *cx, HandleScript script, JSObject &scopeChainArg, Value *rval)
638 {
639 /* The scope chain could be anything, so innerize just in case. */
640 RootedObject scopeChain(cx, &scopeChainArg);
641 scopeChain = GetInnerObject(cx, scopeChain);
642 if (!scopeChain)
643 return false;
645 /* Ensure the scope chain is all same-compartment and terminates in a global. */
646 #ifdef DEBUG
647 JSObject *s = scopeChain;
648 do {
649 assertSameCompartment(cx, s);
650 JS_ASSERT_IF(!s->enclosingScope(), s->is<GlobalObject>());
651 } while ((s = s->enclosingScope()));
652 #endif
654 /* The VAROBJFIX option makes varObj == globalObj in global code. */
655 if (!cx->options().varObjFix()) {
656 if (!scopeChain->setVarObj(cx))
657 return false;
658 }
660 /* Use the scope chain as 'this', modulo outerization. */
661 JSObject *thisObj = JSObject::thisObject(cx, scopeChain);
662 if (!thisObj)
663 return false;
664 Value thisv = ObjectValue(*thisObj);
666 return ExecuteKernel(cx, script, *scopeChain, thisv, EXECUTE_GLOBAL,
667 NullFramePtr() /* evalInFrame */, rval);
668 }
670 bool
671 js::HasInstance(JSContext *cx, HandleObject obj, HandleValue v, bool *bp)
672 {
673 const Class *clasp = obj->getClass();
674 RootedValue local(cx, v);
675 if (clasp->hasInstance)
676 return clasp->hasInstance(cx, obj, &local, bp);
678 RootedValue val(cx, ObjectValue(*obj));
679 js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
680 JSDVG_SEARCH_STACK, val, NullPtr());
681 return false;
682 }
684 bool
685 js::LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, bool *result)
686 {
687 if (SameType(lval, rval)) {
688 if (lval.isString()) {
689 JSString *l = lval.toString();
690 JSString *r = rval.toString();
691 return EqualStrings(cx, l, r, result);
692 }
694 if (lval.isDouble()) {
695 double l = lval.toDouble(), r = rval.toDouble();
696 *result = (l == r);
697 return true;
698 }
700 if (lval.isObject()) {
701 JSObject *l = &lval.toObject();
702 JSObject *r = &rval.toObject();
703 *result = l == r;
704 return true;
705 }
707 *result = lval.payloadAsRawUint32() == rval.payloadAsRawUint32();
708 return true;
709 }
711 if (lval.isNullOrUndefined()) {
712 *result = rval.isNullOrUndefined() ||
713 (rval.isObject() && EmulatesUndefined(&rval.toObject()));
714 return true;
715 }
717 if (rval.isNullOrUndefined()) {
718 *result = (lval.isObject() && EmulatesUndefined(&lval.toObject()));
719 return true;
720 }
722 RootedValue lvalue(cx, lval);
723 RootedValue rvalue(cx, rval);
725 if (!ToPrimitive(cx, &lvalue))
726 return false;
727 if (!ToPrimitive(cx, &rvalue))
728 return false;
730 if (lvalue.get().isString() && rvalue.get().isString()) {
731 JSString *l = lvalue.get().toString();
732 JSString *r = rvalue.get().toString();
733 return EqualStrings(cx, l, r, result);
734 }
736 double l, r;
737 if (!ToNumber(cx, lvalue, &l) || !ToNumber(cx, rvalue, &r))
738 return false;
739 *result = (l == r);
740 return true;
741 }
743 bool
744 js::StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref, bool *equal)
745 {
746 Value lval = lref, rval = rref;
747 if (SameType(lval, rval)) {
748 if (lval.isString())
749 return EqualStrings(cx, lval.toString(), rval.toString(), equal);
750 if (lval.isDouble()) {
751 *equal = (lval.toDouble() == rval.toDouble());
752 return true;
753 }
754 if (lval.isObject()) {
755 *equal = lval.toObject() == rval.toObject();
756 return true;
757 }
758 if (lval.isUndefined()) {
759 *equal = true;
760 return true;
761 }
762 *equal = lval.payloadAsRawUint32() == rval.payloadAsRawUint32();
763 return true;
764 }
766 if (lval.isDouble() && rval.isInt32()) {
767 double ld = lval.toDouble();
768 double rd = rval.toInt32();
769 *equal = (ld == rd);
770 return true;
771 }
772 if (lval.isInt32() && rval.isDouble()) {
773 double ld = lval.toInt32();
774 double rd = rval.toDouble();
775 *equal = (ld == rd);
776 return true;
777 }
779 *equal = false;
780 return true;
781 }
783 static inline bool
784 IsNegativeZero(const Value &v)
785 {
786 return v.isDouble() && mozilla::IsNegativeZero(v.toDouble());
787 }
789 static inline bool
790 IsNaN(const Value &v)
791 {
792 return v.isDouble() && mozilla::IsNaN(v.toDouble());
793 }
795 bool
796 js::SameValue(JSContext *cx, const Value &v1, const Value &v2, bool *same)
797 {
798 if (IsNegativeZero(v1)) {
799 *same = IsNegativeZero(v2);
800 return true;
801 }
802 if (IsNegativeZero(v2)) {
803 *same = false;
804 return true;
805 }
806 if (IsNaN(v1) && IsNaN(v2)) {
807 *same = true;
808 return true;
809 }
810 return StrictlyEqual(cx, v1, v2, same);
811 }
813 JSType
814 js::TypeOfObject(JSObject *obj)
815 {
816 if (EmulatesUndefined(obj))
817 return JSTYPE_VOID;
818 if (obj->isCallable())
819 return JSTYPE_FUNCTION;
820 return JSTYPE_OBJECT;
821 }
823 JSType
824 js::TypeOfValue(const Value &v)
825 {
826 if (v.isNumber())
827 return JSTYPE_NUMBER;
828 if (v.isString())
829 return JSTYPE_STRING;
830 if (v.isNull())
831 return JSTYPE_OBJECT;
832 if (v.isUndefined())
833 return JSTYPE_VOID;
834 if (v.isObject())
835 return TypeOfObject(&v.toObject());
836 JS_ASSERT(v.isBoolean());
837 return JSTYPE_BOOLEAN;
838 }
840 /*
841 * Enter the new with scope using an object at sp[-1] and associate the depth
842 * of the with block with sp + stackIndex.
843 */
844 bool
845 js::EnterWithOperation(JSContext *cx, AbstractFramePtr frame, HandleValue val,
846 HandleObject staticWith)
847 {
848 JS_ASSERT(staticWith->is<StaticWithObject>());
849 RootedObject obj(cx);
850 if (val.isObject()) {
851 obj = &val.toObject();
852 } else {
853 obj = ToObject(cx, val);
854 if (!obj)
855 return false;
856 }
858 RootedObject scopeChain(cx, frame.scopeChain());
859 DynamicWithObject *withobj = DynamicWithObject::create(cx, obj, scopeChain, staticWith);
860 if (!withobj)
861 return false;
863 frame.pushOnScopeChain(*withobj);
864 return true;
865 }
867 // Unwind scope chain and iterator to match the static scope corresponding to
868 // the given bytecode position.
869 void
870 js::UnwindScope(JSContext *cx, ScopeIter &si, jsbytecode *pc)
871 {
872 if (si.done())
873 return;
875 Rooted<NestedScopeObject *> staticScope(cx, si.frame().script()->getStaticScope(pc));
877 for (; si.staticScope() != staticScope; ++si) {
878 switch (si.type()) {
879 case ScopeIter::Block:
880 if (cx->compartment()->debugMode())
881 DebugScopes::onPopBlock(cx, si);
882 if (si.staticBlock().needsClone())
883 si.frame().popBlock(cx);
884 break;
885 case ScopeIter::With:
886 si.frame().popWith(cx);
887 break;
888 case ScopeIter::Call:
889 case ScopeIter::StrictEvalScope:
890 break;
891 }
892 }
893 }
895 static void
896 ForcedReturn(JSContext *cx, ScopeIter &si, InterpreterRegs ®s)
897 {
898 UnwindScope(cx, si, regs.fp()->script()->main());
899 regs.setToEndOfScript();
900 }
902 static void
903 ForcedReturn(JSContext *cx, InterpreterRegs ®s)
904 {
905 ScopeIter si(regs.fp(), regs.pc, cx);
906 ForcedReturn(cx, si, regs);
907 }
909 void
910 js::UnwindForUncatchableException(JSContext *cx, const InterpreterRegs ®s)
911 {
912 /* c.f. the regular (catchable) TryNoteIter loop in HandleError. */
913 for (TryNoteIter tni(cx, regs); !tni.done(); ++tni) {
914 JSTryNote *tn = *tni;
915 if (tn->kind == JSTRY_ITER) {
916 Value *sp = regs.spForStackDepth(tn->stackDepth);
917 UnwindIteratorForUncatchableException(cx, &sp[-1].toObject());
918 }
919 }
920 }
922 TryNoteIter::TryNoteIter(JSContext *cx, const InterpreterRegs ®s)
923 : regs(regs),
924 script(cx, regs.fp()->script()),
925 pcOffset(regs.pc - script->main())
926 {
927 if (script->hasTrynotes()) {
928 tn = script->trynotes()->vector;
929 tnEnd = tn + script->trynotes()->length;
930 } else {
931 tn = tnEnd = nullptr;
932 }
933 settle();
934 }
936 void
937 TryNoteIter::operator++()
938 {
939 ++tn;
940 settle();
941 }
943 bool
944 TryNoteIter::done() const
945 {
946 return tn == tnEnd;
947 }
949 void
950 TryNoteIter::settle()
951 {
952 for (; tn != tnEnd; ++tn) {
953 /* If pc is out of range, try the next one. */
954 if (pcOffset - tn->start >= tn->length)
955 continue;
957 /*
958 * We have a note that covers the exception pc but we must check
959 * whether the interpreter has already executed the corresponding
960 * handler. This is possible when the executed bytecode implements
961 * break or return from inside a for-in loop.
962 *
963 * In this case the emitter generates additional [enditer] and [gosub]
964 * opcodes to close all outstanding iterators and execute the finally
965 * blocks. If such an [enditer] throws an exception, its pc can still
966 * be inside several nested for-in loops and try-finally statements
967 * even if we have already closed the corresponding iterators and
968 * invoked the finally blocks.
969 *
970 * To address this, we make [enditer] always decrease the stack even
971 * when its implementation throws an exception. Thus already executed
972 * [enditer] and [gosub] opcodes will have try notes with the stack
973 * depth exceeding the current one and this condition is what we use to
974 * filter them out.
975 */
976 if (tn->stackDepth <= regs.stackDepth())
977 break;
978 }
979 }
981 enum HandleErrorContinuation
982 {
983 SuccessfulReturnContinuation,
984 ErrorReturnContinuation,
985 CatchContinuation,
986 FinallyContinuation
987 };
989 static HandleErrorContinuation
990 HandleError(JSContext *cx, InterpreterRegs ®s)
991 {
992 JS_ASSERT(regs.fp()->script()->containsPC(regs.pc));
994 ScopeIter si(regs.fp(), regs.pc, cx);
995 bool ok = false;
997 again:
998 if (cx->isExceptionPending()) {
999 /* Call debugger throw hooks. */
1000 if (MOZ_UNLIKELY(cx->compartment()->debugMode())) {
1001 JSTrapStatus status = DebugExceptionUnwind(cx, regs.fp(), regs.pc);
1002 switch (status) {
1003 case JSTRAP_ERROR:
1004 goto again;
1006 case JSTRAP_CONTINUE:
1007 case JSTRAP_THROW:
1008 break;
1010 case JSTRAP_RETURN:
1011 ForcedReturn(cx, si, regs);
1012 return SuccessfulReturnContinuation;
1014 default:
1015 MOZ_ASSUME_UNREACHABLE("Invalid trap status");
1016 }
1017 }
1019 RootedValue exception(cx);
1020 for (TryNoteIter tni(cx, regs); !tni.done(); ++tni) {
1021 JSTryNote *tn = *tni;
1023 UnwindScope(cx, si, regs.fp()->script()->main() + tn->start);
1025 /*
1026 * Set pc to the first bytecode after the the try note to point
1027 * to the beginning of catch or finally or to [enditer] closing
1028 * the for-in loop.
1029 */
1030 regs.pc = regs.fp()->script()->main() + tn->start + tn->length;
1031 regs.sp = regs.spForStackDepth(tn->stackDepth);
1033 switch (tn->kind) {
1034 case JSTRY_CATCH:
1035 /* Catch cannot intercept the closing of a generator. */
1036 if (!cx->getPendingException(&exception))
1037 return ErrorReturnContinuation;
1038 if (exception.isMagic(JS_GENERATOR_CLOSING))
1039 break;
1040 return CatchContinuation;
1042 case JSTRY_FINALLY:
1043 return FinallyContinuation;
1045 case JSTRY_ITER: {
1046 /* This is similar to JSOP_ENDITER in the interpreter loop. */
1047 JS_ASSERT(JSOp(*regs.pc) == JSOP_ENDITER);
1048 RootedObject obj(cx, ®s.sp[-1].toObject());
1049 bool ok = UnwindIteratorForException(cx, obj);
1050 regs.sp -= 1;
1051 if (!ok)
1052 goto again;
1053 break;
1054 }
1056 case JSTRY_LOOP:
1057 break;
1058 }
1059 }
1061 /*
1062 * Propagate the exception or error to the caller unless the exception
1063 * is an asynchronous return from a generator.
1064 */
1065 if (cx->isExceptionPending()) {
1066 RootedValue exception(cx);
1067 if (!cx->getPendingException(&exception))
1068 return ErrorReturnContinuation;
1070 if (exception.isMagic(JS_GENERATOR_CLOSING)) {
1071 cx->clearPendingException();
1072 ok = true;
1073 regs.fp()->clearReturnValue();
1074 }
1075 }
1076 } else {
1077 UnwindForUncatchableException(cx, regs);
1078 }
1080 ForcedReturn(cx, si, regs);
1081 return ok ? SuccessfulReturnContinuation : ErrorReturnContinuation;
1082 }
1084 #define REGS (activation.regs())
1085 #define PUSH_COPY(v) do { *REGS.sp++ = (v); assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); } while (0)
1086 #define PUSH_COPY_SKIP_CHECK(v) *REGS.sp++ = (v)
1087 #define PUSH_NULL() REGS.sp++->setNull()
1088 #define PUSH_UNDEFINED() REGS.sp++->setUndefined()
1089 #define PUSH_BOOLEAN(b) REGS.sp++->setBoolean(b)
1090 #define PUSH_DOUBLE(d) REGS.sp++->setDouble(d)
1091 #define PUSH_INT32(i) REGS.sp++->setInt32(i)
1092 #define PUSH_STRING(s) do { REGS.sp++->setString(s); assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); } while (0)
1093 #define PUSH_OBJECT(obj) do { REGS.sp++->setObject(obj); assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); } while (0)
1094 #define PUSH_OBJECT_OR_NULL(obj) do { REGS.sp++->setObjectOrNull(obj); assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); } while (0)
1095 #define PUSH_HOLE() REGS.sp++->setMagic(JS_ELEMENTS_HOLE)
1096 #define POP_COPY_TO(v) (v) = *--REGS.sp
1097 #define POP_RETURN_VALUE() REGS.fp()->setReturnValue(*--REGS.sp)
1099 #define FETCH_OBJECT(cx, n, obj) \
1100 JS_BEGIN_MACRO \
1101 HandleValue val = REGS.stackHandleAt(n); \
1102 obj = ToObjectFromStack((cx), (val)); \
1103 if (!obj) \
1104 goto error; \
1105 JS_END_MACRO
1107 /*
1108 * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
1109 * remain distinct for the decompiler.
1110 */
1111 JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
1113 /* See TRY_BRANCH_AFTER_COND. */
1114 JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH);
1115 JS_STATIC_ASSERT(JSOP_IFNE == JSOP_IFEQ + 1);
1117 /*
1118 * Inline fast paths for iteration. js_IteratorMore and js_IteratorNext handle
1119 * all cases, but we inline the most frequently taken paths here.
1120 */
1121 bool
1122 js::IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, MutableHandleValue rval)
1123 {
1124 if (iterobj->is<PropertyIteratorObject>()) {
1125 NativeIterator *ni = iterobj->as<PropertyIteratorObject>().getNativeIterator();
1126 if (ni->isKeyIter()) {
1127 *cond = (ni->props_cursor < ni->props_end);
1128 return true;
1129 }
1130 }
1131 Rooted<JSObject*> iobj(cx, iterobj);
1132 if (!js_IteratorMore(cx, iobj, rval))
1133 return false;
1134 *cond = rval.isTrue();
1135 return true;
1136 }
1138 bool
1139 js::IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval)
1140 {
1141 if (iterobj->is<PropertyIteratorObject>()) {
1142 NativeIterator *ni = iterobj->as<PropertyIteratorObject>().getNativeIterator();
1143 if (ni->isKeyIter()) {
1144 JS_ASSERT(ni->props_cursor < ni->props_end);
1145 rval.setString(*ni->current());
1146 ni->incCursor();
1147 return true;
1148 }
1149 }
1150 return js_IteratorNext(cx, iterobj, rval);
1151 }
1153 /*
1154 * Compute the implicit |this| parameter for a call expression where the callee
1155 * funval was resolved from an unqualified name reference to a property on obj
1156 * (an object on the scope chain).
1157 *
1158 * We can avoid computing |this| eagerly and push the implicit callee-coerced
1159 * |this| value, undefined, if any of these conditions hold:
1160 *
1161 * 1. The nominal |this|, obj, is a global object.
1162 *
1163 * 2. The nominal |this|, obj, has one of Block, Call, or DeclEnv class (this
1164 * is what IsCacheableNonGlobalScope tests). Such objects-as-scopes must be
1165 * censored with undefined.
1166 *
1167 * Otherwise, we bind |this| to obj->thisObject(). Only names inside |with|
1168 * statements and embedding-specific scope objects fall into this category.
1169 *
1170 * If the callee is a strict mode function, then code implementing JSOP_THIS
1171 * in the interpreter and JITs will leave undefined as |this|. If funval is a
1172 * function not in strict mode, JSOP_THIS code replaces undefined with funval's
1173 * global.
1174 *
1175 * We set *vp to undefined early to reduce code size and bias this code for the
1176 * common and future-friendly cases.
1177 */
1178 static inline bool
1179 ComputeImplicitThis(JSContext *cx, HandleObject obj, MutableHandleValue vp)
1180 {
1181 vp.setUndefined();
1183 if (obj->is<GlobalObject>())
1184 return true;
1186 if (IsCacheableNonGlobalScope(obj))
1187 return true;
1189 JSObject *nobj = JSObject::thisObject(cx, obj);
1190 if (!nobj)
1191 return false;
1193 vp.setObject(*nobj);
1194 return true;
1195 }
1197 static MOZ_ALWAYS_INLINE bool
1198 AddOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res)
1199 {
1200 if (lhs.isInt32() && rhs.isInt32()) {
1201 int32_t l = lhs.toInt32(), r = rhs.toInt32();
1202 int32_t t;
1203 if (MOZ_LIKELY(SafeAdd(l, r, &t))) {
1204 res.setInt32(t);
1205 return true;
1206 }
1207 }
1209 if (!ToPrimitive(cx, lhs))
1210 return false;
1211 if (!ToPrimitive(cx, rhs))
1212 return false;
1214 bool lIsString, rIsString;
1215 if ((lIsString = lhs.isString()) | (rIsString = rhs.isString())) {
1216 JSString *lstr, *rstr;
1217 if (lIsString) {
1218 lstr = lhs.toString();
1219 } else {
1220 lstr = ToString<CanGC>(cx, lhs);
1221 if (!lstr)
1222 return false;
1223 }
1224 if (rIsString) {
1225 rstr = rhs.toString();
1226 } else {
1227 // Save/restore lstr in case of GC activity under ToString.
1228 lhs.setString(lstr);
1229 rstr = ToString<CanGC>(cx, rhs);
1230 if (!rstr)
1231 return false;
1232 lstr = lhs.toString();
1233 }
1234 JSString *str = ConcatStrings<NoGC>(cx, lstr, rstr);
1235 if (!str) {
1236 RootedString nlstr(cx, lstr), nrstr(cx, rstr);
1237 str = ConcatStrings<CanGC>(cx, nlstr, nrstr);
1238 if (!str)
1239 return false;
1240 }
1241 res.setString(str);
1242 } else {
1243 double l, r;
1244 if (!ToNumber(cx, lhs, &l) || !ToNumber(cx, rhs, &r))
1245 return false;
1246 res.setNumber(l + r);
1247 }
1249 return true;
1250 }
1252 static MOZ_ALWAYS_INLINE bool
1253 SubOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, MutableHandleValue res)
1254 {
1255 double d1, d2;
1256 if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
1257 return false;
1258 res.setNumber(d1 - d2);
1259 return true;
1260 }
1262 static MOZ_ALWAYS_INLINE bool
1263 MulOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, MutableHandleValue res)
1264 {
1265 double d1, d2;
1266 if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
1267 return false;
1268 res.setNumber(d1 * d2);
1269 return true;
1270 }
1272 static MOZ_ALWAYS_INLINE bool
1273 DivOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, MutableHandleValue res)
1274 {
1275 double d1, d2;
1276 if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
1277 return false;
1278 res.setNumber(NumberDiv(d1, d2));
1279 return true;
1280 }
1282 static MOZ_ALWAYS_INLINE bool
1283 ModOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, MutableHandleValue res)
1284 {
1285 int32_t l, r;
1286 if (lhs.isInt32() && rhs.isInt32() &&
1287 (l = lhs.toInt32()) >= 0 && (r = rhs.toInt32()) > 0) {
1288 int32_t mod = l % r;
1289 res.setInt32(mod);
1290 return true;
1291 }
1293 double d1, d2;
1294 if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
1295 return false;
1297 res.setNumber(NumberMod(d1, d2));
1298 return true;
1299 }
1301 static MOZ_ALWAYS_INLINE bool
1302 SetObjectElementOperation(JSContext *cx, Handle<JSObject*> obj, HandleId id, const Value &value,
1303 bool strict, JSScript *script = nullptr, jsbytecode *pc = nullptr)
1304 {
1305 types::TypeScript::MonitorAssign(cx, obj, id);
1307 #ifdef JS_ION
1308 if (obj->isNative() && JSID_IS_INT(id)) {
1309 uint32_t length = obj->getDenseInitializedLength();
1310 int32_t i = JSID_TO_INT(id);
1311 if ((uint32_t)i >= length) {
1312 // Annotate script if provided with information (e.g. baseline)
1313 if (script && script->hasBaselineScript() && *pc == JSOP_SETELEM)
1314 script->baselineScript()->noteArrayWriteHole(script->pcToOffset(pc));
1315 }
1316 }
1317 #endif
1319 if (obj->isNative() && !JSID_IS_INT(id) && !obj->setHadElementsAccess(cx))
1320 return false;
1322 RootedValue tmp(cx, value);
1323 return JSObject::setGeneric(cx, obj, obj, id, &tmp, strict);
1324 }
1326 static MOZ_NEVER_INLINE bool
1327 Interpret(JSContext *cx, RunState &state)
1328 {
1329 /*
1330 * Define macros for an interpreter loop. Opcode dispatch may be either by a
1331 * switch statement or by indirect goto (aka a threaded interpreter), depending
1332 * on compiler support.
1333 *
1334 * Threaded interpretation appears to be well-supported by GCC 3 and higher.
1335 * IBM's C compiler when run with the right options (e.g., -qlanglvl=extended)
1336 * also supports threading. Ditto the SunPro C compiler.
1337 */
1338 #if (__GNUC__ >= 3 || \
1339 (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \
1340 __SUNPRO_C >= 0x570)
1341 // Non-standard but faster indirect-goto-based dispatch.
1342 # define INTERPRETER_LOOP()
1343 # define CASE(OP) label_##OP:
1344 # define DEFAULT() label_default:
1345 # define DISPATCH_TO(OP) goto *addresses[(OP)]
1347 # define LABEL(X) (&&label_##X)
1349 // Use addresses instead of offsets to optimize for runtime speed over
1350 // load-time relocation overhead.
1351 static const void *const addresses[EnableInterruptsPseudoOpcode + 1] = {
1352 # define OPCODE_LABEL(op, ...) LABEL(op),
1353 FOR_EACH_OPCODE(OPCODE_LABEL)
1354 # undef OPCODE_LABEL
1355 # define TRAILING_LABEL(v) \
1356 ((v) == EnableInterruptsPseudoOpcode \
1357 ? LABEL(EnableInterruptsPseudoOpcode) \
1358 : LABEL(default)),
1359 FOR_EACH_TRAILING_UNUSED_OPCODE(TRAILING_LABEL)
1360 # undef TRAILING_LABEL
1361 };
1362 #else
1363 // Portable switch-based dispatch.
1364 # define INTERPRETER_LOOP() the_switch: switch (switchOp)
1365 # define CASE(OP) case OP:
1366 # define DEFAULT() default:
1367 # define DISPATCH_TO(OP) \
1368 JS_BEGIN_MACRO \
1369 switchOp = (OP); \
1370 goto the_switch; \
1371 JS_END_MACRO
1373 // This variable is effectively a parameter to the_switch.
1374 jsbytecode switchOp;
1375 #endif
1377 /*
1378 * Increment REGS.pc by N, load the opcode at that position,
1379 * and jump to the code to execute it.
1380 *
1381 * When Debugger puts a script in single-step mode, all js::Interpret
1382 * invocations that might be presently running that script must have
1383 * interrupts enabled. It's not practical to simply check
1384 * script->stepModeEnabled() at each point some callee could have changed
1385 * it, because there are so many places js::Interpret could possibly cause
1386 * JavaScript to run: each place an object might be coerced to a primitive
1387 * or a number, for example. So instead, we expose a simple mechanism to
1388 * let Debugger tweak the affected js::Interpret frames when an onStep
1389 * handler is added: calling activation.enableInterruptsUnconditionally()
1390 * will enable interrupts, and activation.opMask() is or'd with the opcode
1391 * to implement a simple alternate dispatch.
1392 */
1393 #define ADVANCE_AND_DISPATCH(N) \
1394 JS_BEGIN_MACRO \
1395 REGS.pc += (N); \
1396 SANITY_CHECKS(); \
1397 DISPATCH_TO(*REGS.pc | activation.opMask()); \
1398 JS_END_MACRO
1400 /*
1401 * Shorthand for the common sequence at the end of a fixed-size opcode.
1402 */
1403 #define END_CASE(OP) ADVANCE_AND_DISPATCH(OP##_LENGTH);
1405 /*
1406 * Prepare to call a user-supplied branch handler, and abort the script
1407 * if it returns false.
1408 */
1409 #define CHECK_BRANCH() \
1410 JS_BEGIN_MACRO \
1411 if (!CheckForInterrupt(cx)) \
1412 goto error; \
1413 JS_END_MACRO
1415 /*
1416 * This is a simple wrapper around ADVANCE_AND_DISPATCH which also does
1417 * a CHECK_BRANCH() if n is not positive, which possibly indicates that it
1418 * is the backedge of a loop.
1419 */
1420 #define BRANCH(n) \
1421 JS_BEGIN_MACRO \
1422 int32_t nlen = (n); \
1423 if (nlen <= 0) \
1424 CHECK_BRANCH(); \
1425 ADVANCE_AND_DISPATCH(nlen); \
1426 JS_END_MACRO
1428 #define LOAD_DOUBLE(PCOFF, dbl) \
1429 ((dbl) = script->getConst(GET_UINT32_INDEX(REGS.pc + (PCOFF))).toDouble())
1431 #define SET_SCRIPT(s) \
1432 JS_BEGIN_MACRO \
1433 script = (s); \
1434 if (script->hasAnyBreakpointsOrStepMode() || script->hasScriptCounts()) \
1435 activation.enableInterruptsUnconditionally(); \
1436 JS_END_MACRO
1438 #define SANITY_CHECKS() \
1439 JS_BEGIN_MACRO \
1440 js::gc::MaybeVerifyBarriers(cx); \
1441 JS_ASSERT_IF(script->hasScriptCounts(), \
1442 activation.opMask() == EnableInterruptsPseudoOpcode); \
1443 JS_END_MACRO
1445 gc::MaybeVerifyBarriers(cx, true);
1446 JS_ASSERT(!cx->compartment()->activeAnalysis);
1448 InterpreterFrame *entryFrame = state.pushInterpreterFrame(cx);
1449 if (!entryFrame)
1450 return false;
1452 InterpreterActivation activation(state, cx, entryFrame);
1454 /* The script is used frequently, so keep a local copy. */
1455 RootedScript script(cx);
1456 SET_SCRIPT(REGS.fp()->script());
1458 TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
1459 uint32_t scriptLogId = TraceLogCreateTextId(logger, script);
1460 TraceLogStartEvent(logger, scriptLogId);
1461 TraceLogStartEvent(logger, TraceLogger::Interpreter);
1463 /*
1464 * Pool of rooters for use in this interpreter frame. References to these
1465 * are used for local variables within interpreter cases. This avoids
1466 * creating new rooters each time an interpreter case is entered, and also
1467 * correctness pitfalls due to incorrect compilation of destructor calls
1468 * around computed gotos.
1469 */
1470 RootedValue rootValue0(cx), rootValue1(cx);
1471 RootedString rootString0(cx), rootString1(cx);
1472 RootedObject rootObject0(cx), rootObject1(cx), rootObject2(cx);
1473 RootedFunction rootFunction0(cx);
1474 RootedTypeObject rootType0(cx);
1475 RootedPropertyName rootName0(cx);
1476 RootedId rootId0(cx);
1477 RootedShape rootShape0(cx);
1478 RootedScript rootScript0(cx);
1479 DebugOnly<uint32_t> blockDepth;
1481 if (MOZ_UNLIKELY(REGS.fp()->isGeneratorFrame())) {
1482 JS_ASSERT(script->containsPC(REGS.pc));
1483 JS_ASSERT(REGS.stackDepth() <= script->nslots());
1485 /*
1486 * To support generator_throw and to catch ignored exceptions,
1487 * fail if cx->isExceptionPending() is true.
1488 */
1489 if (cx->isExceptionPending()) {
1490 probes::EnterScript(cx, script, script->functionNonDelazifying(), REGS.fp());
1491 goto error;
1492 }
1493 }
1495 /* State communicated between non-local jumps: */
1496 bool interpReturnOK;
1498 if (!activation.entryFrame()->isGeneratorFrame()) {
1499 if (!activation.entryFrame()->prologue(cx))
1500 goto error;
1501 } else {
1502 if (!probes::EnterScript(cx, script, script->functionNonDelazifying(),
1503 activation.entryFrame()))
1504 {
1505 goto error;
1506 }
1507 }
1508 if (MOZ_UNLIKELY(cx->compartment()->debugMode())) {
1509 JSTrapStatus status = ScriptDebugPrologue(cx, activation.entryFrame(), REGS.pc);
1510 switch (status) {
1511 case JSTRAP_CONTINUE:
1512 break;
1513 case JSTRAP_RETURN:
1514 ForcedReturn(cx, REGS);
1515 goto successful_return_continuation;
1516 case JSTRAP_THROW:
1517 case JSTRAP_ERROR:
1518 goto error;
1519 default:
1520 MOZ_ASSUME_UNREACHABLE("bad ScriptDebugPrologue status");
1521 }
1522 }
1524 if (cx->runtime()->profilingScripts || cx->runtime()->debugHooks.interruptHook)
1525 activation.enableInterruptsUnconditionally();
1527 // Enter the interpreter loop starting at the current pc.
1528 ADVANCE_AND_DISPATCH(0);
1530 INTERPRETER_LOOP() {
1532 CASE(EnableInterruptsPseudoOpcode)
1533 {
1534 bool moreInterrupts = false;
1535 jsbytecode op = *REGS.pc;
1537 if (cx->runtime()->profilingScripts) {
1538 if (!script->hasScriptCounts())
1539 script->initScriptCounts(cx);
1540 moreInterrupts = true;
1541 }
1543 if (script->hasScriptCounts()) {
1544 PCCounts counts = script->getPCCounts(REGS.pc);
1545 counts.get(PCCounts::BASE_INTERP)++;
1546 moreInterrupts = true;
1547 }
1549 if (cx->compartment()->debugMode()) {
1550 JSInterruptHook hook = cx->runtime()->debugHooks.interruptHook;
1551 if (hook || script->stepModeEnabled()) {
1552 RootedValue rval(cx);
1553 JSTrapStatus status = JSTRAP_CONTINUE;
1554 if (hook)
1555 status = hook(cx, script, REGS.pc, rval.address(),
1556 cx->runtime()->debugHooks.interruptHookData);
1557 if (status == JSTRAP_CONTINUE && script->stepModeEnabled())
1558 status = Debugger::onSingleStep(cx, &rval);
1559 switch (status) {
1560 case JSTRAP_ERROR:
1561 goto error;
1562 case JSTRAP_CONTINUE:
1563 break;
1564 case JSTRAP_RETURN:
1565 REGS.fp()->setReturnValue(rval);
1566 ForcedReturn(cx, REGS);
1567 goto successful_return_continuation;
1568 case JSTRAP_THROW:
1569 cx->setPendingException(rval);
1570 goto error;
1571 default:;
1572 }
1573 moreInterrupts = true;
1574 }
1576 if (script->hasAnyBreakpointsOrStepMode())
1577 moreInterrupts = true;
1579 if (script->hasBreakpointsAt(REGS.pc)) {
1580 RootedValue rval(cx);
1581 JSTrapStatus status = Debugger::onTrap(cx, &rval);
1582 switch (status) {
1583 case JSTRAP_ERROR:
1584 goto error;
1585 case JSTRAP_RETURN:
1586 REGS.fp()->setReturnValue(rval);
1587 ForcedReturn(cx, REGS);
1588 goto successful_return_continuation;
1589 case JSTRAP_THROW:
1590 cx->setPendingException(rval);
1591 goto error;
1592 default:
1593 break;
1594 }
1595 JS_ASSERT(status == JSTRAP_CONTINUE);
1596 JS_ASSERT(rval.isInt32() && rval.toInt32() == op);
1597 }
1598 }
1600 JS_ASSERT(activation.opMask() == EnableInterruptsPseudoOpcode);
1601 if (!moreInterrupts)
1602 activation.clearInterruptsMask();
1604 /* Commence executing the actual opcode. */
1605 SANITY_CHECKS();
1606 DISPATCH_TO(op);
1607 }
1609 /* Various 1-byte no-ops. */
1610 CASE(JSOP_NOP)
1611 CASE(JSOP_UNUSED2)
1612 CASE(JSOP_UNUSED45)
1613 CASE(JSOP_UNUSED46)
1614 CASE(JSOP_UNUSED47)
1615 CASE(JSOP_UNUSED48)
1616 CASE(JSOP_UNUSED49)
1617 CASE(JSOP_UNUSED50)
1618 CASE(JSOP_UNUSED51)
1619 CASE(JSOP_UNUSED52)
1620 CASE(JSOP_UNUSED57)
1621 CASE(JSOP_UNUSED101)
1622 CASE(JSOP_UNUSED102)
1623 CASE(JSOP_UNUSED103)
1624 CASE(JSOP_UNUSED104)
1625 CASE(JSOP_UNUSED105)
1626 CASE(JSOP_UNUSED107)
1627 CASE(JSOP_UNUSED124)
1628 CASE(JSOP_UNUSED125)
1629 CASE(JSOP_UNUSED126)
1630 CASE(JSOP_UNUSED138)
1631 CASE(JSOP_UNUSED139)
1632 CASE(JSOP_UNUSED140)
1633 CASE(JSOP_UNUSED141)
1634 CASE(JSOP_UNUSED142)
1635 CASE(JSOP_UNUSED146)
1636 CASE(JSOP_UNUSED147)
1637 CASE(JSOP_UNUSED148)
1638 CASE(JSOP_BACKPATCH)
1639 CASE(JSOP_UNUSED150)
1640 CASE(JSOP_UNUSED156)
1641 CASE(JSOP_UNUSED157)
1642 CASE(JSOP_UNUSED158)
1643 CASE(JSOP_UNUSED159)
1644 CASE(JSOP_UNUSED161)
1645 CASE(JSOP_UNUSED162)
1646 CASE(JSOP_UNUSED163)
1647 CASE(JSOP_UNUSED164)
1648 CASE(JSOP_UNUSED165)
1649 CASE(JSOP_UNUSED166)
1650 CASE(JSOP_UNUSED167)
1651 CASE(JSOP_UNUSED168)
1652 CASE(JSOP_UNUSED169)
1653 CASE(JSOP_UNUSED170)
1654 CASE(JSOP_UNUSED171)
1655 CASE(JSOP_UNUSED172)
1656 CASE(JSOP_UNUSED173)
1657 CASE(JSOP_UNUSED174)
1658 CASE(JSOP_UNUSED175)
1659 CASE(JSOP_UNUSED176)
1660 CASE(JSOP_UNUSED177)
1661 CASE(JSOP_UNUSED178)
1662 CASE(JSOP_UNUSED179)
1663 CASE(JSOP_UNUSED180)
1664 CASE(JSOP_UNUSED181)
1665 CASE(JSOP_UNUSED182)
1666 CASE(JSOP_UNUSED183)
1667 CASE(JSOP_UNUSED185)
1668 CASE(JSOP_UNUSED186)
1669 CASE(JSOP_UNUSED187)
1670 CASE(JSOP_UNUSED189)
1671 CASE(JSOP_UNUSED190)
1672 CASE(JSOP_UNUSED191)
1673 CASE(JSOP_UNUSED192)
1674 CASE(JSOP_UNUSED196)
1675 CASE(JSOP_UNUSED201)
1676 CASE(JSOP_UNUSED205)
1677 CASE(JSOP_UNUSED206)
1678 CASE(JSOP_UNUSED207)
1679 CASE(JSOP_UNUSED208)
1680 CASE(JSOP_UNUSED209)
1681 CASE(JSOP_UNUSED210)
1682 CASE(JSOP_UNUSED211)
1683 CASE(JSOP_UNUSED212)
1684 CASE(JSOP_UNUSED213)
1685 CASE(JSOP_UNUSED219)
1686 CASE(JSOP_UNUSED220)
1687 CASE(JSOP_UNUSED221)
1688 CASE(JSOP_UNUSED222)
1689 CASE(JSOP_UNUSED223)
1690 CASE(JSOP_CONDSWITCH)
1691 CASE(JSOP_TRY)
1692 {
1693 JS_ASSERT(js_CodeSpec[*REGS.pc].length == 1);
1694 ADVANCE_AND_DISPATCH(1);
1695 }
1697 CASE(JSOP_LOOPHEAD)
1698 END_CASE(JSOP_LOOPHEAD)
1700 CASE(JSOP_LABEL)
1701 END_CASE(JSOP_LABEL)
1703 CASE(JSOP_LOOPENTRY)
1705 #ifdef JS_ION
1706 // Attempt on-stack replacement with Baseline code.
1707 if (jit::IsBaselineEnabled(cx)) {
1708 jit::MethodStatus status = jit::CanEnterBaselineAtBranch(cx, REGS.fp(), false);
1709 if (status == jit::Method_Error)
1710 goto error;
1711 if (status == jit::Method_Compiled) {
1712 bool wasSPS = REGS.fp()->hasPushedSPSFrame();
1713 jit::IonExecStatus maybeOsr = jit::EnterBaselineAtBranch(cx, REGS.fp(), REGS.pc);
1715 // We failed to call into baseline at all, so treat as an error.
1716 if (maybeOsr == jit::IonExec_Aborted)
1717 goto error;
1719 interpReturnOK = (maybeOsr == jit::IonExec_Ok);
1721 // Pop the SPS frame pushed by the interpreter. (The compiled version of the
1722 // function popped a copy of the frame pushed by the OSR trampoline.)
1723 if (wasSPS)
1724 cx->runtime()->spsProfiler.exit(script, script->functionNonDelazifying());
1726 if (activation.entryFrame() != REGS.fp())
1727 goto jit_return_pop_frame;
1728 goto leave_on_safe_point;
1729 }
1730 }
1731 #endif /* JS_ION */
1733 END_CASE(JSOP_LOOPENTRY)
1735 CASE(JSOP_LINENO)
1736 END_CASE(JSOP_LINENO)
1738 CASE(JSOP_UNDEFINED)
1739 PUSH_UNDEFINED();
1740 END_CASE(JSOP_UNDEFINED)
1742 CASE(JSOP_POP)
1743 REGS.sp--;
1744 END_CASE(JSOP_POP)
1746 CASE(JSOP_POPN)
1747 JS_ASSERT(GET_UINT16(REGS.pc) <= REGS.stackDepth());
1748 REGS.sp -= GET_UINT16(REGS.pc);
1749 END_CASE(JSOP_POPN)
1751 CASE(JSOP_DUPAT)
1752 {
1753 JS_ASSERT(GET_UINT24(REGS.pc) < REGS.stackDepth());
1754 unsigned i = GET_UINT24(REGS.pc);
1755 const Value &rref = REGS.sp[-int(i + 1)];
1756 PUSH_COPY(rref);
1757 }
1758 END_CASE(JSOP_DUPAT)
1760 CASE(JSOP_SETRVAL)
1761 POP_RETURN_VALUE();
1762 END_CASE(JSOP_SETRVAL)
1764 CASE(JSOP_ENTERWITH)
1765 {
1766 RootedValue &val = rootValue0;
1767 RootedObject &staticWith = rootObject0;
1768 val = REGS.sp[-1];
1769 REGS.sp--;
1770 staticWith = script->getObject(REGS.pc);
1772 if (!EnterWithOperation(cx, REGS.fp(), val, staticWith))
1773 goto error;
1774 }
1775 END_CASE(JSOP_ENTERWITH)
1777 CASE(JSOP_LEAVEWITH)
1778 REGS.fp()->popWith(cx);
1779 END_CASE(JSOP_LEAVEWITH)
1781 CASE(JSOP_RETURN)
1782 POP_RETURN_VALUE();
1783 /* FALL THROUGH */
1785 CASE(JSOP_RETRVAL)
1786 {
1787 /*
1788 * When the inlined frame exits with an exception or an error, ok will be
1789 * false after the inline_return label.
1790 */
1791 CHECK_BRANCH();
1793 successful_return_continuation:
1794 interpReturnOK = true;
1795 return_continuation:
1796 if (activation.entryFrame() != REGS.fp())
1797 inline_return:
1798 {
1799 // Stop the engine. (No details about which engine exactly, could be
1800 // interpreter, Baseline or IonMonkey.)
1801 TraceLogStopEvent(logger);
1802 // Stop the script. (Again no details about which script exactly.)
1803 TraceLogStopEvent(logger);
1805 if (MOZ_UNLIKELY(cx->compartment()->debugMode()))
1806 interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), REGS.pc, interpReturnOK);
1808 if (!REGS.fp()->isYielding())
1809 REGS.fp()->epilogue(cx);
1810 else
1811 probes::ExitScript(cx, script, script->functionNonDelazifying(),
1812 REGS.fp()->hasPushedSPSFrame());
1814 #if defined(JS_ION)
1815 jit_return_pop_frame:
1816 #endif
1818 activation.popInlineFrame(REGS.fp());
1819 SET_SCRIPT(REGS.fp()->script());
1821 #if defined(JS_ION)
1822 jit_return:
1823 #endif
1825 JS_ASSERT(js_CodeSpec[*REGS.pc].format & JOF_INVOKE);
1827 /* Resume execution in the calling frame. */
1828 if (MOZ_LIKELY(interpReturnOK)) {
1829 TypeScript::Monitor(cx, script, REGS.pc, REGS.sp[-1]);
1831 ADVANCE_AND_DISPATCH(JSOP_CALL_LENGTH);
1832 }
1834 /* Increment pc so that |sp - fp->slots == ReconstructStackDepth(pc)|. */
1835 REGS.pc += JSOP_CALL_LENGTH;
1836 goto error;
1837 } else {
1838 JS_ASSERT(REGS.stackDepth() == 0);
1839 }
1840 goto exit;
1841 }
1843 CASE(JSOP_DEFAULT)
1844 REGS.sp--;
1845 /* FALL THROUGH */
1846 CASE(JSOP_GOTO)
1847 {
1848 BRANCH(GET_JUMP_OFFSET(REGS.pc));
1849 }
1851 CASE(JSOP_IFEQ)
1852 {
1853 bool cond = ToBooleanOp(REGS);
1854 REGS.sp--;
1855 if (!cond)
1856 BRANCH(GET_JUMP_OFFSET(REGS.pc));
1857 }
1858 END_CASE(JSOP_IFEQ)
1860 CASE(JSOP_IFNE)
1861 {
1862 bool cond = ToBooleanOp(REGS);
1863 REGS.sp--;
1864 if (cond)
1865 BRANCH(GET_JUMP_OFFSET(REGS.pc));
1866 }
1867 END_CASE(JSOP_IFNE)
1869 CASE(JSOP_OR)
1870 {
1871 bool cond = ToBooleanOp(REGS);
1872 if (cond)
1873 ADVANCE_AND_DISPATCH(GET_JUMP_OFFSET(REGS.pc));
1874 }
1875 END_CASE(JSOP_OR)
1877 CASE(JSOP_AND)
1878 {
1879 bool cond = ToBooleanOp(REGS);
1880 if (!cond)
1881 ADVANCE_AND_DISPATCH(GET_JUMP_OFFSET(REGS.pc));
1882 }
1883 END_CASE(JSOP_AND)
1885 #define FETCH_ELEMENT_ID(n, id) \
1886 JS_BEGIN_MACRO \
1887 if (!ValueToId<CanGC>(cx, REGS.stackHandleAt(n), &(id))) \
1888 goto error; \
1889 JS_END_MACRO
1891 #define TRY_BRANCH_AFTER_COND(cond,spdec) \
1892 JS_BEGIN_MACRO \
1893 JS_ASSERT(js_CodeSpec[*REGS.pc].length == 1); \
1894 unsigned diff_ = (unsigned) GET_UINT8(REGS.pc) - (unsigned) JSOP_IFEQ; \
1895 if (diff_ <= 1) { \
1896 REGS.sp -= (spdec); \
1897 if ((cond) == (diff_ != 0)) { \
1898 ++REGS.pc; \
1899 BRANCH(GET_JUMP_OFFSET(REGS.pc)); \
1900 } \
1901 ADVANCE_AND_DISPATCH(1 + JSOP_IFEQ_LENGTH); \
1902 } \
1903 JS_END_MACRO
1905 CASE(JSOP_IN)
1906 {
1907 HandleValue rref = REGS.stackHandleAt(-1);
1908 if (!rref.isObject()) {
1909 js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rref, js::NullPtr());
1910 goto error;
1911 }
1912 RootedObject &obj = rootObject0;
1913 obj = &rref.toObject();
1914 RootedId &id = rootId0;
1915 FETCH_ELEMENT_ID(-2, id);
1916 RootedObject &obj2 = rootObject1;
1917 RootedShape &prop = rootShape0;
1918 if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &prop))
1919 goto error;
1920 bool cond = prop != nullptr;
1921 prop = nullptr;
1922 TRY_BRANCH_AFTER_COND(cond, 2);
1923 REGS.sp--;
1924 REGS.sp[-1].setBoolean(cond);
1925 }
1926 END_CASE(JSOP_IN)
1928 CASE(JSOP_ITER)
1929 {
1930 JS_ASSERT(REGS.stackDepth() >= 1);
1931 uint8_t flags = GET_UINT8(REGS.pc);
1932 MutableHandleValue res = REGS.stackHandleAt(-1);
1933 if (!ValueToIterator(cx, flags, res))
1934 goto error;
1935 JS_ASSERT(!res.isPrimitive());
1936 }
1937 END_CASE(JSOP_ITER)
1939 CASE(JSOP_MOREITER)
1940 {
1941 JS_ASSERT(REGS.stackDepth() >= 1);
1942 JS_ASSERT(REGS.sp[-1].isObject());
1943 PUSH_NULL();
1944 bool cond;
1945 MutableHandleValue res = REGS.stackHandleAt(-1);
1946 if (!IteratorMore(cx, ®S.sp[-2].toObject(), &cond, res))
1947 goto error;
1948 REGS.sp[-1].setBoolean(cond);
1949 }
1950 END_CASE(JSOP_MOREITER)
1952 CASE(JSOP_ITERNEXT)
1953 {
1954 JS_ASSERT(REGS.sp[-1].isObject());
1955 PUSH_NULL();
1956 MutableHandleValue res = REGS.stackHandleAt(-1);
1957 RootedObject &obj = rootObject0;
1958 obj = ®S.sp[-2].toObject();
1959 if (!IteratorNext(cx, obj, res))
1960 goto error;
1961 }
1962 END_CASE(JSOP_ITERNEXT)
1964 CASE(JSOP_ENDITER)
1965 {
1966 JS_ASSERT(REGS.stackDepth() >= 1);
1967 RootedObject &obj = rootObject0;
1968 obj = ®S.sp[-1].toObject();
1969 bool ok = CloseIterator(cx, obj);
1970 REGS.sp--;
1971 if (!ok)
1972 goto error;
1973 }
1974 END_CASE(JSOP_ENDITER)
1976 CASE(JSOP_DUP)
1977 {
1978 JS_ASSERT(REGS.stackDepth() >= 1);
1979 const Value &rref = REGS.sp[-1];
1980 PUSH_COPY(rref);
1981 }
1982 END_CASE(JSOP_DUP)
1984 CASE(JSOP_DUP2)
1985 {
1986 JS_ASSERT(REGS.stackDepth() >= 2);
1987 const Value &lref = REGS.sp[-2];
1988 const Value &rref = REGS.sp[-1];
1989 PUSH_COPY(lref);
1990 PUSH_COPY(rref);
1991 }
1992 END_CASE(JSOP_DUP2)
1994 CASE(JSOP_SWAP)
1995 {
1996 JS_ASSERT(REGS.stackDepth() >= 2);
1997 Value &lref = REGS.sp[-2];
1998 Value &rref = REGS.sp[-1];
1999 lref.swap(rref);
2000 }
2001 END_CASE(JSOP_SWAP)
2003 CASE(JSOP_PICK)
2004 {
2005 unsigned i = GET_UINT8(REGS.pc);
2006 JS_ASSERT(REGS.stackDepth() >= i + 1);
2007 Value lval = REGS.sp[-int(i + 1)];
2008 memmove(REGS.sp - (i + 1), REGS.sp - i, sizeof(Value) * i);
2009 REGS.sp[-1] = lval;
2010 }
2011 END_CASE(JSOP_PICK)
2013 CASE(JSOP_SETCONST)
2014 {
2015 RootedPropertyName &name = rootName0;
2016 name = script->getName(REGS.pc);
2018 RootedValue &rval = rootValue0;
2019 rval = REGS.sp[-1];
2021 RootedObject &obj = rootObject0;
2022 obj = ®S.fp()->varObj();
2024 if (!SetConstOperation(cx, obj, name, rval))
2025 goto error;
2026 }
2027 END_CASE(JSOP_SETCONST);
2029 CASE(JSOP_BINDGNAME)
2030 PUSH_OBJECT(REGS.fp()->global());
2031 END_CASE(JSOP_BINDGNAME)
2033 CASE(JSOP_BINDINTRINSIC)
2034 PUSH_OBJECT(*cx->global()->intrinsicsHolder());
2035 END_CASE(JSOP_BINDINTRINSIC)
2037 CASE(JSOP_BINDNAME)
2038 {
2039 RootedObject &scopeChain = rootObject0;
2040 scopeChain = REGS.fp()->scopeChain();
2042 RootedPropertyName &name = rootName0;
2043 name = script->getName(REGS.pc);
2045 /* Assigning to an undeclared name adds a property to the global object. */
2046 RootedObject &scope = rootObject1;
2047 if (!LookupNameWithGlobalDefault(cx, name, scopeChain, &scope))
2048 goto error;
2050 PUSH_OBJECT(*scope);
2051 }
2052 END_CASE(JSOP_BINDNAME)
2054 #define BITWISE_OP(OP) \
2055 JS_BEGIN_MACRO \
2056 int32_t i, j; \
2057 if (!ToInt32(cx, REGS.stackHandleAt(-2), &i)) \
2058 goto error; \
2059 if (!ToInt32(cx, REGS.stackHandleAt(-1), &j)) \
2060 goto error; \
2061 i = i OP j; \
2062 REGS.sp--; \
2063 REGS.sp[-1].setInt32(i); \
2064 JS_END_MACRO
2066 CASE(JSOP_BITOR)
2067 BITWISE_OP(|);
2068 END_CASE(JSOP_BITOR)
2070 CASE(JSOP_BITXOR)
2071 BITWISE_OP(^);
2072 END_CASE(JSOP_BITXOR)
2074 CASE(JSOP_BITAND)
2075 BITWISE_OP(&);
2076 END_CASE(JSOP_BITAND)
2078 #undef BITWISE_OP
2080 CASE(JSOP_EQ)
2081 if (!LooseEqualityOp<true>(cx, REGS))
2082 goto error;
2083 END_CASE(JSOP_EQ)
2085 CASE(JSOP_NE)
2086 if (!LooseEqualityOp<false>(cx, REGS))
2087 goto error;
2088 END_CASE(JSOP_NE)
2090 #define STRICT_EQUALITY_OP(OP, COND) \
2091 JS_BEGIN_MACRO \
2092 const Value &rref = REGS.sp[-1]; \
2093 const Value &lref = REGS.sp[-2]; \
2094 bool equal; \
2095 if (!StrictlyEqual(cx, lref, rref, &equal)) \
2096 goto error; \
2097 (COND) = equal OP true; \
2098 REGS.sp--; \
2099 JS_END_MACRO
2101 CASE(JSOP_STRICTEQ)
2102 {
2103 bool cond;
2104 STRICT_EQUALITY_OP(==, cond);
2105 REGS.sp[-1].setBoolean(cond);
2106 }
2107 END_CASE(JSOP_STRICTEQ)
2109 CASE(JSOP_STRICTNE)
2110 {
2111 bool cond;
2112 STRICT_EQUALITY_OP(!=, cond);
2113 REGS.sp[-1].setBoolean(cond);
2114 }
2115 END_CASE(JSOP_STRICTNE)
2117 CASE(JSOP_CASE)
2118 {
2119 bool cond;
2120 STRICT_EQUALITY_OP(==, cond);
2121 if (cond) {
2122 REGS.sp--;
2123 BRANCH(GET_JUMP_OFFSET(REGS.pc));
2124 }
2125 }
2126 END_CASE(JSOP_CASE)
2128 #undef STRICT_EQUALITY_OP
2130 CASE(JSOP_LT)
2131 {
2132 bool cond;
2133 MutableHandleValue lval = REGS.stackHandleAt(-2);
2134 MutableHandleValue rval = REGS.stackHandleAt(-1);
2135 if (!LessThanOperation(cx, lval, rval, &cond))
2136 goto error;
2137 TRY_BRANCH_AFTER_COND(cond, 2);
2138 REGS.sp[-2].setBoolean(cond);
2139 REGS.sp--;
2140 }
2141 END_CASE(JSOP_LT)
2143 CASE(JSOP_LE)
2144 {
2145 bool cond;
2146 MutableHandleValue lval = REGS.stackHandleAt(-2);
2147 MutableHandleValue rval = REGS.stackHandleAt(-1);
2148 if (!LessThanOrEqualOperation(cx, lval, rval, &cond))
2149 goto error;
2150 TRY_BRANCH_AFTER_COND(cond, 2);
2151 REGS.sp[-2].setBoolean(cond);
2152 REGS.sp--;
2153 }
2154 END_CASE(JSOP_LE)
2156 CASE(JSOP_GT)
2157 {
2158 bool cond;
2159 MutableHandleValue lval = REGS.stackHandleAt(-2);
2160 MutableHandleValue rval = REGS.stackHandleAt(-1);
2161 if (!GreaterThanOperation(cx, lval, rval, &cond))
2162 goto error;
2163 TRY_BRANCH_AFTER_COND(cond, 2);
2164 REGS.sp[-2].setBoolean(cond);
2165 REGS.sp--;
2166 }
2167 END_CASE(JSOP_GT)
2169 CASE(JSOP_GE)
2170 {
2171 bool cond;
2172 MutableHandleValue lval = REGS.stackHandleAt(-2);
2173 MutableHandleValue rval = REGS.stackHandleAt(-1);
2174 if (!GreaterThanOrEqualOperation(cx, lval, rval, &cond))
2175 goto error;
2176 TRY_BRANCH_AFTER_COND(cond, 2);
2177 REGS.sp[-2].setBoolean(cond);
2178 REGS.sp--;
2179 }
2180 END_CASE(JSOP_GE)
2182 #define SIGNED_SHIFT_OP(OP) \
2183 JS_BEGIN_MACRO \
2184 int32_t i, j; \
2185 if (!ToInt32(cx, REGS.stackHandleAt(-2), &i)) \
2186 goto error; \
2187 if (!ToInt32(cx, REGS.stackHandleAt(-1), &j)) \
2188 goto error; \
2189 i = i OP (j & 31); \
2190 REGS.sp--; \
2191 REGS.sp[-1].setInt32(i); \
2192 JS_END_MACRO
2194 CASE(JSOP_LSH)
2195 SIGNED_SHIFT_OP(<<);
2196 END_CASE(JSOP_LSH)
2198 CASE(JSOP_RSH)
2199 SIGNED_SHIFT_OP(>>);
2200 END_CASE(JSOP_RSH)
2202 #undef SIGNED_SHIFT_OP
2204 CASE(JSOP_URSH)
2205 {
2206 HandleValue lval = REGS.stackHandleAt(-2);
2207 HandleValue rval = REGS.stackHandleAt(-1);
2208 MutableHandleValue res = REGS.stackHandleAt(-2);
2209 if (!UrshOperation(cx, lval, rval, res))
2210 goto error;
2211 REGS.sp--;
2212 }
2213 END_CASE(JSOP_URSH)
2215 CASE(JSOP_ADD)
2216 {
2217 MutableHandleValue lval = REGS.stackHandleAt(-2);
2218 MutableHandleValue rval = REGS.stackHandleAt(-1);
2219 MutableHandleValue res = REGS.stackHandleAt(-2);
2220 if (!AddOperation(cx, lval, rval, res))
2221 goto error;
2222 REGS.sp--;
2223 }
2224 END_CASE(JSOP_ADD)
2226 CASE(JSOP_SUB)
2227 {
2228 RootedValue &lval = rootValue0, &rval = rootValue1;
2229 lval = REGS.sp[-2];
2230 rval = REGS.sp[-1];
2231 MutableHandleValue res = REGS.stackHandleAt(-2);
2232 if (!SubOperation(cx, lval, rval, res))
2233 goto error;
2234 REGS.sp--;
2235 }
2236 END_CASE(JSOP_SUB)
2238 CASE(JSOP_MUL)
2239 {
2240 RootedValue &lval = rootValue0, &rval = rootValue1;
2241 lval = REGS.sp[-2];
2242 rval = REGS.sp[-1];
2243 MutableHandleValue res = REGS.stackHandleAt(-2);
2244 if (!MulOperation(cx, lval, rval, res))
2245 goto error;
2246 REGS.sp--;
2247 }
2248 END_CASE(JSOP_MUL)
2250 CASE(JSOP_DIV)
2251 {
2252 RootedValue &lval = rootValue0, &rval = rootValue1;
2253 lval = REGS.sp[-2];
2254 rval = REGS.sp[-1];
2255 MutableHandleValue res = REGS.stackHandleAt(-2);
2256 if (!DivOperation(cx, lval, rval, res))
2257 goto error;
2258 REGS.sp--;
2259 }
2260 END_CASE(JSOP_DIV)
2262 CASE(JSOP_MOD)
2263 {
2264 RootedValue &lval = rootValue0, &rval = rootValue1;
2265 lval = REGS.sp[-2];
2266 rval = REGS.sp[-1];
2267 MutableHandleValue res = REGS.stackHandleAt(-2);
2268 if (!ModOperation(cx, lval, rval, res))
2269 goto error;
2270 REGS.sp--;
2271 }
2272 END_CASE(JSOP_MOD)
2274 CASE(JSOP_NOT)
2275 {
2276 bool cond = ToBooleanOp(REGS);
2277 REGS.sp--;
2278 PUSH_BOOLEAN(!cond);
2279 }
2280 END_CASE(JSOP_NOT)
2282 CASE(JSOP_BITNOT)
2283 {
2284 int32_t i;
2285 HandleValue value = REGS.stackHandleAt(-1);
2286 if (!BitNot(cx, value, &i))
2287 goto error;
2288 REGS.sp[-1].setInt32(i);
2289 }
2290 END_CASE(JSOP_BITNOT)
2292 CASE(JSOP_NEG)
2293 {
2294 RootedValue &val = rootValue0;
2295 val = REGS.sp[-1];
2296 MutableHandleValue res = REGS.stackHandleAt(-1);
2297 if (!NegOperation(cx, script, REGS.pc, val, res))
2298 goto error;
2299 }
2300 END_CASE(JSOP_NEG)
2302 CASE(JSOP_POS)
2303 if (!ToNumber(cx, REGS.stackHandleAt(-1)))
2304 goto error;
2305 END_CASE(JSOP_POS)
2307 CASE(JSOP_DELNAME)
2308 {
2309 /* Strict mode code should never contain JSOP_DELNAME opcodes. */
2310 JS_ASSERT(!script->strict());
2312 RootedPropertyName &name = rootName0;
2313 name = script->getName(REGS.pc);
2315 RootedObject &scopeObj = rootObject0;
2316 scopeObj = REGS.fp()->scopeChain();
2318 PUSH_BOOLEAN(true);
2319 MutableHandleValue res = REGS.stackHandleAt(-1);
2320 if (!DeleteNameOperation(cx, name, scopeObj, res))
2321 goto error;
2322 }
2323 END_CASE(JSOP_DELNAME)
2325 CASE(JSOP_DELPROP)
2326 {
2327 RootedPropertyName &name = rootName0;
2328 name = script->getName(REGS.pc);
2330 RootedObject &obj = rootObject0;
2331 FETCH_OBJECT(cx, -1, obj);
2333 bool succeeded;
2334 if (!JSObject::deleteProperty(cx, obj, name, &succeeded))
2335 goto error;
2336 if (!succeeded && script->strict()) {
2337 obj->reportNotConfigurable(cx, NameToId(name));
2338 goto error;
2339 }
2340 MutableHandleValue res = REGS.stackHandleAt(-1);
2341 res.setBoolean(succeeded);
2342 }
2343 END_CASE(JSOP_DELPROP)
2345 CASE(JSOP_DELELEM)
2346 {
2347 /* Fetch the left part and resolve it to a non-null object. */
2348 RootedObject &obj = rootObject0;
2349 FETCH_OBJECT(cx, -2, obj);
2351 RootedValue &propval = rootValue0;
2352 propval = REGS.sp[-1];
2354 bool succeeded;
2355 if (!JSObject::deleteByValue(cx, obj, propval, &succeeded))
2356 goto error;
2357 if (!succeeded && script->strict()) {
2358 // XXX This observably calls ToString(propval). We should convert to
2359 // PropertyKey and use that to delete, and to report an error if
2360 // necessary!
2361 RootedId id(cx);
2362 if (!ValueToId<CanGC>(cx, propval, &id))
2363 goto error;
2364 obj->reportNotConfigurable(cx, id);
2365 goto error;
2366 }
2368 MutableHandleValue res = REGS.stackHandleAt(-2);
2369 res.setBoolean(succeeded);
2370 REGS.sp--;
2371 }
2372 END_CASE(JSOP_DELELEM)
2374 CASE(JSOP_TOID)
2375 {
2376 /*
2377 * Increment or decrement requires use to lookup the same property twice,
2378 * but we need to avoid the observable stringification the second time.
2379 * There must be an object value below the id, which will not be popped.
2380 */
2381 RootedValue &objval = rootValue0, &idval = rootValue1;
2382 objval = REGS.sp[-2];
2383 idval = REGS.sp[-1];
2385 MutableHandleValue res = REGS.stackHandleAt(-1);
2386 if (!ToIdOperation(cx, script, REGS.pc, objval, idval, res))
2387 goto error;
2388 }
2389 END_CASE(JSOP_TOID)
2391 CASE(JSOP_TYPEOFEXPR)
2392 CASE(JSOP_TYPEOF)
2393 {
2394 REGS.sp[-1].setString(TypeOfOperation(REGS.sp[-1], cx->runtime()));
2395 }
2396 END_CASE(JSOP_TYPEOF)
2398 CASE(JSOP_VOID)
2399 REGS.sp[-1].setUndefined();
2400 END_CASE(JSOP_VOID)
2402 CASE(JSOP_THIS)
2403 if (!ComputeThis(cx, REGS.fp()))
2404 goto error;
2405 PUSH_COPY(REGS.fp()->thisValue());
2406 END_CASE(JSOP_THIS)
2408 CASE(JSOP_GETPROP)
2409 CASE(JSOP_GETXPROP)
2410 CASE(JSOP_LENGTH)
2411 CASE(JSOP_CALLPROP)
2412 {
2414 MutableHandleValue lval = REGS.stackHandleAt(-1);
2415 if (!GetPropertyOperation(cx, REGS.fp(), script, REGS.pc, lval, lval))
2416 goto error;
2418 TypeScript::Monitor(cx, script, REGS.pc, lval);
2419 assertSameCompartmentDebugOnly(cx, lval);
2420 }
2421 END_CASE(JSOP_GETPROP)
2423 CASE(JSOP_SETINTRINSIC)
2424 {
2425 HandleValue value = REGS.stackHandleAt(-1);
2427 if (!SetIntrinsicOperation(cx, script, REGS.pc, value))
2428 goto error;
2430 REGS.sp[-2] = REGS.sp[-1];
2431 REGS.sp--;
2432 }
2433 END_CASE(JSOP_SETINTRINSIC)
2435 CASE(JSOP_SETGNAME)
2436 CASE(JSOP_SETNAME)
2437 {
2438 RootedObject &scope = rootObject0;
2439 scope = ®S.sp[-2].toObject();
2441 HandleValue value = REGS.stackHandleAt(-1);
2443 if (!SetNameOperation(cx, script, REGS.pc, scope, value))
2444 goto error;
2446 REGS.sp[-2] = REGS.sp[-1];
2447 REGS.sp--;
2448 }
2449 END_CASE(JSOP_SETNAME)
2451 CASE(JSOP_SETPROP)
2452 {
2453 HandleValue lval = REGS.stackHandleAt(-2);
2454 HandleValue rval = REGS.stackHandleAt(-1);
2456 if (!SetPropertyOperation(cx, script, REGS.pc, lval, rval))
2457 goto error;
2459 REGS.sp[-2] = REGS.sp[-1];
2460 REGS.sp--;
2461 }
2462 END_CASE(JSOP_SETPROP)
2464 CASE(JSOP_GETELEM)
2465 CASE(JSOP_CALLELEM)
2466 {
2467 MutableHandleValue lval = REGS.stackHandleAt(-2);
2468 HandleValue rval = REGS.stackHandleAt(-1);
2469 MutableHandleValue res = REGS.stackHandleAt(-2);
2471 bool done = false;
2472 if (!GetElemOptimizedArguments(cx, REGS.fp(), lval, rval, res, &done))
2473 goto error;
2475 if (!done) {
2476 if (!GetElementOperation(cx, JSOp(*REGS.pc), lval, rval, res))
2477 goto error;
2478 }
2480 TypeScript::Monitor(cx, script, REGS.pc, res);
2481 REGS.sp--;
2482 }
2483 END_CASE(JSOP_GETELEM)
2485 CASE(JSOP_SETELEM)
2486 {
2487 RootedObject &obj = rootObject0;
2488 FETCH_OBJECT(cx, -3, obj);
2489 RootedId &id = rootId0;
2490 FETCH_ELEMENT_ID(-2, id);
2491 Value &value = REGS.sp[-1];
2492 if (!SetObjectElementOperation(cx, obj, id, value, script->strict()))
2493 goto error;
2494 REGS.sp[-3] = value;
2495 REGS.sp -= 2;
2496 }
2497 END_CASE(JSOP_SETELEM)
2499 CASE(JSOP_EVAL)
2500 {
2501 CallArgs args = CallArgsFromSp(GET_ARGC(REGS.pc), REGS.sp);
2502 if (REGS.fp()->scopeChain()->global().valueIsEval(args.calleev())) {
2503 if (!DirectEval(cx, args))
2504 goto error;
2505 } else {
2506 if (!Invoke(cx, args))
2507 goto error;
2508 }
2509 REGS.sp = args.spAfterCall();
2510 TypeScript::Monitor(cx, script, REGS.pc, REGS.sp[-1]);
2511 }
2512 END_CASE(JSOP_EVAL)
2514 CASE(JSOP_SPREADNEW)
2515 CASE(JSOP_SPREADCALL)
2516 if (REGS.fp()->hasPushedSPSFrame())
2517 cx->runtime()->spsProfiler.updatePC(script, REGS.pc);
2518 /* FALL THROUGH */
2520 CASE(JSOP_SPREADEVAL)
2521 {
2522 JS_ASSERT(REGS.stackDepth() >= 3);
2523 RootedObject &aobj = rootObject0;
2524 aobj = ®S.sp[-1].toObject();
2526 uint32_t length = aobj->as<ArrayObject>().length();
2528 if (length > ARGS_LENGTH_MAX) {
2529 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
2530 *REGS.pc == JSOP_SPREADNEW ? JSMSG_TOO_MANY_CON_SPREADARGS
2531 : JSMSG_TOO_MANY_FUN_SPREADARGS);
2532 goto error;
2533 }
2535 InvokeArgs args(cx);
2537 if (!args.init(length))
2538 return false;
2540 args.setCallee(REGS.sp[-3]);
2541 args.setThis(REGS.sp[-2]);
2543 if (!GetElements(cx, aobj, length, args.array()))
2544 goto error;
2546 switch (*REGS.pc) {
2547 case JSOP_SPREADNEW:
2548 if (!InvokeConstructor(cx, args))
2549 goto error;
2550 break;
2551 case JSOP_SPREADCALL:
2552 if (!Invoke(cx, args))
2553 goto error;
2554 break;
2555 case JSOP_SPREADEVAL:
2556 if (REGS.fp()->scopeChain()->global().valueIsEval(args.calleev())) {
2557 if (!DirectEval(cx, args))
2558 goto error;
2559 } else {
2560 if (!Invoke(cx, args))
2561 goto error;
2562 }
2563 break;
2564 default:
2565 MOZ_ASSUME_UNREACHABLE("bad spread opcode");
2566 }
2568 REGS.sp -= 2;
2569 REGS.sp[-1] = args.rval();
2570 TypeScript::Monitor(cx, script, REGS.pc, REGS.sp[-1]);
2571 }
2572 END_CASE(JSOP_SPREADCALL)
2574 CASE(JSOP_FUNAPPLY)
2575 {
2576 CallArgs args = CallArgsFromSp(GET_ARGC(REGS.pc), REGS.sp);
2577 if (!GuardFunApplyArgumentsOptimization(cx, REGS.fp(), args.calleev(), args.array(),
2578 args.length()))
2579 goto error;
2580 /* FALL THROUGH */
2581 }
2583 CASE(JSOP_NEW)
2584 CASE(JSOP_CALL)
2585 CASE(JSOP_FUNCALL)
2586 {
2587 if (REGS.fp()->hasPushedSPSFrame())
2588 cx->runtime()->spsProfiler.updatePC(script, REGS.pc);
2589 JS_ASSERT(REGS.stackDepth() >= 2 + GET_ARGC(REGS.pc));
2590 CallArgs args = CallArgsFromSp(GET_ARGC(REGS.pc), REGS.sp);
2592 bool construct = (*REGS.pc == JSOP_NEW);
2594 RootedFunction &fun = rootFunction0;
2595 RootedScript &funScript = rootScript0;
2596 bool isFunction = IsFunctionObject(args.calleev(), fun.address());
2598 /*
2599 * Some builtins are marked as clone-at-callsite to increase precision of
2600 * TI and JITs.
2601 */
2602 if (isFunction && fun->isInterpreted()) {
2603 funScript = fun->getOrCreateScript(cx);
2604 if (!funScript)
2605 goto error;
2606 if (funScript->shouldCloneAtCallsite()) {
2607 fun = CloneFunctionAtCallsite(cx, fun, script, REGS.pc);
2608 if (!fun)
2609 goto error;
2610 args.setCallee(ObjectValue(*fun));
2611 }
2612 }
2614 /* Don't bother trying to fast-path calls to scripted non-constructors. */
2615 if (!isFunction || !fun->isInterpretedConstructor()) {
2616 if (construct) {
2617 if (!InvokeConstructor(cx, args))
2618 goto error;
2619 } else {
2620 if (!Invoke(cx, args))
2621 goto error;
2622 }
2623 Value *newsp = args.spAfterCall();
2624 TypeScript::Monitor(cx, script, REGS.pc, newsp[-1]);
2625 REGS.sp = newsp;
2626 ADVANCE_AND_DISPATCH(JSOP_CALL_LENGTH);
2627 }
2629 InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE;
2630 bool newType = UseNewType(cx, script, REGS.pc);
2632 TypeMonitorCall(cx, args, construct);
2634 #ifdef JS_ION
2635 {
2636 InvokeState state(cx, args, initial);
2637 if (newType)
2638 state.setUseNewType();
2640 if (!newType && jit::IsIonEnabled(cx)) {
2641 jit::MethodStatus status = jit::CanEnter(cx, state);
2642 if (status == jit::Method_Error)
2643 goto error;
2644 if (status == jit::Method_Compiled) {
2645 jit::IonExecStatus exec = jit::IonCannon(cx, state);
2646 CHECK_BRANCH();
2647 REGS.sp = args.spAfterCall();
2648 interpReturnOK = !IsErrorStatus(exec);
2649 goto jit_return;
2650 }
2651 }
2653 if (jit::IsBaselineEnabled(cx)) {
2654 jit::MethodStatus status = jit::CanEnterBaselineMethod(cx, state);
2655 if (status == jit::Method_Error)
2656 goto error;
2657 if (status == jit::Method_Compiled) {
2658 jit::IonExecStatus exec = jit::EnterBaselineMethod(cx, state);
2659 CHECK_BRANCH();
2660 REGS.sp = args.spAfterCall();
2661 interpReturnOK = !IsErrorStatus(exec);
2662 goto jit_return;
2663 }
2664 }
2665 }
2666 #endif
2668 funScript = fun->nonLazyScript();
2669 if (!activation.pushInlineFrame(args, funScript, initial))
2670 goto error;
2672 if (newType)
2673 REGS.fp()->setUseNewType();
2675 SET_SCRIPT(REGS.fp()->script());
2677 uint32_t scriptLogId = TraceLogCreateTextId(logger, script);
2678 TraceLogStartEvent(logger, scriptLogId);
2679 TraceLogStartEvent(logger, TraceLogger::Interpreter);
2681 if (!REGS.fp()->prologue(cx))
2682 goto error;
2683 if (MOZ_UNLIKELY(cx->compartment()->debugMode())) {
2684 switch (ScriptDebugPrologue(cx, REGS.fp(), REGS.pc)) {
2685 case JSTRAP_CONTINUE:
2686 break;
2687 case JSTRAP_RETURN:
2688 ForcedReturn(cx, REGS);
2689 goto successful_return_continuation;
2690 case JSTRAP_THROW:
2691 case JSTRAP_ERROR:
2692 goto error;
2693 default:
2694 MOZ_ASSUME_UNREACHABLE("bad ScriptDebugPrologue status");
2695 }
2696 }
2698 /* Load first op and dispatch it (safe since JSOP_RETRVAL). */
2699 ADVANCE_AND_DISPATCH(0);
2700 }
2702 CASE(JSOP_SETCALL)
2703 {
2704 JS_ALWAYS_FALSE(SetCallOperation(cx));
2705 goto error;
2706 }
2707 END_CASE(JSOP_SETCALL)
2709 CASE(JSOP_IMPLICITTHIS)
2710 {
2711 RootedPropertyName &name = rootName0;
2712 name = script->getName(REGS.pc);
2714 RootedObject &scopeObj = rootObject0;
2715 scopeObj = REGS.fp()->scopeChain();
2717 RootedObject &scope = rootObject1;
2718 if (!LookupNameWithGlobalDefault(cx, name, scopeObj, &scope))
2719 goto error;
2721 RootedValue &v = rootValue0;
2722 if (!ComputeImplicitThis(cx, scope, &v))
2723 goto error;
2724 PUSH_COPY(v);
2725 }
2726 END_CASE(JSOP_IMPLICITTHIS)
2728 CASE(JSOP_GETGNAME)
2729 CASE(JSOP_NAME)
2730 {
2731 RootedValue &rval = rootValue0;
2733 if (!NameOperation(cx, REGS.fp(), REGS.pc, &rval))
2734 goto error;
2736 PUSH_COPY(rval);
2737 TypeScript::Monitor(cx, script, REGS.pc, rval);
2738 }
2739 END_CASE(JSOP_NAME)
2741 CASE(JSOP_GETINTRINSIC)
2742 {
2743 RootedValue &rval = rootValue0;
2745 if (!GetIntrinsicOperation(cx, REGS.pc, &rval))
2746 goto error;
2748 PUSH_COPY(rval);
2749 TypeScript::Monitor(cx, script, REGS.pc, rval);
2750 }
2751 END_CASE(JSOP_GETINTRINSIC)
2753 CASE(JSOP_UINT16)
2754 PUSH_INT32((int32_t) GET_UINT16(REGS.pc));
2755 END_CASE(JSOP_UINT16)
2757 CASE(JSOP_UINT24)
2758 PUSH_INT32((int32_t) GET_UINT24(REGS.pc));
2759 END_CASE(JSOP_UINT24)
2761 CASE(JSOP_INT8)
2762 PUSH_INT32(GET_INT8(REGS.pc));
2763 END_CASE(JSOP_INT8)
2765 CASE(JSOP_INT32)
2766 PUSH_INT32(GET_INT32(REGS.pc));
2767 END_CASE(JSOP_INT32)
2769 CASE(JSOP_DOUBLE)
2770 {
2771 double dbl;
2772 LOAD_DOUBLE(0, dbl);
2773 PUSH_DOUBLE(dbl);
2774 }
2775 END_CASE(JSOP_DOUBLE)
2777 CASE(JSOP_STRING)
2778 PUSH_STRING(script->getAtom(REGS.pc));
2779 END_CASE(JSOP_STRING)
2781 CASE(JSOP_OBJECT)
2782 {
2783 RootedObject &ref = rootObject0;
2784 ref = script->getObject(REGS.pc);
2785 if (JS::CompartmentOptionsRef(cx).cloneSingletons(cx)) {
2786 JSObject *obj = js::DeepCloneObjectLiteral(cx, ref, js::MaybeSingletonObject);
2787 if (!obj)
2788 goto error;
2789 PUSH_OBJECT(*obj);
2790 } else {
2791 JS::CompartmentOptionsRef(cx).setSingletonsAsValues();
2792 PUSH_OBJECT(*ref);
2793 }
2794 }
2795 END_CASE(JSOP_OBJECT)
2797 CASE(JSOP_REGEXP)
2798 {
2799 /*
2800 * Push a regexp object cloned from the regexp literal object mapped by the
2801 * bytecode at pc.
2802 */
2803 JSObject *obj = CloneRegExpObject(cx, script->getRegExp(REGS.pc));
2804 if (!obj)
2805 goto error;
2806 PUSH_OBJECT(*obj);
2807 }
2808 END_CASE(JSOP_REGEXP)
2810 CASE(JSOP_ZERO)
2811 PUSH_INT32(0);
2812 END_CASE(JSOP_ZERO)
2814 CASE(JSOP_ONE)
2815 PUSH_INT32(1);
2816 END_CASE(JSOP_ONE)
2818 CASE(JSOP_NULL)
2819 PUSH_NULL();
2820 END_CASE(JSOP_NULL)
2822 CASE(JSOP_FALSE)
2823 PUSH_BOOLEAN(false);
2824 END_CASE(JSOP_FALSE)
2826 CASE(JSOP_TRUE)
2827 PUSH_BOOLEAN(true);
2828 END_CASE(JSOP_TRUE)
2830 CASE(JSOP_TABLESWITCH)
2831 {
2832 jsbytecode *pc2 = REGS.pc;
2833 int32_t len = GET_JUMP_OFFSET(pc2);
2835 /*
2836 * ECMAv2+ forbids conversion of discriminant, so we will skip to the
2837 * default case if the discriminant isn't already an int jsval. (This
2838 * opcode is emitted only for dense int-domain switches.)
2839 */
2840 const Value &rref = *--REGS.sp;
2841 int32_t i;
2842 if (rref.isInt32()) {
2843 i = rref.toInt32();
2844 } else {
2845 /* Use mozilla::NumberEqualsInt32 to treat -0 (double) as 0. */
2846 if (!rref.isDouble() || !NumberEqualsInt32(rref.toDouble(), &i))
2847 ADVANCE_AND_DISPATCH(len);
2848 }
2850 pc2 += JUMP_OFFSET_LEN;
2851 int32_t low = GET_JUMP_OFFSET(pc2);
2852 pc2 += JUMP_OFFSET_LEN;
2853 int32_t high = GET_JUMP_OFFSET(pc2);
2855 i -= low;
2856 if ((uint32_t)i < (uint32_t)(high - low + 1)) {
2857 pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i;
2858 int32_t off = (int32_t) GET_JUMP_OFFSET(pc2);
2859 if (off)
2860 len = off;
2861 }
2862 ADVANCE_AND_DISPATCH(len);
2863 }
2865 CASE(JSOP_ARGUMENTS)
2866 JS_ASSERT(!REGS.fp()->fun()->hasRest());
2867 if (!script->ensureHasAnalyzedArgsUsage(cx))
2868 goto error;
2869 if (script->needsArgsObj()) {
2870 ArgumentsObject *obj = ArgumentsObject::createExpected(cx, REGS.fp());
2871 if (!obj)
2872 goto error;
2873 PUSH_COPY(ObjectValue(*obj));
2874 } else {
2875 PUSH_COPY(MagicValue(JS_OPTIMIZED_ARGUMENTS));
2876 }
2877 END_CASE(JSOP_ARGUMENTS)
2879 CASE(JSOP_RUNONCE)
2880 {
2881 if (!RunOnceScriptPrologue(cx, script))
2882 goto error;
2883 }
2884 END_CASE(JSOP_RUNONCE)
2886 CASE(JSOP_REST)
2887 {
2888 RootedObject &rest = rootObject0;
2889 rest = REGS.fp()->createRestParameter(cx);
2890 if (!rest)
2891 goto error;
2892 PUSH_COPY(ObjectValue(*rest));
2893 }
2894 END_CASE(JSOP_REST)
2896 CASE(JSOP_GETALIASEDVAR)
2897 {
2898 ScopeCoordinate sc = ScopeCoordinate(REGS.pc);
2899 PUSH_COPY(REGS.fp()->aliasedVarScope(sc).aliasedVar(sc));
2900 TypeScript::Monitor(cx, script, REGS.pc, REGS.sp[-1]);
2901 }
2902 END_CASE(JSOP_GETALIASEDVAR)
2904 CASE(JSOP_SETALIASEDVAR)
2905 {
2906 ScopeCoordinate sc = ScopeCoordinate(REGS.pc);
2907 ScopeObject &obj = REGS.fp()->aliasedVarScope(sc);
2909 // Avoid computing the name if no type updates are needed, as this may be
2910 // expensive on scopes with large numbers of variables.
2911 PropertyName *name = (obj.hasSingletonType() && !obj.hasLazyType())
2912 ? ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, REGS.pc)
2913 : nullptr;
2915 obj.setAliasedVar(cx, sc, name, REGS.sp[-1]);
2916 }
2917 END_CASE(JSOP_SETALIASEDVAR)
2919 CASE(JSOP_GETARG)
2920 {
2921 unsigned i = GET_ARGNO(REGS.pc);
2922 if (script->argsObjAliasesFormals())
2923 PUSH_COPY(REGS.fp()->argsObj().arg(i));
2924 else
2925 PUSH_COPY(REGS.fp()->unaliasedFormal(i));
2926 }
2927 END_CASE(JSOP_GETARG)
2929 CASE(JSOP_SETARG)
2930 {
2931 unsigned i = GET_ARGNO(REGS.pc);
2932 if (script->argsObjAliasesFormals())
2933 REGS.fp()->argsObj().setArg(i, REGS.sp[-1]);
2934 else
2935 REGS.fp()->unaliasedFormal(i) = REGS.sp[-1];
2936 }
2937 END_CASE(JSOP_SETARG)
2939 CASE(JSOP_GETLOCAL)
2940 {
2941 uint32_t i = GET_LOCALNO(REGS.pc);
2942 PUSH_COPY_SKIP_CHECK(REGS.fp()->unaliasedLocal(i));
2944 /*
2945 * Skip the same-compartment assertion if the local will be immediately
2946 * popped. We do not guarantee sync for dead locals when coming in from the
2947 * method JIT, and a GETLOCAL followed by POP is not considered to be
2948 * a use of the variable.
2949 */
2950 if (REGS.pc[JSOP_GETLOCAL_LENGTH] != JSOP_POP)
2951 assertSameCompartmentDebugOnly(cx, REGS.sp[-1]);
2952 }
2953 END_CASE(JSOP_GETLOCAL)
2955 CASE(JSOP_SETLOCAL)
2956 {
2957 uint32_t i = GET_LOCALNO(REGS.pc);
2958 REGS.fp()->unaliasedLocal(i) = REGS.sp[-1];
2959 }
2960 END_CASE(JSOP_SETLOCAL)
2962 CASE(JSOP_DEFCONST)
2963 CASE(JSOP_DEFVAR)
2964 {
2965 /* ES5 10.5 step 8 (with subsequent errata). */
2966 unsigned attrs = JSPROP_ENUMERATE;
2967 if (!REGS.fp()->isEvalFrame())
2968 attrs |= JSPROP_PERMANENT;
2969 if (*REGS.pc == JSOP_DEFCONST)
2970 attrs |= JSPROP_READONLY;
2972 /* Step 8b. */
2973 RootedObject &obj = rootObject0;
2974 obj = ®S.fp()->varObj();
2976 RootedPropertyName &name = rootName0;
2977 name = script->getName(REGS.pc);
2979 if (!DefVarOrConstOperation(cx, obj, name, attrs))
2980 goto error;
2981 }
2982 END_CASE(JSOP_DEFVAR)
2984 CASE(JSOP_DEFFUN)
2985 {
2986 /*
2987 * A top-level function defined in Global or Eval code (see ECMA-262
2988 * Ed. 3), or else a SpiderMonkey extension: a named function statement in
2989 * a compound statement (not at the top statement level of global code, or
2990 * at the top level of a function body).
2991 */
2992 RootedFunction &fun = rootFunction0;
2993 fun = script->getFunction(GET_UINT32_INDEX(REGS.pc));
2995 if (!DefFunOperation(cx, script, REGS.fp()->scopeChain(), fun))
2996 goto error;
2997 }
2998 END_CASE(JSOP_DEFFUN)
3000 CASE(JSOP_LAMBDA)
3001 {
3002 /* Load the specified function object literal. */
3003 RootedFunction &fun = rootFunction0;
3004 fun = script->getFunction(GET_UINT32_INDEX(REGS.pc));
3006 JSObject *obj = Lambda(cx, fun, REGS.fp()->scopeChain());
3007 if (!obj)
3008 goto error;
3009 JS_ASSERT(obj->getProto());
3010 PUSH_OBJECT(*obj);
3011 }
3012 END_CASE(JSOP_LAMBDA)
3014 CASE(JSOP_LAMBDA_ARROW)
3015 {
3016 /* Load the specified function object literal. */
3017 RootedFunction &fun = rootFunction0;
3018 fun = script->getFunction(GET_UINT32_INDEX(REGS.pc));
3019 RootedValue &thisv = rootValue0;
3020 thisv = REGS.sp[-1];
3021 JSObject *obj = LambdaArrow(cx, fun, REGS.fp()->scopeChain(), thisv);
3022 if (!obj)
3023 goto error;
3024 JS_ASSERT(obj->getProto());
3025 REGS.sp[-1].setObject(*obj);
3026 }
3027 END_CASE(JSOP_LAMBDA_ARROW)
3029 CASE(JSOP_CALLEE)
3030 JS_ASSERT(REGS.fp()->isNonEvalFunctionFrame());
3031 PUSH_COPY(REGS.fp()->calleev());
3032 END_CASE(JSOP_CALLEE)
3034 CASE(JSOP_INITPROP_GETTER)
3035 CASE(JSOP_INITPROP_SETTER)
3036 {
3037 RootedObject &obj = rootObject0;
3038 RootedPropertyName &name = rootName0;
3039 RootedObject &val = rootObject1;
3041 JS_ASSERT(REGS.stackDepth() >= 2);
3042 obj = ®S.sp[-2].toObject();
3043 name = script->getName(REGS.pc);
3044 val = ®S.sp[-1].toObject();
3046 if (!InitGetterSetterOperation(cx, REGS.pc, obj, name, val))
3047 goto error;
3049 REGS.sp--;
3050 }
3051 END_CASE(JSOP_INITPROP_GETTER)
3053 CASE(JSOP_INITELEM_GETTER)
3054 CASE(JSOP_INITELEM_SETTER)
3055 {
3056 RootedObject &obj = rootObject0;
3057 RootedValue &idval = rootValue0;
3058 RootedObject &val = rootObject1;
3060 JS_ASSERT(REGS.stackDepth() >= 3);
3061 obj = ®S.sp[-3].toObject();
3062 idval = REGS.sp[-2];
3063 val = ®S.sp[-1].toObject();
3065 if (!InitGetterSetterOperation(cx, REGS.pc, obj, idval, val))
3066 goto error;
3068 REGS.sp -= 2;
3069 }
3070 END_CASE(JSOP_INITELEM_GETTER)
3072 CASE(JSOP_HOLE)
3073 PUSH_HOLE();
3074 END_CASE(JSOP_HOLE)
3076 CASE(JSOP_NEWINIT)
3077 {
3078 uint8_t i = GET_UINT8(REGS.pc);
3079 JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
3081 RootedObject &obj = rootObject0;
3082 NewObjectKind newKind;
3083 if (i == JSProto_Array) {
3084 newKind = UseNewTypeForInitializer(script, REGS.pc, &ArrayObject::class_);
3085 obj = NewDenseEmptyArray(cx, nullptr, newKind);
3086 } else {
3087 gc::AllocKind allocKind = GuessObjectGCKind(0);
3088 newKind = UseNewTypeForInitializer(script, REGS.pc, &JSObject::class_);
3089 obj = NewBuiltinClassInstance(cx, &JSObject::class_, allocKind, newKind);
3090 }
3091 if (!obj || !SetInitializerObjectType(cx, script, REGS.pc, obj, newKind))
3092 goto error;
3094 PUSH_OBJECT(*obj);
3095 }
3096 END_CASE(JSOP_NEWINIT)
3098 CASE(JSOP_NEWARRAY)
3099 {
3100 unsigned count = GET_UINT24(REGS.pc);
3101 RootedObject &obj = rootObject0;
3102 NewObjectKind newKind = UseNewTypeForInitializer(script, REGS.pc, &ArrayObject::class_);
3103 obj = NewDenseAllocatedArray(cx, count, nullptr, newKind);
3104 if (!obj || !SetInitializerObjectType(cx, script, REGS.pc, obj, newKind))
3105 goto error;
3107 PUSH_OBJECT(*obj);
3108 }
3109 END_CASE(JSOP_NEWARRAY)
3111 CASE(JSOP_NEWOBJECT)
3112 {
3113 RootedObject &baseobj = rootObject0;
3114 baseobj = script->getObject(REGS.pc);
3116 RootedObject &obj = rootObject1;
3117 NewObjectKind newKind = UseNewTypeForInitializer(script, REGS.pc, baseobj->getClass());
3118 obj = CopyInitializerObject(cx, baseobj, newKind);
3119 if (!obj || !SetInitializerObjectType(cx, script, REGS.pc, obj, newKind))
3120 goto error;
3122 PUSH_OBJECT(*obj);
3123 }
3124 END_CASE(JSOP_NEWOBJECT)
3126 CASE(JSOP_ENDINIT)
3127 {
3128 /* FIXME remove JSOP_ENDINIT bug 588522 */
3129 JS_ASSERT(REGS.stackDepth() >= 1);
3130 JS_ASSERT(REGS.sp[-1].isObject() || REGS.sp[-1].isUndefined());
3131 }
3132 END_CASE(JSOP_ENDINIT)
3134 CASE(JSOP_MUTATEPROTO)
3135 {
3136 MOZ_ASSERT(REGS.stackDepth() >= 2);
3138 if (REGS.sp[-1].isObjectOrNull()) {
3139 RootedObject &newProto = rootObject1;
3140 rootObject1 = REGS.sp[-1].toObjectOrNull();
3142 RootedObject &obj = rootObject0;
3143 obj = ®S.sp[-2].toObject();
3144 MOZ_ASSERT(obj->is<JSObject>());
3146 bool succeeded;
3147 if (!JSObject::setProto(cx, obj, newProto, &succeeded))
3148 goto error;
3149 MOZ_ASSERT(succeeded);
3150 }
3152 REGS.sp--;
3153 }
3154 END_CASE(JSOP_MUTATEPROTO);
3156 CASE(JSOP_INITPROP)
3157 {
3158 /* Load the property's initial value into rval. */
3159 JS_ASSERT(REGS.stackDepth() >= 2);
3160 RootedValue &rval = rootValue0;
3161 rval = REGS.sp[-1];
3163 /* Load the object being initialized into lval/obj. */
3164 RootedObject &obj = rootObject0;
3165 obj = ®S.sp[-2].toObject();
3166 JS_ASSERT(obj->is<JSObject>());
3168 PropertyName *name = script->getName(REGS.pc);
3170 RootedId &id = rootId0;
3171 id = NameToId(name);
3173 if (!DefineNativeProperty(cx, obj, id, rval, nullptr, nullptr, JSPROP_ENUMERATE))
3174 goto error;
3176 REGS.sp--;
3177 }
3178 END_CASE(JSOP_INITPROP);
3180 CASE(JSOP_INITELEM)
3181 {
3182 JS_ASSERT(REGS.stackDepth() >= 3);
3183 HandleValue val = REGS.stackHandleAt(-1);
3184 HandleValue id = REGS.stackHandleAt(-2);
3186 RootedObject &obj = rootObject0;
3187 obj = ®S.sp[-3].toObject();
3189 if (!InitElemOperation(cx, obj, id, val))
3190 goto error;
3192 REGS.sp -= 2;
3193 }
3194 END_CASE(JSOP_INITELEM)
3196 CASE(JSOP_INITELEM_ARRAY)
3197 {
3198 JS_ASSERT(REGS.stackDepth() >= 2);
3199 HandleValue val = REGS.stackHandleAt(-1);
3201 RootedObject &obj = rootObject0;
3202 obj = ®S.sp[-2].toObject();
3204 JS_ASSERT(obj->is<ArrayObject>());
3206 uint32_t index = GET_UINT24(REGS.pc);
3207 if (!InitArrayElemOperation(cx, REGS.pc, obj, index, val))
3208 goto error;
3210 REGS.sp--;
3211 }
3212 END_CASE(JSOP_INITELEM_ARRAY)
3214 CASE(JSOP_INITELEM_INC)
3215 {
3216 JS_ASSERT(REGS.stackDepth() >= 3);
3217 HandleValue val = REGS.stackHandleAt(-1);
3219 RootedObject &obj = rootObject0;
3220 obj = ®S.sp[-3].toObject();
3222 uint32_t index = REGS.sp[-2].toInt32();
3223 if (!InitArrayElemOperation(cx, REGS.pc, obj, index, val))
3224 goto error;
3226 REGS.sp[-2].setInt32(index + 1);
3227 REGS.sp--;
3228 }
3229 END_CASE(JSOP_INITELEM_INC)
3231 CASE(JSOP_SPREAD)
3232 {
3233 int32_t count = REGS.sp[-2].toInt32();
3234 RootedObject &arr = rootObject0;
3235 arr = ®S.sp[-3].toObject();
3236 const Value iterable = REGS.sp[-1];
3237 ForOfIterator iter(cx);
3238 RootedValue &iterVal = rootValue0;
3239 iterVal.set(iterable);
3240 if (!iter.init(iterVal))
3241 goto error;
3242 while (true) {
3243 bool done;
3244 if (!iter.next(&iterVal, &done))
3245 goto error;
3246 if (done)
3247 break;
3248 if (count == INT32_MAX) {
3249 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
3250 JSMSG_SPREAD_TOO_LARGE);
3251 goto error;
3252 }
3253 if (!JSObject::defineElement(cx, arr, count++, iterVal, nullptr, nullptr,
3254 JSPROP_ENUMERATE))
3255 goto error;
3256 }
3257 REGS.sp[-2].setInt32(count);
3258 REGS.sp--;
3259 }
3260 END_CASE(JSOP_SPREAD)
3262 CASE(JSOP_GOSUB)
3263 {
3264 PUSH_BOOLEAN(false);
3265 int32_t i = script->pcToOffset(REGS.pc) + JSOP_GOSUB_LENGTH;
3266 int32_t len = GET_JUMP_OFFSET(REGS.pc);
3267 PUSH_INT32(i);
3268 ADVANCE_AND_DISPATCH(len);
3269 }
3271 CASE(JSOP_RETSUB)
3272 {
3273 /* Pop [exception or hole, retsub pc-index]. */
3274 Value rval, lval;
3275 POP_COPY_TO(rval);
3276 POP_COPY_TO(lval);
3277 JS_ASSERT(lval.isBoolean());
3278 if (lval.toBoolean()) {
3279 /*
3280 * Exception was pending during finally, throw it *before* we adjust
3281 * pc, because pc indexes into script->trynotes. This turns out not to
3282 * be necessary, but it seems clearer. And it points out a FIXME:
3283 * 350509, due to Igor Bukanov.
3284 */
3285 cx->setPendingException(rval);
3286 goto error;
3287 }
3288 JS_ASSERT(rval.isInt32());
3290 /* Increment the PC by this much. */
3291 int32_t len = rval.toInt32() - int32_t(script->pcToOffset(REGS.pc));
3292 ADVANCE_AND_DISPATCH(len);
3293 }
3295 CASE(JSOP_EXCEPTION)
3296 {
3297 PUSH_NULL();
3298 MutableHandleValue res = REGS.stackHandleAt(-1);
3299 if (!GetAndClearException(cx, res))
3300 goto error;
3301 }
3302 END_CASE(JSOP_EXCEPTION)
3304 CASE(JSOP_FINALLY)
3305 CHECK_BRANCH();
3306 END_CASE(JSOP_FINALLY)
3308 CASE(JSOP_THROWING)
3309 {
3310 JS_ASSERT(!cx->isExceptionPending());
3311 Value v;
3312 POP_COPY_TO(v);
3313 cx->setPendingException(v);
3314 }
3315 END_CASE(JSOP_THROWING)
3317 CASE(JSOP_THROW)
3318 {
3319 CHECK_BRANCH();
3320 RootedValue &v = rootValue0;
3321 POP_COPY_TO(v);
3322 JS_ALWAYS_FALSE(Throw(cx, v));
3323 /* let the code at error try to catch the exception. */
3324 goto error;
3325 }
3327 CASE(JSOP_INSTANCEOF)
3328 {
3329 RootedValue &rref = rootValue0;
3330 rref = REGS.sp[-1];
3331 if (rref.isPrimitive()) {
3332 js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, -1, rref, js::NullPtr());
3333 goto error;
3334 }
3335 RootedObject &obj = rootObject0;
3336 obj = &rref.toObject();
3337 bool cond = false;
3338 if (!HasInstance(cx, obj, REGS.stackHandleAt(-2), &cond))
3339 goto error;
3340 REGS.sp--;
3341 REGS.sp[-1].setBoolean(cond);
3342 }
3343 END_CASE(JSOP_INSTANCEOF)
3345 CASE(JSOP_DEBUGGER)
3346 {
3347 JSTrapStatus st = JSTRAP_CONTINUE;
3348 RootedValue rval(cx);
3349 if (JSDebuggerHandler handler = cx->runtime()->debugHooks.debuggerHandler)
3350 st = handler(cx, script, REGS.pc, rval.address(), cx->runtime()->debugHooks.debuggerHandlerData);
3351 if (st == JSTRAP_CONTINUE)
3352 st = Debugger::onDebuggerStatement(cx, &rval);
3353 switch (st) {
3354 case JSTRAP_ERROR:
3355 goto error;
3356 case JSTRAP_CONTINUE:
3357 break;
3358 case JSTRAP_RETURN:
3359 REGS.fp()->setReturnValue(rval);
3360 ForcedReturn(cx, REGS);
3361 goto successful_return_continuation;
3362 case JSTRAP_THROW:
3363 cx->setPendingException(rval);
3364 goto error;
3365 default:;
3366 }
3367 }
3368 END_CASE(JSOP_DEBUGGER)
3370 CASE(JSOP_PUSHBLOCKSCOPE)
3371 {
3372 StaticBlockObject &blockObj = script->getObject(REGS.pc)->as<StaticBlockObject>();
3374 JS_ASSERT(blockObj.needsClone());
3375 // Clone block and push on scope chain.
3376 if (!REGS.fp()->pushBlock(cx, blockObj))
3377 goto error;
3378 }
3379 END_CASE(JSOP_PUSHBLOCKSCOPE)
3381 CASE(JSOP_POPBLOCKSCOPE)
3382 {
3383 #ifdef DEBUG
3384 // Pop block from scope chain.
3385 JS_ASSERT(*(REGS.pc - JSOP_DEBUGLEAVEBLOCK_LENGTH) == JSOP_DEBUGLEAVEBLOCK);
3386 NestedScopeObject *scope = script->getStaticScope(REGS.pc - JSOP_DEBUGLEAVEBLOCK_LENGTH);
3387 JS_ASSERT(scope && scope->is<StaticBlockObject>());
3388 StaticBlockObject &blockObj = scope->as<StaticBlockObject>();
3389 JS_ASSERT(blockObj.needsClone());
3390 #endif
3392 // Pop block from scope chain.
3393 REGS.fp()->popBlock(cx);
3394 }
3395 END_CASE(JSOP_POPBLOCKSCOPE)
3397 CASE(JSOP_DEBUGLEAVEBLOCK)
3398 {
3399 JS_ASSERT(script->getStaticScope(REGS.pc));
3400 JS_ASSERT(script->getStaticScope(REGS.pc)->is<StaticBlockObject>());
3402 // FIXME: This opcode should not be necessary. The debugger shouldn't need
3403 // help from bytecode to do its job. See bug 927782.
3405 if (MOZ_UNLIKELY(cx->compartment()->debugMode()))
3406 DebugScopes::onPopBlock(cx, REGS.fp(), REGS.pc);
3407 }
3408 END_CASE(JSOP_DEBUGLEAVEBLOCK)
3410 CASE(JSOP_GENERATOR)
3411 {
3412 JS_ASSERT(!cx->isExceptionPending());
3413 REGS.fp()->initGeneratorFrame();
3414 REGS.pc += JSOP_GENERATOR_LENGTH;
3415 JSObject *obj = js_NewGenerator(cx, REGS);
3416 if (!obj)
3417 goto error;
3418 REGS.fp()->setReturnValue(ObjectValue(*obj));
3419 REGS.fp()->setYielding();
3420 interpReturnOK = true;
3421 if (activation.entryFrame() != REGS.fp())
3422 goto inline_return;
3423 goto exit;
3424 }
3426 CASE(JSOP_YIELD)
3427 JS_ASSERT(!cx->isExceptionPending());
3428 JS_ASSERT(REGS.fp()->isNonEvalFunctionFrame());
3429 if (cx->innermostGenerator()->state == JSGEN_CLOSING) {
3430 RootedValue &val = rootValue0;
3431 val.setObject(REGS.fp()->callee());
3432 js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD, JSDVG_SEARCH_STACK, val, js::NullPtr());
3433 goto error;
3434 }
3435 REGS.fp()->setReturnValue(REGS.sp[-1]);
3436 REGS.fp()->setYielding();
3437 REGS.pc += JSOP_YIELD_LENGTH;
3438 interpReturnOK = true;
3439 goto exit;
3441 CASE(JSOP_ARRAYPUSH)
3442 {
3443 RootedObject &obj = rootObject0;
3444 obj = ®S.sp[-1].toObject();
3445 if (!NewbornArrayPush(cx, obj, REGS.sp[-2]))
3446 goto error;
3447 REGS.sp -= 2;
3448 }
3449 END_CASE(JSOP_ARRAYPUSH)
3451 DEFAULT()
3452 {
3453 char numBuf[12];
3454 JS_snprintf(numBuf, sizeof numBuf, "%d", *REGS.pc);
3455 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
3456 JSMSG_BAD_BYTECODE, numBuf);
3457 goto error;
3458 }
3460 } /* interpreter loop */
3462 MOZ_ASSUME_UNREACHABLE("Interpreter loop exited via fallthrough");
3464 error:
3465 switch (HandleError(cx, REGS)) {
3466 case SuccessfulReturnContinuation:
3467 goto successful_return_continuation;
3469 case ErrorReturnContinuation:
3470 interpReturnOK = false;
3471 goto return_continuation;
3473 case CatchContinuation:
3474 ADVANCE_AND_DISPATCH(0);
3476 case FinallyContinuation:
3477 /*
3478 * Push (true, exception) pair for finally to indicate that [retsub]
3479 * should rethrow the exception.
3480 */
3481 RootedValue &exception = rootValue0;
3482 if (!cx->getPendingException(&exception)) {
3483 interpReturnOK = false;
3484 goto return_continuation;
3485 }
3486 PUSH_BOOLEAN(true);
3487 PUSH_COPY(exception);
3488 cx->clearPendingException();
3489 ADVANCE_AND_DISPATCH(0);
3490 }
3491 MOZ_ASSUME_UNREACHABLE("Invalid HandleError continuation");
3493 exit:
3494 if (MOZ_UNLIKELY(cx->compartment()->debugMode()))
3495 interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), REGS.pc, interpReturnOK);
3496 if (!REGS.fp()->isYielding())
3497 REGS.fp()->epilogue(cx);
3498 else
3499 probes::ExitScript(cx, script, script->functionNonDelazifying(),
3500 REGS.fp()->hasPushedSPSFrame());
3502 gc::MaybeVerifyBarriers(cx, true);
3504 TraceLogStopEvent(logger);
3505 TraceLogStopEvent(logger, scriptLogId);
3507 #ifdef JS_ION
3508 /*
3509 * This path is used when it's guaranteed the method can be finished
3510 * inside the JIT.
3511 */
3512 leave_on_safe_point:
3513 #endif
3515 if (interpReturnOK)
3516 state.setReturnValue(activation.entryFrame()->returnValue());
3518 return interpReturnOK;
3519 }
3521 bool
3522 js::Throw(JSContext *cx, HandleValue v)
3523 {
3524 JS_ASSERT(!cx->isExceptionPending());
3525 cx->setPendingException(v);
3526 return false;
3527 }
3529 bool
3530 js::GetProperty(JSContext *cx, HandleValue v, HandlePropertyName name, MutableHandleValue vp)
3531 {
3532 if (name == cx->names().length) {
3533 // Fast path for strings, arrays and arguments.
3534 if (GetLengthProperty(v, vp))
3535 return true;
3536 }
3538 RootedObject obj(cx, ToObjectFromStack(cx, v));
3539 if (!obj)
3540 return false;
3541 return JSObject::getProperty(cx, obj, obj, name, vp);
3542 }
3544 bool
3545 js::CallProperty(JSContext *cx, HandleValue v, HandlePropertyName name, MutableHandleValue vp)
3546 {
3547 if (!GetProperty(cx, v, name, vp))
3548 return false;
3550 #if JS_HAS_NO_SUCH_METHOD
3551 if (MOZ_UNLIKELY(vp.isUndefined()) && v.isObject())
3552 {
3553 RootedObject obj(cx, &v.toObject());
3554 if (!OnUnknownMethod(cx, obj, StringValue(name), vp))
3555 return false;
3556 }
3557 #endif
3559 return true;
3560 }
3562 bool
3563 js::GetScopeName(JSContext *cx, HandleObject scopeChain, HandlePropertyName name, MutableHandleValue vp)
3564 {
3565 RootedShape shape(cx);
3566 RootedObject obj(cx), pobj(cx);
3567 if (!LookupName(cx, name, scopeChain, &obj, &pobj, &shape))
3568 return false;
3570 if (!shape) {
3571 JSAutoByteString printable;
3572 if (AtomToPrintableString(cx, name, &printable))
3573 js_ReportIsNotDefined(cx, printable.ptr());
3574 return false;
3575 }
3577 return JSObject::getProperty(cx, obj, obj, name, vp);
3578 }
3580 /*
3581 * Alternate form for NAME opcodes followed immediately by a TYPEOF,
3582 * which do not report an exception on (typeof foo == "undefined") tests.
3583 */
3584 bool
3585 js::GetScopeNameForTypeOf(JSContext *cx, HandleObject scopeChain, HandlePropertyName name,
3586 MutableHandleValue vp)
3587 {
3588 RootedShape shape(cx);
3589 RootedObject obj(cx), pobj(cx);
3590 if (!LookupName(cx, name, scopeChain, &obj, &pobj, &shape))
3591 return false;
3593 if (!shape) {
3594 vp.set(UndefinedValue());
3595 return true;
3596 }
3598 return JSObject::getProperty(cx, obj, obj, name, vp);
3599 }
3601 JSObject *
3602 js::Lambda(JSContext *cx, HandleFunction fun, HandleObject parent)
3603 {
3604 MOZ_ASSERT(!fun->isArrow());
3606 RootedObject clone(cx, CloneFunctionObjectIfNotSingleton(cx, fun, parent, TenuredObject));
3607 if (!clone)
3608 return nullptr;
3610 MOZ_ASSERT(clone->global() == clone->global());
3611 return clone;
3612 }
3614 JSObject *
3615 js::LambdaArrow(JSContext *cx, HandleFunction fun, HandleObject parent, HandleValue thisv)
3616 {
3617 MOZ_ASSERT(fun->isArrow());
3619 RootedObject clone(cx, CloneFunctionObjectIfNotSingleton(cx, fun, parent, TenuredObject));
3620 if (!clone)
3621 return nullptr;
3623 MOZ_ASSERT(clone->as<JSFunction>().isArrow());
3624 clone->as<JSFunction>().setExtendedSlot(0, thisv);
3626 MOZ_ASSERT(clone->global() == clone->global());
3627 return clone;
3628 }
3630 bool
3631 js::DefFunOperation(JSContext *cx, HandleScript script, HandleObject scopeChain,
3632 HandleFunction funArg)
3633 {
3634 /*
3635 * If static link is not current scope, clone fun's object to link to the
3636 * current scope via parent. We do this to enable sharing of compiled
3637 * functions among multiple equivalent scopes, amortizing the cost of
3638 * compilation over a number of executions. Examples include XUL scripts
3639 * and event handlers shared among Firefox or other Mozilla app chrome
3640 * windows, and user-defined JS functions precompiled and then shared among
3641 * requests in server-side JS.
3642 */
3643 RootedFunction fun(cx, funArg);
3644 if (fun->isNative() || fun->environment() != scopeChain) {
3645 fun = CloneFunctionObjectIfNotSingleton(cx, fun, scopeChain, TenuredObject);
3646 if (!fun)
3647 return false;
3648 } else {
3649 JS_ASSERT(script->compileAndGo());
3650 JS_ASSERT(!script->functionNonDelazifying());
3651 }
3653 /*
3654 * We define the function as a property of the variable object and not the
3655 * current scope chain even for the case of function expression statements
3656 * and functions defined by eval inside let or with blocks.
3657 */
3658 RootedObject parent(cx, scopeChain);
3659 while (!parent->isVarObj())
3660 parent = parent->enclosingScope();
3662 /* ES5 10.5 (NB: with subsequent errata). */
3663 RootedPropertyName name(cx, fun->atom()->asPropertyName());
3665 RootedShape shape(cx);
3666 RootedObject pobj(cx);
3667 if (!JSObject::lookupProperty(cx, parent, name, &pobj, &shape))
3668 return false;
3670 RootedValue rval(cx, ObjectValue(*fun));
3672 /*
3673 * ECMA requires functions defined when entering Eval code to be
3674 * impermanent.
3675 */
3676 unsigned attrs = script->isActiveEval()
3677 ? JSPROP_ENUMERATE
3678 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
3680 /* Steps 5d, 5f. */
3681 if (!shape || pobj != parent) {
3682 return JSObject::defineProperty(cx, parent, name, rval, JS_PropertyStub,
3683 JS_StrictPropertyStub, attrs);
3684 }
3686 /* Step 5e. */
3687 JS_ASSERT(parent->isNative());
3688 if (parent->is<GlobalObject>()) {
3689 if (shape->configurable()) {
3690 return JSObject::defineProperty(cx, parent, name, rval, JS_PropertyStub,
3691 JS_StrictPropertyStub, attrs);
3692 }
3694 if (shape->isAccessorDescriptor() || !shape->writable() || !shape->enumerable()) {
3695 JSAutoByteString bytes;
3696 if (AtomToPrintableString(cx, name, &bytes)) {
3697 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REDEFINE_PROP,
3698 bytes.ptr());
3699 }
3701 return false;
3702 }
3703 }
3705 /*
3706 * Non-global properties, and global properties which we aren't simply
3707 * redefining, must be set. First, this preserves their attributes.
3708 * Second, this will produce warnings and/or errors as necessary if the
3709 * specified Call object property is not writable (const).
3710 */
3712 /* Step 5f. */
3713 return JSObject::setProperty(cx, parent, parent, name, &rval, script->strict());
3714 }
3716 bool
3717 js::SetCallOperation(JSContext *cx)
3718 {
3719 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_LEFTSIDE_OF_ASS);
3720 return false;
3721 }
3723 bool
3724 js::GetAndClearException(JSContext *cx, MutableHandleValue res)
3725 {
3726 bool status = cx->getPendingException(res);
3727 cx->clearPendingException();
3728 if (!status)
3729 return false;
3731 // Allow interrupting deeply nested exception handling.
3732 return CheckForInterrupt(cx);
3733 }
3735 template <bool strict>
3736 bool
3737 js::SetProperty(JSContext *cx, HandleObject obj, HandleId id, const Value &value)
3738 {
3739 RootedValue v(cx, value);
3740 return JSObject::setGeneric(cx, obj, obj, id, &v, strict);
3741 }
3743 template bool js::SetProperty<true> (JSContext *cx, HandleObject obj, HandleId id, const Value &value);
3744 template bool js::SetProperty<false>(JSContext *cx, HandleObject obj, HandleId id, const Value &value);
3746 template <bool strict>
3747 bool
3748 js::DeleteProperty(JSContext *cx, HandleValue v, HandlePropertyName name, bool *bp)
3749 {
3750 RootedObject obj(cx, ToObjectFromStack(cx, v));
3751 if (!obj)
3752 return false;
3754 if (!JSObject::deleteProperty(cx, obj, name, bp))
3755 return false;
3757 if (strict && !*bp) {
3758 obj->reportNotConfigurable(cx, NameToId(name));
3759 return false;
3760 }
3761 return true;
3762 }
3764 template bool js::DeleteProperty<true> (JSContext *cx, HandleValue val, HandlePropertyName name, bool *bp);
3765 template bool js::DeleteProperty<false>(JSContext *cx, HandleValue val, HandlePropertyName name, bool *bp);
3767 template <bool strict>
3768 bool
3769 js::DeleteElement(JSContext *cx, HandleValue val, HandleValue index, bool *bp)
3770 {
3771 RootedObject obj(cx, ToObjectFromStack(cx, val));
3772 if (!obj)
3773 return false;
3775 if (!JSObject::deleteByValue(cx, obj, index, bp))
3776 return false;
3778 if (strict && !*bp) {
3779 // XXX This observably calls ToString(propval). We should convert to
3780 // PropertyKey and use that to delete, and to report an error if
3781 // necessary!
3782 RootedId id(cx);
3783 if (!ValueToId<CanGC>(cx, index, &id))
3784 return false;
3785 obj->reportNotConfigurable(cx, id);
3786 return false;
3787 }
3788 return true;
3789 }
3791 template bool js::DeleteElement<true> (JSContext *, HandleValue, HandleValue, bool *succeeded);
3792 template bool js::DeleteElement<false>(JSContext *, HandleValue, HandleValue, bool *succeeded);
3794 bool
3795 js::GetElement(JSContext *cx, MutableHandleValue lref, HandleValue rref, MutableHandleValue vp)
3796 {
3797 return GetElementOperation(cx, JSOP_GETELEM, lref, rref, vp);
3798 }
3800 bool
3801 js::CallElement(JSContext *cx, MutableHandleValue lref, HandleValue rref, MutableHandleValue res)
3802 {
3803 return GetElementOperation(cx, JSOP_CALLELEM, lref, rref, res);
3804 }
3806 bool
3807 js::SetObjectElement(JSContext *cx, HandleObject obj, HandleValue index, HandleValue value,
3808 bool strict)
3809 {
3810 RootedId id(cx);
3811 if (!ValueToId<CanGC>(cx, index, &id))
3812 return false;
3813 return SetObjectElementOperation(cx, obj, id, value, strict);
3814 }
3816 bool
3817 js::SetObjectElement(JSContext *cx, HandleObject obj, HandleValue index, HandleValue value,
3818 bool strict, HandleScript script, jsbytecode *pc)
3819 {
3820 JS_ASSERT(pc);
3821 RootedId id(cx);
3822 if (!ValueToId<CanGC>(cx, index, &id))
3823 return false;
3824 return SetObjectElementOperation(cx, obj, id, value, strict, script, pc);
3825 }
3827 bool
3828 js::InitElementArray(JSContext *cx, jsbytecode *pc, HandleObject obj, uint32_t index, HandleValue value)
3829 {
3830 return InitArrayElemOperation(cx, pc, obj, index, value);
3831 }
3833 bool
3834 js::AddValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res)
3835 {
3836 return AddOperation(cx, lhs, rhs, res);
3837 }
3839 bool
3840 js::SubValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res)
3841 {
3842 return SubOperation(cx, lhs, rhs, res);
3843 }
3845 bool
3846 js::MulValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res)
3847 {
3848 return MulOperation(cx, lhs, rhs, res);
3849 }
3851 bool
3852 js::DivValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res)
3853 {
3854 return DivOperation(cx, lhs, rhs, res);
3855 }
3857 bool
3858 js::ModValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res)
3859 {
3860 return ModOperation(cx, lhs, rhs, res);
3861 }
3863 bool
3864 js::UrshValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res)
3865 {
3866 return UrshOperation(cx, lhs, rhs, res);
3867 }
3869 bool
3870 js::DeleteNameOperation(JSContext *cx, HandlePropertyName name, HandleObject scopeObj,
3871 MutableHandleValue res)
3872 {
3873 RootedObject scope(cx), pobj(cx);
3874 RootedShape shape(cx);
3875 if (!LookupName(cx, name, scopeObj, &scope, &pobj, &shape))
3876 return false;
3878 if (!scope) {
3879 // Return true for non-existent names.
3880 res.setBoolean(true);
3881 return true;
3882 }
3884 bool succeeded;
3885 if (!JSObject::deleteProperty(cx, scope, name, &succeeded))
3886 return false;
3887 res.setBoolean(succeeded);
3888 return true;
3889 }
3891 bool
3892 js::ImplicitThisOperation(JSContext *cx, HandleObject scopeObj, HandlePropertyName name,
3893 MutableHandleValue res)
3894 {
3895 RootedObject obj(cx);
3896 if (!LookupNameWithGlobalDefault(cx, name, scopeObj, &obj))
3897 return false;
3899 return ComputeImplicitThis(cx, obj, res);
3900 }
3902 bool
3903 js::RunOnceScriptPrologue(JSContext *cx, HandleScript script)
3904 {
3905 JS_ASSERT(script->treatAsRunOnce());
3907 if (!script->hasRunOnce()) {
3908 script->setHasRunOnce();
3909 return true;
3910 }
3912 // Force instantiation of the script's function's type to ensure the flag
3913 // is preserved in type information.
3914 if (!script->functionNonDelazifying()->getType(cx))
3915 return false;
3917 types::MarkTypeObjectFlags(cx, script->functionNonDelazifying(),
3918 types::OBJECT_FLAG_RUNONCE_INVALIDATED);
3919 return true;
3920 }
3922 bool
3923 js::InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandleId id,
3924 HandleObject val)
3925 {
3926 JS_ASSERT(val->isCallable());
3927 PropertyOp getter;
3928 StrictPropertyOp setter;
3929 unsigned attrs = JSPROP_ENUMERATE | JSPROP_SHARED;
3931 JSOp op = JSOp(*pc);
3933 if (op == JSOP_INITPROP_GETTER || op == JSOP_INITELEM_GETTER) {
3934 getter = CastAsPropertyOp(val);
3935 setter = JS_StrictPropertyStub;
3936 attrs |= JSPROP_GETTER;
3937 } else {
3938 JS_ASSERT(op == JSOP_INITPROP_SETTER || op == JSOP_INITELEM_SETTER);
3939 getter = JS_PropertyStub;
3940 setter = CastAsStrictPropertyOp(val);
3941 attrs |= JSPROP_SETTER;
3942 }
3944 RootedValue scratch(cx);
3945 return JSObject::defineGeneric(cx, obj, id, scratch, getter, setter, attrs);
3946 }
3948 bool
3949 js::InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj,
3950 HandlePropertyName name, HandleObject val)
3951 {
3952 RootedId id(cx, NameToId(name));
3953 return InitGetterSetterOperation(cx, pc, obj, id, val);
3954 }
3956 bool
3957 js::InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandleValue idval,
3958 HandleObject val)
3959 {
3960 RootedId id(cx);
3961 if (!ValueToId<CanGC>(cx, idval, &id))
3962 return false;
3964 return InitGetterSetterOperation(cx, pc, obj, id, val);
3965 }