js/src/vm/Interpreter.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial