Frame.cpp (66485B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "debugger/Frame-inl.h" 8 9 #include "mozilla/Assertions.h" // for MOZ_ASSERT, MOZ_ASSERT_IF, MOZ_CRASH 10 #include "mozilla/HashTable.h" // for HashMapEntry 11 #include "mozilla/Maybe.h" // for Maybe 12 #include "mozilla/Range.h" // for Range 13 #include "mozilla/RangedPtr.h" // for RangedPtr 14 #include "mozilla/Result.h" // for Result 15 #include "mozilla/ScopeExit.h" // for ScopeExit 16 #include "mozilla/Vector.h" // for Vector 17 18 #include <stddef.h> // for size_t 19 #include <stdint.h> // for int32_t 20 #include <string.h> // for strlen 21 #include <utility> // for std::move 22 23 #include "jsnum.h" // for Int32ToString 24 25 #include "builtin/Array.h" // for NewDenseCopiedArray 26 #include "debugger/Debugger.h" // for Completion, Debugger 27 #include "debugger/DebugScript.h" 28 #include "debugger/Environment.h" // for DebuggerEnvironment 29 #include "debugger/NoExecute.h" // for LeaveDebuggeeNoExecute 30 #include "debugger/Object.h" // for DebuggerObject 31 #include "debugger/Script.h" // for DebuggerScript 32 #include "frontend/BytecodeCompiler.h" // for CompileGlobalScript, CompileGlobalScriptWithExtraBindings, CompileEvalScript 33 #include "frontend/FrontendContext.h" // for AutoReportFrontendContext 34 #include "gc/Barrier.h" // for HeapPtr 35 #include "gc/GC.h" // for MemoryUse 36 #include "gc/GCContext.h" // for JS::GCContext 37 #include "gc/Marking.h" // for IsAboutToBeFinalized 38 #include "gc/Tracer.h" // for TraceCrossCompartmentEdge 39 #include "gc/ZoneAllocator.h" // for AddCellMemory 40 #include "jit/JSJitFrameIter.h" // for InlineFrameIterator 41 #include "jit/RematerializedFrame.h" // for RematerializedFrame 42 #include "js/CallArgs.h" // for CallArgs 43 #include "js/EnvironmentChain.h" // JS::EnvironmentChain 44 #include "js/friend/ErrorMessages.h" // for GetErrorMessage, JSMSG_* 45 #include "js/GCVector.h" // for JS::StackGCVector 46 #include "js/Object.h" // for SetReservedSlot 47 #include "js/Proxy.h" // for PrivateValue 48 #include "js/RootingAPI.h" // for JS::Rooted, JS::Handle, JS::MutableHandle 49 #include "js/SourceText.h" // for SourceText, SourceOwnership 50 #include "js/StableStringChars.h" // for AutoStableStringChars 51 #include "vm/ArgumentsObject.h" // for ArgumentsObject 52 #include "vm/ArrayObject.h" // for ArrayObject 53 #include "vm/AsyncFunction.h" // for AsyncFunctionGeneratorObject 54 #include "vm/AsyncIteration.h" // for AsyncGeneratorObject 55 #include "vm/BytecodeUtil.h" // for JSDVG_SEARCH_STACK 56 #include "vm/Compartment.h" // for Compartment 57 #include "vm/EnvironmentObject.h" // for GlobalLexicalEnvironmentObject 58 #include "vm/GeneratorObject.h" // for AbstractGeneratorObject 59 #include "vm/GlobalObject.h" // for GlobalObject 60 #include "vm/Interpreter.h" // for Call, ExecuteKernel 61 #include "vm/JSAtomUtils.h" // for Atomize, AtomizeUTF8Chars 62 #include "vm/JSContext.h" // for JSContext, ReportValueError 63 #include "vm/JSFunction.h" // for JSFunction, NewNativeFunction 64 #include "vm/JSObject.h" // for JSObject, RequireObject 65 #include "vm/JSScript.h" // for JSScript 66 #include "vm/NativeObject.h" // for NativeDefineDataProperty 67 #include "vm/Realm.h" // for AutoRealm 68 #include "vm/Runtime.h" // for JSAtomState 69 #include "vm/Scope.h" // for PositionalFormalParameterIter 70 #include "vm/Stack.h" // for AbstractFramePtr, FrameIter 71 #include "vm/StringType.h" // for PropertyName, JSString 72 #include "wasm/WasmDebug.h" // for DebugState 73 #include "wasm/WasmDebugFrame.h" // for DebugFrame 74 #include "wasm/WasmInstance.h" // for Instance 75 #include "wasm/WasmJS.h" // for WasmInstanceObject 76 77 #include "debugger/Debugger-inl.h" // for Debugger::fromJSObject 78 #include "gc/WeakMap-inl.h" // for WeakMap::remove 79 #include "vm/Compartment-inl.h" // for Compartment::wrap 80 #include "vm/JSContext-inl.h" // for JSContext::check 81 #include "vm/JSObject-inl.h" // for NewObjectWithGivenProto 82 #include "vm/NativeObject-inl.h" // for NativeObject::global 83 #include "vm/ObjectOperations-inl.h" // for GetProperty 84 #include "vm/Realm-inl.h" // for AutoRealm::AutoRealm 85 #include "vm/Stack-inl.h" // for AbstractFramePtr::script 86 87 namespace js { 88 namespace jit { 89 class JitFrameLayout; 90 } /* namespace jit */ 91 } /* namespace js */ 92 93 using namespace js; 94 95 using JS::AutoStableStringChars; 96 using JS::CompileOptions; 97 using JS::SourceOwnership; 98 using JS::SourceText; 99 using mozilla::Maybe; 100 101 ScriptedOnStepHandler::ScriptedOnStepHandler(JSObject* object) 102 : object_(object) { 103 MOZ_ASSERT(object_->isCallable()); 104 } 105 106 JSObject* ScriptedOnStepHandler::object() const { return object_; } 107 108 void ScriptedOnStepHandler::hold(JSObject* owner) { 109 AddCellMemory(owner, allocSize(), MemoryUse::DebuggerOnStepHandler); 110 } 111 112 void ScriptedOnStepHandler::drop(JS::GCContext* gcx, JSObject* owner) { 113 gcx->delete_(owner, this, allocSize(), MemoryUse::DebuggerOnStepHandler); 114 } 115 116 void ScriptedOnStepHandler::trace(JSTracer* tracer) { 117 TraceEdge(tracer, &object_, "OnStepHandlerFunction.object"); 118 } 119 120 bool ScriptedOnStepHandler::onStep(JSContext* cx, Handle<DebuggerFrame*> frame, 121 ResumeMode& resumeMode, 122 MutableHandleValue vp) { 123 RootedValue fval(cx, ObjectValue(*object_)); 124 RootedValue rval(cx); 125 if (!js::Call(cx, fval, frame, &rval)) { 126 return false; 127 } 128 129 return ParseResumptionValue(cx, rval, resumeMode, vp); 130 }; 131 132 size_t ScriptedOnStepHandler::allocSize() const { return sizeof(*this); } 133 134 ScriptedOnPopHandler::ScriptedOnPopHandler(JSObject* object) : object_(object) { 135 MOZ_ASSERT(object->isCallable()); 136 } 137 138 JSObject* ScriptedOnPopHandler::object() const { return object_; } 139 140 void ScriptedOnPopHandler::hold(JSObject* owner) { 141 AddCellMemory(owner, allocSize(), MemoryUse::DebuggerOnPopHandler); 142 } 143 144 void ScriptedOnPopHandler::drop(JS::GCContext* gcx, JSObject* owner) { 145 gcx->delete_(owner, this, allocSize(), MemoryUse::DebuggerOnPopHandler); 146 } 147 148 void ScriptedOnPopHandler::trace(JSTracer* tracer) { 149 TraceEdge(tracer, &object_, "OnStepHandlerFunction.object"); 150 } 151 152 bool ScriptedOnPopHandler::onPop(JSContext* cx, Handle<DebuggerFrame*> frame, 153 const Completion& completion, 154 ResumeMode& resumeMode, 155 MutableHandleValue vp) { 156 Debugger* dbg = frame->owner(); 157 158 RootedValue completionValue(cx); 159 if (!completion.buildCompletionValue(cx, dbg, &completionValue)) { 160 return false; 161 } 162 163 RootedValue fval(cx, ObjectValue(*object_)); 164 RootedValue rval(cx); 165 if (!js::Call(cx, fval, frame, completionValue, &rval)) { 166 return false; 167 } 168 169 return ParseResumptionValue(cx, rval, resumeMode, vp); 170 }; 171 172 size_t ScriptedOnPopHandler::allocSize() const { return sizeof(*this); } 173 174 js::Debugger* js::DebuggerFrame::owner() const { 175 JSObject* dbgobj = &getReservedSlot(OWNER_SLOT).toObject(); 176 return Debugger::fromJSObject(dbgobj); 177 } 178 179 const JSClassOps DebuggerFrame::classOps_ = { 180 nullptr, // addProperty 181 nullptr, // delProperty 182 nullptr, // enumerate 183 nullptr, // newEnumerate 184 nullptr, // resolve 185 nullptr, // mayResolve 186 finalize, // finalize 187 nullptr, // call 188 nullptr, // construct 189 CallTraceMethod<DebuggerFrame>, // trace 190 }; 191 192 const JSClass DebuggerFrame::class_ = { 193 "Frame", 194 JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | 195 // We require foreground finalization so we can destruct GeneratorInfo's 196 // HeapPtrs. 197 JSCLASS_FOREGROUND_FINALIZE, 198 &DebuggerFrame::classOps_, 199 }; 200 201 enum { JSSLOT_DEBUGARGUMENTS_FRAME, JSSLOT_DEBUGARGUMENTS_COUNT }; 202 203 const JSClass DebuggerArguments::class_ = { 204 "Arguments", 205 JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGARGUMENTS_COUNT), 206 }; 207 208 bool DebuggerFrame::resume(const FrameIter& iter) { 209 FrameIter::Data* data = iter.copyData(); 210 if (!data) { 211 return false; 212 } 213 setFrameIterData(data); 214 return true; 215 } 216 217 void DebuggerFrame::suspendWasmFrame(JS::GCContext* gcx) { 218 freeFrameIterData(gcx); 219 } 220 221 bool DebuggerFrame::hasAnyHooks() const { 222 return !getReservedSlot(ONSTEP_HANDLER_SLOT).isUndefined() || 223 !getReservedSlot(ONPOP_HANDLER_SLOT).isUndefined(); 224 } 225 226 /* static */ 227 NativeObject* DebuggerFrame::initClass(JSContext* cx, 228 Handle<GlobalObject*> global, 229 HandleObject dbgCtor) { 230 return InitClass(cx, dbgCtor, nullptr, nullptr, "Frame", construct, 0, 231 properties_, methods_, nullptr, nullptr); 232 } 233 234 /* static */ 235 DebuggerFrame* DebuggerFrame::create( 236 JSContext* cx, HandleObject proto, Handle<NativeObject*> debugger, 237 const FrameIter* maybeIter, 238 Handle<AbstractGeneratorObject*> maybeGenerator) { 239 Rooted<DebuggerFrame*> frame( 240 cx, NewObjectWithGivenProto<DebuggerFrame>(cx, proto)); 241 if (!frame) { 242 return nullptr; 243 } 244 245 frame->setReservedSlot(OWNER_SLOT, ObjectValue(*debugger)); 246 247 if (maybeIter) { 248 FrameIter::Data* data = maybeIter->copyData(); 249 if (!data) { 250 return nullptr; 251 } 252 253 frame->setFrameIterData(data); 254 } 255 256 if (maybeGenerator) { 257 if (!DebuggerFrame::setGeneratorInfo(cx, frame, maybeGenerator)) { 258 frame->freeFrameIterData(cx->gcContext()); 259 return nullptr; 260 } 261 } 262 263 return frame; 264 } 265 266 /** 267 * Information held by a DebuggerFrame about a generator/async call. A 268 * Debugger.Frame's GENERATOR_INFO_SLOT, if set, holds a PrivateValue pointing 269 * to one of these. 270 * 271 * This is created and attached as soon as a generator object is created for a 272 * debuggee generator/async frame, retained across suspensions and resumptions, 273 * and cleared when the generator call ends permanently. 274 * 275 * It may seem like this information might belong in ordinary reserved slots on 276 * the DebuggerFrame object. But that isn't possible: 277 * 278 * 1) Slots cannot contain cross-compartment references directly. 279 * 2) Ordinary cross-compartment wrappers aren't good enough, because the 280 * debugger must create its own magic entries in the wrapper table for the GC 281 * to get zone collection groups right. 282 * 3) Even if we make debugger wrapper table entries by hand, hiding 283 * cross-compartment edges as PrivateValues doesn't call post-barriers, and 284 * the generational GC won't update our pointer when the generator object 285 * gets tenured. 286 * 287 * Yes, officer, I definitely knew all this in advance and designed it this way 288 * the first time. 289 * 290 * Note that it is not necessary to have a second cross-compartment wrapper 291 * table entry to cover the pointer to the generator's script. The wrapper table 292 * entries play two roles: they help the GC put a debugger zone in the same zone 293 * group as its debuggee, and they serve as roots when collecting the debuggee 294 * zone, but not the debugger zone. Since an AbstractGeneratorObject holds a 295 * strong reference to its callee's script (via the callee), and the AGO and the 296 * script are always in the same compartment, it suffices to add a 297 * cross-compartment wrapper table entry for the Debugger.Frame -> AGO edge. 298 */ 299 class DebuggerFrame::GeneratorInfo { 300 // An unwrapped cross-compartment reference to the generator object. 301 // 302 // Always an object. 303 // 304 // This cannot be GCPtr because we are not always destructed during sweeping; 305 // a Debugger.Frame's generator is also cleared when the generator returns 306 // permanently. 307 const HeapPtr<Value> unwrappedGenerator_; 308 309 // A cross-compartment reference to the generator's script. 310 const HeapPtr<JSScript*> generatorScript_; 311 312 public: 313 GeneratorInfo(Handle<AbstractGeneratorObject*> unwrappedGenerator, 314 HandleScript generatorScript) 315 : unwrappedGenerator_(ObjectValue(*unwrappedGenerator)), 316 generatorScript_(generatorScript) {} 317 318 // Trace a rooted instance of this class, e.g. a Rooted<GeneratorInfo>. 319 void trace(JSTracer* tracer) { 320 TraceRoot(tracer, &unwrappedGenerator_, "Debugger.Frame generator object"); 321 TraceRoot(tracer, &generatorScript_, "Debugger.Frame generator script"); 322 } 323 // Trace a GeneratorInfo from a DebuggerFrame object. 324 void trace(JSTracer* tracer, DebuggerFrame& frameObj) { 325 TraceCrossCompartmentEdge(tracer, &frameObj, &unwrappedGenerator_, 326 "Debugger.Frame generator object"); 327 TraceCrossCompartmentEdge(tracer, &frameObj, &generatorScript_, 328 "Debugger.Frame generator script"); 329 } 330 331 AbstractGeneratorObject& unwrappedGenerator() const { 332 return unwrappedGenerator_.toObject().as<AbstractGeneratorObject>(); 333 } 334 335 JSScript* generatorScript() { return generatorScript_; } 336 337 bool isGeneratorScriptAboutToBeFinalized() { 338 return IsAboutToBeFinalized(generatorScript_); 339 } 340 }; 341 342 bool js::DebuggerFrame::isSuspended() const { 343 return hasGeneratorInfo() && 344 generatorInfo()->unwrappedGenerator().isSuspended(); 345 } 346 347 js::AbstractGeneratorObject& js::DebuggerFrame::unwrappedGenerator() const { 348 return generatorInfo()->unwrappedGenerator(); 349 } 350 351 #ifdef DEBUG 352 JSScript* js::DebuggerFrame::generatorScript() const { 353 return generatorInfo()->generatorScript(); 354 } 355 #endif 356 357 /* static */ 358 bool DebuggerFrame::setGeneratorInfo(JSContext* cx, 359 Handle<DebuggerFrame*> frame, 360 Handle<AbstractGeneratorObject*> genObj) { 361 cx->check(frame); 362 363 MOZ_ASSERT(!frame->hasGeneratorInfo()); 364 MOZ_ASSERT(!genObj->isClosed()); 365 366 // When we initialize the generator information, we do not need to adjust 367 // the stepper increment, because either it was already incremented when 368 // the step hook was added, or we're setting this into on a new DebuggerFrame 369 // that has not yet had the chance for a hook to be added to it. 370 MOZ_ASSERT_IF(frame->onStepHandler(), frame->frameIterData()); 371 MOZ_ASSERT_IF(!frame->frameIterData(), !frame->onStepHandler()); 372 373 // There are two relations we must establish: 374 // 375 // 1) The DebuggerFrame must point to the AbstractGeneratorObject. 376 // 377 // 2) The generator's script's observer count must be bumped. 378 379 RootedScript script(cx, genObj->callee().nonLazyScript()); 380 Rooted<UniquePtr<GeneratorInfo>> info( 381 cx, cx->make_unique<GeneratorInfo>(genObj, script)); 382 if (!info) { 383 return false; 384 } 385 386 AutoRealm ar(cx, script); 387 388 // All frames running a debuggee script must themselves be marked as 389 // debuggee frames. Bumping a script's generator observer count makes it a 390 // debuggee, so we need to mark all frames on the stack running it as 391 // debuggees as well, not just this one. This call takes care of all that. 392 if (!Debugger::ensureExecutionObservabilityOfScript(cx, script)) { 393 return false; 394 } 395 396 if (!DebugScript::incrementGeneratorObserverCount(cx, script)) { 397 return false; 398 } 399 400 InitReservedSlot(frame, GENERATOR_INFO_SLOT, info.release(), 401 MemoryUse::DebuggerFrameGeneratorInfo); 402 return true; 403 } 404 405 void DebuggerFrame::terminate(JS::GCContext* gcx, AbstractFramePtr frame) { 406 if (frameIterData()) { 407 // If no frame pointer was provided to decrement the stepper counter, 408 // then we must be terminating a generator, otherwise the stepper count 409 // would have no way to synchronize properly. 410 MOZ_ASSERT_IF(!frame, hasGeneratorInfo()); 411 412 freeFrameIterData(gcx); 413 if (frame && !hasGeneratorInfo() && onStepHandler()) { 414 // If we are terminating a non-generator frame that had a step handler, 415 // we need to decrement the counter to keep things in sync. 416 decrementStepperCounter(gcx, frame); 417 } 418 } 419 420 if (!hasGeneratorInfo()) { 421 return; 422 } 423 424 GeneratorInfo* info = generatorInfo(); 425 426 // 3) The generator's script's observer count must be dropped. 427 // 428 // For ordinary calls, Debugger.Frame objects drop the script's stepper count 429 // when the frame is popped, but for generators, they leave the stepper count 430 // incremented across suspensions. This means that, whereas ordinary calls 431 // never need to drop the stepper count from the D.F finalizer, generator 432 // calls may. 433 if (!info->isGeneratorScriptAboutToBeFinalized()) { 434 JSScript* generatorScript = info->generatorScript(); 435 DebugScript::decrementGeneratorObserverCount(gcx, generatorScript); 436 if (onStepHandler()) { 437 // If we are terminating a generator frame that had a step handler, 438 // we need to decrement the counter to keep things in sync. 439 decrementStepperCounter(gcx, generatorScript); 440 } 441 } 442 443 // 1) The DebuggerFrame must no longer point to the AbstractGeneratorObject. 444 setReservedSlot(GENERATOR_INFO_SLOT, UndefinedValue()); 445 gcx->delete_(this, info, MemoryUse::DebuggerFrameGeneratorInfo); 446 } 447 448 void DebuggerFrame::onGeneratorClosed(JS::GCContext* gcx) { 449 GeneratorInfo* info = generatorInfo(); 450 451 // If the generator is closed, eagerly drop the onStep handler, to make sure 452 // the stepper counter matches in the assertion in DebugAPI::onSingleStep. 453 // Also clear the slot in order to suppress the decrementStepperCounter in 454 // DebuggerFrame::terminate. 455 if (!info->isGeneratorScriptAboutToBeFinalized()) { 456 JSScript* generatorScript = info->generatorScript(); 457 OnStepHandler* handler = onStepHandler(); 458 if (handler) { 459 decrementStepperCounter(gcx, generatorScript); 460 setReservedSlot(ONSTEP_HANDLER_SLOT, UndefinedValue()); 461 handler->drop(gcx, this); 462 } 463 } 464 } 465 466 void DebuggerFrame::suspend(JS::GCContext* gcx) { 467 // There must be generator info because otherwise this would be the same 468 // overall behavior as terminate() except that here we do not properly 469 // adjust stepper counts. 470 MOZ_ASSERT(hasGeneratorInfo()); 471 472 freeFrameIterData(gcx); 473 } 474 475 /* static */ 476 bool DebuggerFrame::getCallee(JSContext* cx, Handle<DebuggerFrame*> frame, 477 MutableHandle<DebuggerObject*> result) { 478 RootedObject callee(cx); 479 if (frame->isOnStack()) { 480 AbstractFramePtr referent = DebuggerFrame::getReferent(frame); 481 if (referent.isFunctionFrame()) { 482 callee = referent.callee(); 483 } 484 } else { 485 MOZ_ASSERT(frame->isSuspended()); 486 487 callee = &frame->generatorInfo()->unwrappedGenerator().callee(); 488 } 489 490 return frame->owner()->wrapNullableDebuggeeObject(cx, callee, result); 491 } 492 493 /* static */ 494 bool DebuggerFrame::getIsConstructing(JSContext* cx, 495 Handle<DebuggerFrame*> frame, 496 bool& result) { 497 if (frame->isOnStack()) { 498 FrameIter iter = frame->getFrameIter(cx); 499 500 result = iter.isFunctionFrame() && iter.isConstructing(); 501 } else { 502 MOZ_ASSERT(frame->isSuspended()); 503 504 // Generators and async functions can't be constructed. 505 result = false; 506 } 507 return true; 508 } 509 510 static void UpdateFrameIterPc(FrameIter& iter) { 511 if (iter.abstractFramePtr().isWasmDebugFrame()) { 512 // Wasm debug frames don't need their pc updated -- it's null. 513 return; 514 } 515 516 if (iter.abstractFramePtr().isRematerializedFrame()) { 517 #ifdef DEBUG 518 // Rematerialized frames don't need their pc updated. The reason we 519 // need to update pc is because we might get the same Debugger.Frame 520 // object for multiple re-entries into debugger code from debuggee 521 // code. This reentrancy is not possible with rematerialized frames, 522 // because when returning to debuggee code, we would have bailed out 523 // to baseline. 524 // 525 // We walk the stack to assert that it doesn't need updating. 526 jit::RematerializedFrame* frame = 527 iter.abstractFramePtr().asRematerializedFrame(); 528 jit::JitFrameLayout* jsFrame = (jit::JitFrameLayout*)frame->top(); 529 jit::JitActivation* activation = iter.activation()->asJit(); 530 531 JSContext* cx = TlsContext.get(); 532 MOZ_ASSERT(cx == activation->cx()); 533 534 ActivationIterator activationIter(cx); 535 while (activationIter.activation() != activation) { 536 ++activationIter; 537 } 538 539 OnlyJSJitFrameIter jitIter(activationIter); 540 while (!jitIter.frame().isIonJS() || jitIter.frame().jsFrame() != jsFrame) { 541 ++jitIter; 542 } 543 544 jit::InlineFrameIterator ionInlineIter(cx, &jitIter.frame()); 545 while (ionInlineIter.frameNo() != frame->frameNo()) { 546 ++ionInlineIter; 547 } 548 549 MOZ_ASSERT(ionInlineIter.pc() == iter.pc()); 550 #endif 551 return; 552 } 553 554 iter.updatePcQuadratic(); 555 } 556 557 /* static */ 558 bool DebuggerFrame::getEnvironment(JSContext* cx, Handle<DebuggerFrame*> frame, 559 MutableHandle<DebuggerEnvironment*> result) { 560 Debugger* dbg = frame->owner(); 561 Rooted<Env*> env(cx); 562 563 if (frame->isOnStack()) { 564 FrameIter iter = frame->getFrameIter(cx); 565 566 { 567 AutoRealm ar(cx, iter.abstractFramePtr().environmentChain()); 568 UpdateFrameIterPc(iter); 569 env = GetDebugEnvironmentForFrame(cx, iter.abstractFramePtr(), iter.pc()); 570 } 571 } else { 572 MOZ_ASSERT(frame->isSuspended()); 573 574 AbstractGeneratorObject& genObj = 575 frame->generatorInfo()->unwrappedGenerator(); 576 JSScript* script = frame->generatorInfo()->generatorScript(); 577 578 { 579 AutoRealm ar(cx, &genObj.environmentChain()); 580 env = GetDebugEnvironmentForSuspendedGenerator(cx, script, genObj); 581 } 582 } 583 584 if (!env) { 585 return false; 586 } 587 588 return dbg->wrapEnvironment(cx, env, result); 589 } 590 591 /* static */ 592 bool DebuggerFrame::getOffset(JSContext* cx, Handle<DebuggerFrame*> frame, 593 size_t& result) { 594 if (frame->isOnStack()) { 595 FrameIter iter = frame->getFrameIter(cx); 596 597 AbstractFramePtr referent = DebuggerFrame::getReferent(frame); 598 if (referent.isWasmDebugFrame()) { 599 iter.wasmUpdateBytecodeOffset(); 600 result = iter.wasmBytecodeOffset(); 601 } else { 602 JSScript* script = iter.script(); 603 UpdateFrameIterPc(iter); 604 jsbytecode* pc = iter.pc(); 605 result = script->pcToOffset(pc); 606 } 607 } else { 608 MOZ_ASSERT(frame->isSuspended()); 609 610 AbstractGeneratorObject& genObj = 611 frame->generatorInfo()->unwrappedGenerator(); 612 JSScript* script = frame->generatorInfo()->generatorScript(); 613 result = script->resumeOffsets()[genObj.resumeIndex()]; 614 } 615 return true; 616 } 617 618 /* static */ 619 bool DebuggerFrame::getOlder(JSContext* cx, Handle<DebuggerFrame*> frame, 620 MutableHandle<DebuggerFrame*> result) { 621 if (frame->isOnStack()) { 622 Debugger* dbg = frame->owner(); 623 FrameIter iter = frame->getFrameIter(cx); 624 625 while (true) { 626 Activation& activation = *iter.activation(); 627 ++iter; 628 629 // If the parent frame crosses an explicit async stack boundary, we 630 // treat that as an indication to stop traversing sync frames, so that 631 // the on-stack Debugger.Frame instances align with what you would 632 // see in a stringified stack trace. 633 if (iter.activation() != &activation && activation.asyncStack() && 634 activation.asyncCallIsExplicit()) { 635 break; 636 } 637 638 // If there is no parent frame, we're done. 639 if (iter.done()) { 640 break; 641 } 642 643 if (dbg->observesFrame(iter)) { 644 if (iter.isIon() && !iter.ensureHasRematerializedFrame(cx)) { 645 return false; 646 } 647 return dbg->getFrame(cx, iter, result); 648 } 649 } 650 } else { 651 MOZ_ASSERT(frame->isSuspended()); 652 653 // If the frame is suspended, there is no older frame. 654 } 655 656 result.set(nullptr); 657 return true; 658 } 659 660 /* static */ 661 bool DebuggerFrame::getAsyncPromise(JSContext* cx, Handle<DebuggerFrame*> frame, 662 MutableHandle<DebuggerObject*> result) { 663 MOZ_ASSERT(frame->isOnStack() || frame->isSuspended()); 664 665 if (!frame->hasGeneratorInfo()) { 666 // An on-stack frame may not have an associated generator yet when the 667 // frame is initially entered. 668 result.set(nullptr); 669 return true; 670 } 671 672 RootedObject resultObject(cx); 673 AbstractGeneratorObject& generator = frame->unwrappedGenerator(); 674 if (generator.is<AsyncFunctionGeneratorObject>()) { 675 resultObject = generator.as<AsyncFunctionGeneratorObject>().promise(); 676 } else if (generator.is<AsyncGeneratorObject>()) { 677 Rooted<AsyncGeneratorObject*> asyncGen( 678 cx, &generator.as<AsyncGeneratorObject>()); 679 // In initial function execution, there is no promise. 680 if (!asyncGen->isQueueEmpty()) { 681 resultObject = AsyncGeneratorObject::peekRequest(asyncGen)->promise(); 682 } 683 } else { 684 MOZ_CRASH("Unknown async generator type"); 685 } 686 687 return frame->owner()->wrapNullableDebuggeeObject(cx, resultObject, result); 688 } 689 690 /* static */ 691 bool DebuggerFrame::getThis(JSContext* cx, Handle<DebuggerFrame*> frame, 692 MutableHandleValue result) { 693 Debugger* dbg = frame->owner(); 694 695 if (frame->isOnStack()) { 696 if (!requireScriptReferent(cx, frame)) { 697 return false; 698 } 699 FrameIter iter = frame->getFrameIter(cx); 700 701 { 702 AbstractFramePtr frame = iter.abstractFramePtr(); 703 AutoRealm ar(cx, frame.environmentChain()); 704 705 UpdateFrameIterPc(iter); 706 707 if (!GetThisValueForDebuggerFrameMaybeOptimizedOut(cx, frame, iter.pc(), 708 result)) { 709 return false; 710 } 711 } 712 } else { 713 MOZ_ASSERT(frame->isSuspended()); 714 715 AbstractGeneratorObject& genObj = 716 frame->generatorInfo()->unwrappedGenerator(); 717 AutoRealm ar(cx, &genObj); 718 JSScript* script = frame->generatorInfo()->generatorScript(); 719 720 if (!GetThisValueForDebuggerSuspendedGeneratorMaybeOptimizedOut( 721 cx, genObj, script, result)) { 722 return false; 723 } 724 } 725 726 return dbg->wrapDebuggeeValue(cx, result); 727 } 728 729 /* static */ 730 DebuggerFrameType DebuggerFrame::getType(Handle<DebuggerFrame*> frame) { 731 if (frame->isOnStack()) { 732 AbstractFramePtr referent = DebuggerFrame::getReferent(frame); 733 734 // Indirect eval frames are both isGlobalFrame() and isEvalFrame(), so the 735 // order of checks here is significant. 736 if (referent.isEvalFrame()) { 737 return DebuggerFrameType::Eval; 738 } 739 740 if (referent.isGlobalFrame()) { 741 return DebuggerFrameType::Global; 742 } 743 744 if (referent.isFunctionFrame()) { 745 return DebuggerFrameType::Call; 746 } 747 748 if (referent.isModuleFrame()) { 749 return DebuggerFrameType::Module; 750 } 751 752 if (referent.isWasmDebugFrame()) { 753 return DebuggerFrameType::WasmCall; 754 } 755 } else { 756 MOZ_ASSERT(frame->isSuspended()); 757 758 return DebuggerFrameType::Call; 759 } 760 761 MOZ_CRASH("Unknown frame type"); 762 } 763 764 /* static */ 765 DebuggerFrameImplementation DebuggerFrame::getImplementation( 766 Handle<DebuggerFrame*> frame) { 767 AbstractFramePtr referent = DebuggerFrame::getReferent(frame); 768 769 if (referent.isBaselineFrame()) { 770 return DebuggerFrameImplementation::Baseline; 771 } 772 773 if (referent.isRematerializedFrame()) { 774 return DebuggerFrameImplementation::Ion; 775 } 776 777 if (referent.isWasmDebugFrame()) { 778 return DebuggerFrameImplementation::Wasm; 779 } 780 781 return DebuggerFrameImplementation::Interpreter; 782 } 783 784 /* 785 * If succesful, transfers the ownership of the given `handler` to this 786 * Debugger.Frame. Note that on failure, the ownership of `handler` is not 787 * transferred, and the caller is responsible for cleaning it up. 788 */ 789 /* static */ 790 bool DebuggerFrame::setOnStepHandler(JSContext* cx, 791 Handle<DebuggerFrame*> frame, 792 UniquePtr<OnStepHandler> handlerArg) { 793 // Handler has never been successfully associated with the frame so allow 794 // UniquePtr to delete it rather than calling drop() if we return early from 795 // this method.. 796 Rooted<UniquePtr<OnStepHandler>> handler(cx, std::move(handlerArg)); 797 798 OnStepHandler* prior = frame->onStepHandler(); 799 if (handler.get() == prior) { 800 return true; 801 } 802 803 JS::GCContext* gcx = cx->gcContext(); 804 if (frame->isOnStack()) { 805 AbstractFramePtr referent = DebuggerFrame::getReferent(frame); 806 807 // Adjust execution observability and step counts on whatever code (JS or 808 // Wasm) this frame is running. 809 if (handler && !prior) { 810 if (!frame->incrementStepperCounter(cx, referent)) { 811 return false; 812 } 813 } else if (!handler && prior) { 814 frame->decrementStepperCounter(cx->gcContext(), referent); 815 } 816 } else if (frame->isSuspended()) { 817 RootedScript script(cx, frame->generatorInfo()->generatorScript()); 818 819 if (handler && !prior) { 820 if (!frame->incrementStepperCounter(cx, script)) { 821 return false; 822 } 823 } else if (!handler && prior) { 824 frame->decrementStepperCounter(cx->gcContext(), script); 825 } 826 } else { 827 // If the frame is entirely dead, we still allow setting the onStep 828 // handler, but it has no effect. 829 } 830 831 // Now that the stepper counts and observability are set correctly, we can 832 // actually switch the handler. 833 if (prior) { 834 prior->drop(gcx, frame); 835 } 836 837 if (handler) { 838 handler->hold(frame); 839 frame->setReservedSlot(ONSTEP_HANDLER_SLOT, 840 PrivateValue(handler.get().release())); 841 } else { 842 frame->setReservedSlot(ONSTEP_HANDLER_SLOT, UndefinedValue()); 843 } 844 845 return true; 846 } 847 848 bool DebuggerFrame::incrementStepperCounter(JSContext* cx, 849 AbstractFramePtr referent) { 850 if (!referent.isWasmDebugFrame()) { 851 RootedScript script(cx, referent.script()); 852 return incrementStepperCounter(cx, script); 853 } 854 855 wasm::Instance* instance = referent.asWasmDebugFrame()->instance(); 856 wasm::DebugFrame* wasmFrame = referent.asWasmDebugFrame(); 857 // Single stepping toggled off->on. 858 if (!instance->debug().incrementStepperCount(cx, instance, 859 wasmFrame->funcIndex())) { 860 return false; 861 } 862 863 return true; 864 } 865 866 bool DebuggerFrame::incrementStepperCounter(JSContext* cx, 867 HandleScript script) { 868 // Single stepping toggled off->on. 869 AutoRealm ar(cx, script); 870 // Ensure observability *before* incrementing the step mode count. 871 // Calling this function after calling incrementStepperCount 872 // will make it a no-op. 873 if (!Debugger::ensureExecutionObservabilityOfScript(cx, script)) { 874 return false; 875 } 876 if (!DebugScript::incrementStepperCount(cx, script)) { 877 return false; 878 } 879 880 return true; 881 } 882 883 void DebuggerFrame::decrementStepperCounter(JS::GCContext* gcx, 884 AbstractFramePtr referent) { 885 if (!referent.isWasmDebugFrame()) { 886 decrementStepperCounter(gcx, referent.script()); 887 return; 888 } 889 890 wasm::Instance* instance = referent.asWasmDebugFrame()->instance(); 891 wasm::DebugFrame* wasmFrame = referent.asWasmDebugFrame(); 892 // Single stepping toggled on->off. 893 instance->debug().decrementStepperCount(gcx, instance, 894 wasmFrame->funcIndex()); 895 } 896 897 void DebuggerFrame::decrementStepperCounter(JS::GCContext* gcx, 898 JSScript* script) { 899 // Single stepping toggled on->off. 900 DebugScript::decrementStepperCount(gcx, script); 901 } 902 903 /* static */ 904 bool DebuggerFrame::getArguments(JSContext* cx, Handle<DebuggerFrame*> frame, 905 MutableHandle<DebuggerArguments*> result) { 906 Value argumentsv = frame->getReservedSlot(ARGUMENTS_SLOT); 907 if (!argumentsv.isUndefined()) { 908 result.set(argumentsv.isObject() 909 ? &argumentsv.toObject().as<DebuggerArguments>() 910 : nullptr); 911 return true; 912 } 913 914 AbstractFramePtr referent = DebuggerFrame::getReferent(frame); 915 916 Rooted<DebuggerArguments*> arguments(cx); 917 if (referent.hasArgs()) { 918 Rooted<GlobalObject*> global(cx, &frame->global()); 919 RootedObject proto(cx, GlobalObject::getOrCreateArrayPrototype(cx, global)); 920 if (!proto) { 921 return false; 922 } 923 arguments = DebuggerArguments::create(cx, proto, frame); 924 if (!arguments) { 925 return false; 926 } 927 } else { 928 arguments = nullptr; 929 } 930 931 result.set(arguments); 932 frame->setReservedSlot(ARGUMENTS_SLOT, ObjectOrNullValue(result)); 933 return true; 934 } 935 936 static WithEnvironmentObject* CreateBindingsEnv( 937 JSContext* cx, JS::Handle<JSObject*> enclosingEnv, 938 JS::Handle<JS::StackGCVector<JS::PropertyKey>> bindingKeys, 939 JS::Handle<JS::StackGCVector<JS::Value>> bindingValues) { 940 JS::Rooted<PlainObject*> bindingsObj(cx, 941 NewPlainObjectWithProto(cx, nullptr)); 942 if (!bindingsObj) { 943 return nullptr; 944 } 945 JS::Rooted<JS::PropertyKey> id(cx); 946 JS::Rooted<JS::Value> val(cx); 947 for (size_t i = 0; i < bindingKeys.length(); i++) { 948 id = bindingKeys[i]; 949 cx->markId(id); 950 val = bindingValues[i]; 951 if (!cx->compartment()->wrap(cx, &val) || 952 !NativeDefineDataProperty(cx, bindingsObj, id, val, 0)) { 953 return nullptr; 954 } 955 } 956 957 JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No); 958 if (!envChain.append(bindingsObj)) { 959 return nullptr; 960 } 961 962 return CreateObjectsForEnvironmentChain(cx, envChain, enclosingEnv); 963 } 964 965 /* 966 * Evaluate |chars| in the environment |envArg|, treating that source as 967 * appearing starting at |evalOptions.lineno()| in |evalOptions.filename()|. 968 * Store the return value in |*rval|. 969 * 970 * If |evalOptions.kind()| is either `Frame` or |FrameWithExtraBindings|, 971 * evaluate as for a direct eval in |frame|; |envArg| must be |frame|'s 972 * DebugScopeObject; either way, 973 * |frame|'s scope is where newly declared variables go. In this case, 974 * |frame| must have a computed 'this' value. 975 * 976 * If |evalOptions.kind()| is one of the following, |bindingKeys| and 977 * |bindingValues| should represent the bindings, and a new 978 * |WithEnvironmentObject| is created with |envArg| as enclosing environment, 979 * and used for the evaluation. 980 * - FrameWithExtraBindings 981 * - GlobalWithExtraInnerBindings 982 * - GlobalWithExtraOuterBindings 983 */ 984 static bool EvaluateInEnv( 985 JSContext* cx, Handle<Env*> envArg, AbstractFramePtr frame, 986 mozilla::Range<const char16_t> chars, const EvalOptions& evalOptions, 987 JS::Handle<JS::StackGCVector<JS::PropertyKey>> bindingKeys, 988 JS::Handle<JS::StackGCVector<JS::Value>> bindingValues, 989 MutableHandleValue rval) { 990 #ifdef DEBUG 991 switch (evalOptions.kind()) { 992 case EvalOptions::EnvKind::Frame: 993 case EvalOptions::EnvKind::FrameWithExtraBindings: 994 MOZ_ASSERT(frame); 995 break; 996 case EvalOptions::EnvKind::Global: 997 MOZ_ASSERT(!frame); 998 break; 999 case EvalOptions::EnvKind::GlobalWithExtraInnerBindings: 1000 case EvalOptions::EnvKind::GlobalWithExtraOuterBindings: 1001 MOZ_ASSERT(!frame); 1002 break; 1003 } 1004 #endif 1005 1006 cx->check(envArg, frame); 1007 1008 AutoSetBypassCSPForDebugger setFlag(cx, evalOptions.bypassCSP()); 1009 1010 CompileOptions options(cx); 1011 const char* filename = 1012 evalOptions.filename() ? evalOptions.filename() : "debugger eval code"; 1013 options.setIsRunOnce(true) 1014 .setNoScriptRval(false) 1015 .setFileAndLine(filename, evalOptions.lineno()) 1016 .setHideScriptFromDebugger(evalOptions.hideFromDebugger()) 1017 .setIntroductionType("debugger eval") 1018 /* Do not perform the Javascript filename validation security check for 1019 * javascript executions sent through the debugger. Besides making up 1020 * a filename for these codepaths, we must allow arbitrary JS execution 1021 * for the Browser toolbox to function. */ 1022 .setSkipFilenameValidation(true) 1023 /* Don't lazy parse. We need full-parsing to correctly support bytecode 1024 * emission for private fields/methods. See EmitterScope::lookupPrivate. 1025 */ 1026 .setForceFullParse(); 1027 1028 if (frame && frame.hasScript() && frame.script()->strict()) { 1029 options.setForceStrictMode(); 1030 } 1031 1032 SourceText<char16_t> srcBuf; 1033 if (!srcBuf.init(cx, chars.begin().get(), chars.length(), 1034 SourceOwnership::Borrowed)) { 1035 return false; 1036 } 1037 1038 // Compile the script and compute the environment object. 1039 JS::Rooted<JSScript*> script(cx); 1040 JS::Rooted<JSObject*> env(cx); 1041 switch (evalOptions.kind()) { 1042 case EvalOptions::EnvKind::FrameWithExtraBindings: { 1043 env = CreateBindingsEnv(cx, envArg, bindingKeys, bindingValues); 1044 if (!env) { 1045 return false; 1046 } 1047 [[fallthrough]]; 1048 } 1049 case EvalOptions::EnvKind::Frame: { 1050 // Default to |envArg| when no extra bindings are used. 1051 if (!env) { 1052 env = envArg; 1053 } 1054 1055 options.setNonSyntacticScope(true); 1056 1057 Rooted<Scope*> scope( 1058 cx, GlobalScope::createEmpty(cx, ScopeKind::NonSyntactic)); 1059 if (!scope) { 1060 return false; 1061 } 1062 1063 script = frontend::CompileEvalScript(cx, options, srcBuf, scope, env); 1064 if (!script) { 1065 return false; 1066 } 1067 break; 1068 } 1069 case EvalOptions::EnvKind::Global: { 1070 AutoReportFrontendContext fc(cx); 1071 script = frontend::CompileGlobalScript(cx, &fc, options, srcBuf, 1072 ScopeKind::Global); 1073 if (!script) { 1074 return false; 1075 } 1076 1077 env = envArg; 1078 break; 1079 } 1080 case EvalOptions::EnvKind::GlobalWithExtraOuterBindings: { 1081 // Do not consider executeInGlobal{,WithBindings} as an eval, but instead 1082 // as executing a series of statements at the global level. This is to 1083 // circumvent the fresh lexical scope that all eval have, so that the 1084 // users of executeInGlobal{,WithBindings}, like the web console, may add 1085 // new bindings to the global scope. 1086 1087 MOZ_ASSERT(envArg == &cx->global()->lexicalEnvironment()); 1088 1089 options.setNonSyntacticScope(true); 1090 1091 AutoReportFrontendContext fc(cx); 1092 script = frontend::CompileGlobalScriptWithExtraBindings( 1093 cx, &fc, options, srcBuf, bindingKeys, bindingValues, &env); 1094 if (!script) { 1095 return false; 1096 } 1097 break; 1098 } 1099 case EvalOptions::EnvKind::GlobalWithExtraInnerBindings: { 1100 env = CreateBindingsEnv(cx, envArg, bindingKeys, bindingValues); 1101 if (!env) { 1102 return false; 1103 } 1104 1105 options.setNonSyntacticScope(true); 1106 1107 AutoReportFrontendContext fc(cx); 1108 script = frontend::CompileGlobalScript(cx, &fc, options, srcBuf, 1109 ScopeKind::NonSyntactic); 1110 if (!script) { 1111 return false; 1112 } 1113 break; 1114 } 1115 } 1116 1117 return ExecuteKernel(cx, script, env, frame, rval); 1118 } 1119 1120 Result<Completion> js::DebuggerGenericEval( 1121 JSContext* cx, const mozilla::Range<const char16_t> chars, 1122 HandleObject bindings, const EvalOptions& options, Debugger* dbg, 1123 HandleObject envArg, FrameIter* iter) { 1124 #ifdef DEBUG 1125 switch (options.kind()) { 1126 case EvalOptions::EnvKind::Frame: 1127 MOZ_ASSERT(iter); 1128 MOZ_ASSERT(!envArg); 1129 MOZ_ASSERT(!bindings); 1130 break; 1131 case EvalOptions::EnvKind::FrameWithExtraBindings: 1132 MOZ_ASSERT(iter); 1133 MOZ_ASSERT(!envArg); 1134 MOZ_ASSERT(bindings); 1135 break; 1136 case EvalOptions::EnvKind::Global: 1137 MOZ_ASSERT(!iter); 1138 MOZ_ASSERT(envArg); 1139 MOZ_ASSERT(envArg->is<GlobalLexicalEnvironmentObject>()); 1140 MOZ_ASSERT(!bindings); 1141 break; 1142 case EvalOptions::EnvKind::GlobalWithExtraInnerBindings: 1143 case EvalOptions::EnvKind::GlobalWithExtraOuterBindings: 1144 MOZ_ASSERT(!iter); 1145 MOZ_ASSERT(envArg); 1146 MOZ_ASSERT(envArg->is<GlobalLexicalEnvironmentObject>()); 1147 MOZ_ASSERT(bindings); 1148 break; 1149 } 1150 #endif 1151 1152 // Gather keys and values of bindings, if any. This must be done in the 1153 // debugger compartment, since that is where any exceptions must be thrown. 1154 RootedIdVector keys(cx); 1155 RootedValueVector values(cx); 1156 if (bindings) { 1157 if (!GetPropertyKeys(cx, bindings, JSITER_OWNONLY, &keys) || 1158 !values.growBy(keys.length())) { 1159 return cx->alreadyReportedError(); 1160 } 1161 for (size_t i = 0; i < keys.length(); i++) { 1162 MutableHandleValue valp = values[i]; 1163 if (!GetProperty(cx, bindings, bindings, keys[i], valp) || 1164 !dbg->unwrapDebuggeeValue(cx, valp)) { 1165 return cx->alreadyReportedError(); 1166 } 1167 } 1168 } 1169 1170 Maybe<AutoRealm> ar; 1171 if (iter) { 1172 ar.emplace(cx, iter->environmentChain(cx)); 1173 } else { 1174 ar.emplace(cx, envArg); 1175 } 1176 1177 Rooted<Env*> env(cx); 1178 if (iter) { 1179 env = GetDebugEnvironmentForFrame(cx, iter->abstractFramePtr(), iter->pc()); 1180 if (!env) { 1181 return cx->alreadyReportedError(); 1182 } 1183 } else { 1184 env = envArg; 1185 } 1186 1187 if (iter && iter->hasScript() && iter->script()->allowRelazify()) { 1188 // This is a function that was first lazy, and delazified, and it's marked 1189 // as relazifyable because there was no inner function. 1190 // 1191 // Evaluating a script can create inner function, which should prevent the 1192 // relazification. 1193 iter->script()->clearAllowRelazify(); 1194 } 1195 1196 // Note whether we are in an evaluation that might invoke the OnNativeCall 1197 // hook, so that the JITs will be disabled. 1198 Maybe<AutoNoteExclusiveDebuggerOnEval> noteEvaluation; 1199 if (dbg->isExclusiveDebuggerOnEval()) { 1200 noteEvaluation.emplace(cx, dbg); 1201 } 1202 1203 // Run the code and produce the completion value. 1204 LeaveDebuggeeNoExecute nnx(cx); 1205 RootedValue rval(cx); 1206 AbstractFramePtr frame = iter ? iter->abstractFramePtr() : NullFramePtr(); 1207 1208 bool ok = EvaluateInEnv(cx, env, frame, chars, options, keys, values, &rval); 1209 Rooted<Completion> completion(cx, Completion::fromJSResult(cx, ok, rval)); 1210 ar.reset(); 1211 return completion.get(); 1212 } 1213 1214 /* static */ 1215 Result<Completion> DebuggerFrame::eval(JSContext* cx, 1216 Handle<DebuggerFrame*> frame, 1217 mozilla::Range<const char16_t> chars, 1218 HandleObject bindings, 1219 const EvalOptions& options) { 1220 MOZ_ASSERT(options.kind() == EvalOptions::EnvKind::Frame || 1221 options.kind() == EvalOptions::EnvKind::FrameWithExtraBindings); 1222 MOZ_ASSERT(frame->isOnStack()); 1223 1224 Debugger* dbg = frame->owner(); 1225 FrameIter iter = frame->getFrameIter(cx); 1226 1227 UpdateFrameIterPc(iter); 1228 1229 return DebuggerGenericEval(cx, chars, bindings, options, dbg, nullptr, &iter); 1230 } 1231 1232 bool DebuggerFrame::isOnStack() const { 1233 // Note: this is equivalent to checking frameIterData() != nullptr. 1234 return !getFixedSlot(FRAME_ITER_SLOT).isUndefined(); 1235 } 1236 1237 bool DebuggerFrame::isOnStackOrSuspendedWasmStack() const { 1238 // Note: this is equivalent to checking `frameIterData() != nullptr && 1239 // !hasGeneratorInfo()`, but works also when called from the trace hook 1240 // during a moving GC. 1241 return !getFixedSlot(FRAME_ITER_SLOT).isUndefined() || 1242 getFixedSlot(GENERATOR_INFO_SLOT).isUndefined(); 1243 } 1244 1245 OnStepHandler* DebuggerFrame::onStepHandler() const { 1246 return maybePtrFromReservedSlot<OnStepHandler>(ONSTEP_HANDLER_SLOT); 1247 } 1248 1249 OnPopHandler* DebuggerFrame::onPopHandler() const { 1250 return maybePtrFromReservedSlot<OnPopHandler>(ONPOP_HANDLER_SLOT); 1251 } 1252 1253 void DebuggerFrame::setOnPopHandler(JSContext* cx, OnPopHandler* handler) { 1254 OnPopHandler* prior = onPopHandler(); 1255 if (handler == prior) { 1256 return; 1257 } 1258 1259 JS::GCContext* gcx = cx->gcContext(); 1260 1261 if (prior) { 1262 prior->drop(gcx, this); 1263 } 1264 1265 if (handler) { 1266 setReservedSlot(ONPOP_HANDLER_SLOT, PrivateValue(handler)); 1267 handler->hold(this); 1268 } else { 1269 setReservedSlot(ONPOP_HANDLER_SLOT, UndefinedValue()); 1270 } 1271 } 1272 1273 FrameIter::Data* DebuggerFrame::frameIterData() const { 1274 return maybePtrFromReservedSlot<FrameIter::Data>(FRAME_ITER_SLOT); 1275 } 1276 1277 /* static */ 1278 AbstractFramePtr DebuggerFrame::getReferent(Handle<DebuggerFrame*> frame) { 1279 FrameIter iter(*frame->frameIterData()); 1280 return iter.abstractFramePtr(); 1281 } 1282 1283 FrameIter DebuggerFrame::getFrameIter(JSContext* cx) { 1284 FrameIter::Data* data = frameIterData(); 1285 MOZ_ASSERT(data); 1286 MOZ_ASSERT(data->cx_ == cx); 1287 1288 return FrameIter(*data); 1289 } 1290 1291 /* static */ 1292 bool DebuggerFrame::requireScriptReferent(JSContext* cx, 1293 Handle<DebuggerFrame*> frame) { 1294 AbstractFramePtr referent = DebuggerFrame::getReferent(frame); 1295 if (!referent.hasScript()) { 1296 RootedValue frameobj(cx, ObjectValue(*frame)); 1297 ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK, frameobj, 1298 nullptr, "a script frame"); 1299 return false; 1300 } 1301 return true; 1302 } 1303 1304 void DebuggerFrame::setFrameIterData(FrameIter::Data* data) { 1305 MOZ_ASSERT(data); 1306 MOZ_ASSERT(!frameIterData()); 1307 InitReservedSlot(this, FRAME_ITER_SLOT, data, 1308 MemoryUse::DebuggerFrameIterData); 1309 } 1310 1311 void DebuggerFrame::freeFrameIterData(JS::GCContext* gcx) { 1312 if (FrameIter::Data* data = frameIterData()) { 1313 gcx->delete_(this, data, MemoryUse::DebuggerFrameIterData); 1314 setReservedSlot(FRAME_ITER_SLOT, UndefinedValue()); 1315 } 1316 } 1317 1318 bool DebuggerFrame::replaceFrameIterData(JSContext* cx, const FrameIter& iter) { 1319 FrameIter::Data* data = iter.copyData(); 1320 if (!data) { 1321 return false; 1322 } 1323 freeFrameIterData(cx->gcContext()); 1324 setFrameIterData(data); 1325 return true; 1326 } 1327 1328 /* static */ 1329 void DebuggerFrame::finalize(JS::GCContext* gcx, JSObject* obj) { 1330 MOZ_ASSERT(gcx->onMainThread()); 1331 1332 DebuggerFrame& frameobj = obj->as<DebuggerFrame>(); 1333 1334 // Connections between dying Debugger.Frames and their 1335 // AbstractGeneratorObjects, as well as the frame's stack data should have 1336 // been by a call to terminate() from sweepAll or some other place. 1337 MOZ_ASSERT(!frameobj.hasGeneratorInfo()); 1338 MOZ_ASSERT(!frameobj.frameIterData()); 1339 OnStepHandler* onStepHandler = frameobj.onStepHandler(); 1340 if (onStepHandler) { 1341 onStepHandler->drop(gcx, &frameobj); 1342 } 1343 OnPopHandler* onPopHandler = frameobj.onPopHandler(); 1344 if (onPopHandler) { 1345 onPopHandler->drop(gcx, &frameobj); 1346 } 1347 } 1348 1349 void DebuggerFrame::trace(JSTracer* trc) { 1350 OnStepHandler* onStepHandler = this->onStepHandler(); 1351 if (onStepHandler) { 1352 onStepHandler->trace(trc); 1353 } 1354 OnPopHandler* onPopHandler = this->onPopHandler(); 1355 if (onPopHandler) { 1356 onPopHandler->trace(trc); 1357 } 1358 1359 if (hasGeneratorInfo()) { 1360 generatorInfo()->trace(trc, *this); 1361 } 1362 } 1363 1364 /* static */ 1365 DebuggerFrame* DebuggerFrame::check(JSContext* cx, HandleValue thisv) { 1366 JSObject* thisobj = RequireObject(cx, thisv); 1367 if (!thisobj) { 1368 return nullptr; 1369 } 1370 if (!thisobj->is<DebuggerFrame>()) { 1371 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1372 JSMSG_INCOMPATIBLE_PROTO, "Debugger.Frame", 1373 "method", thisobj->getClass()->name); 1374 return nullptr; 1375 } 1376 1377 return &thisobj->as<DebuggerFrame>(); 1378 } 1379 1380 struct MOZ_STACK_CLASS DebuggerFrame::CallData { 1381 JSContext* cx; 1382 const CallArgs& args; 1383 1384 Handle<DebuggerFrame*> frame; 1385 1386 CallData(JSContext* cx, const CallArgs& args, Handle<DebuggerFrame*> frame) 1387 : cx(cx), args(args), frame(frame) {} 1388 1389 bool argumentsGetter(); 1390 bool calleeGetter(); 1391 bool constructingGetter(); 1392 bool environmentGetter(); 1393 bool generatorGetter(); 1394 bool asyncPromiseGetter(); 1395 bool olderSavedFrameGetter(); 1396 bool liveGetter(); 1397 bool onStackGetter(); 1398 bool terminatedGetter(); 1399 bool offsetGetter(); 1400 bool olderGetter(); 1401 bool getScript(); 1402 bool thisGetter(); 1403 bool typeGetter(); 1404 bool implementationGetter(); 1405 bool onStepGetter(); 1406 bool onStepSetter(); 1407 bool onPopGetter(); 1408 bool onPopSetter(); 1409 bool evalMethod(); 1410 bool evalWithBindingsMethod(); 1411 1412 using Method = bool (CallData::*)(); 1413 1414 template <Method MyMethod> 1415 static bool ToNative(JSContext* cx, unsigned argc, Value* vp); 1416 1417 bool ensureOnStack() const; 1418 bool ensureOnStackOrSuspended() const; 1419 }; 1420 1421 template <DebuggerFrame::CallData::Method MyMethod> 1422 /* static */ 1423 bool DebuggerFrame::CallData::ToNative(JSContext* cx, unsigned argc, 1424 Value* vp) { 1425 CallArgs args = CallArgsFromVp(argc, vp); 1426 1427 Rooted<DebuggerFrame*> frame(cx, DebuggerFrame::check(cx, args.thisv())); 1428 if (!frame) { 1429 return false; 1430 } 1431 1432 CallData data(cx, args, frame); 1433 return (data.*MyMethod)(); 1434 } 1435 1436 static bool EnsureOnStack(JSContext* cx, Handle<DebuggerFrame*> frame) { 1437 MOZ_ASSERT(frame); 1438 if (!frame->isOnStack()) { 1439 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1440 JSMSG_DEBUG_NOT_ON_STACK, "Debugger.Frame"); 1441 return false; 1442 } 1443 1444 return true; 1445 } 1446 static bool EnsureOnStackOrSuspended(JSContext* cx, 1447 Handle<DebuggerFrame*> frame) { 1448 MOZ_ASSERT(frame); 1449 if (!frame->isOnStack() && !frame->isSuspended()) { 1450 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1451 JSMSG_DEBUG_NOT_ON_STACK_OR_SUSPENDED, 1452 "Debugger.Frame"); 1453 return false; 1454 } 1455 1456 return true; 1457 } 1458 1459 bool DebuggerFrame::CallData::ensureOnStack() const { 1460 return EnsureOnStack(cx, frame); 1461 } 1462 bool DebuggerFrame::CallData::ensureOnStackOrSuspended() const { 1463 return EnsureOnStackOrSuspended(cx, frame); 1464 } 1465 1466 bool DebuggerFrame::CallData::typeGetter() { 1467 if (!ensureOnStackOrSuspended()) { 1468 return false; 1469 } 1470 1471 DebuggerFrameType type = DebuggerFrame::getType(frame); 1472 1473 JSString* str; 1474 switch (type) { 1475 case DebuggerFrameType::Eval: 1476 str = cx->names().eval; 1477 break; 1478 case DebuggerFrameType::Global: 1479 str = cx->names().global; 1480 break; 1481 case DebuggerFrameType::Call: 1482 str = cx->names().call; 1483 break; 1484 case DebuggerFrameType::Module: 1485 str = cx->names().module; 1486 break; 1487 case DebuggerFrameType::WasmCall: 1488 str = cx->names().wasmcall; 1489 break; 1490 default: 1491 MOZ_CRASH("bad DebuggerFrameType value"); 1492 } 1493 1494 args.rval().setString(str); 1495 return true; 1496 } 1497 1498 bool DebuggerFrame::CallData::implementationGetter() { 1499 if (!ensureOnStack()) { 1500 return false; 1501 } 1502 1503 DebuggerFrameImplementation implementation = 1504 DebuggerFrame::getImplementation(frame); 1505 1506 const char* s; 1507 switch (implementation) { 1508 case DebuggerFrameImplementation::Baseline: 1509 s = "baseline"; 1510 break; 1511 case DebuggerFrameImplementation::Ion: 1512 s = "ion"; 1513 break; 1514 case DebuggerFrameImplementation::Interpreter: 1515 s = "interpreter"; 1516 break; 1517 case DebuggerFrameImplementation::Wasm: 1518 s = "wasm"; 1519 break; 1520 default: 1521 MOZ_CRASH("bad DebuggerFrameImplementation value"); 1522 } 1523 1524 JSAtom* str = Atomize(cx, s, strlen(s)); 1525 if (!str) { 1526 return false; 1527 } 1528 1529 args.rval().setString(str); 1530 return true; 1531 } 1532 1533 bool DebuggerFrame::CallData::environmentGetter() { 1534 if (!ensureOnStackOrSuspended()) { 1535 return false; 1536 } 1537 1538 Rooted<DebuggerEnvironment*> result(cx); 1539 if (!DebuggerFrame::getEnvironment(cx, frame, &result)) { 1540 return false; 1541 } 1542 1543 args.rval().setObject(*result); 1544 return true; 1545 } 1546 1547 bool DebuggerFrame::CallData::calleeGetter() { 1548 if (!ensureOnStackOrSuspended()) { 1549 return false; 1550 } 1551 1552 Rooted<DebuggerObject*> result(cx); 1553 if (!DebuggerFrame::getCallee(cx, frame, &result)) { 1554 return false; 1555 } 1556 1557 args.rval().setObjectOrNull(result); 1558 return true; 1559 } 1560 1561 bool DebuggerFrame::CallData::generatorGetter() { 1562 JS_ReportErrorASCII(cx, 1563 "Debugger.Frame.prototype.generator has been removed. " 1564 "Use frame.script.isGeneratorFunction instead."); 1565 return false; 1566 } 1567 1568 bool DebuggerFrame::CallData::constructingGetter() { 1569 if (!ensureOnStackOrSuspended()) { 1570 return false; 1571 } 1572 1573 bool result; 1574 if (!DebuggerFrame::getIsConstructing(cx, frame, result)) { 1575 return false; 1576 } 1577 1578 args.rval().setBoolean(result); 1579 return true; 1580 } 1581 1582 bool DebuggerFrame::CallData::asyncPromiseGetter() { 1583 if (!ensureOnStackOrSuspended()) { 1584 return false; 1585 } 1586 1587 RootedScript script(cx); 1588 if (frame->isOnStack()) { 1589 FrameIter iter = frame->getFrameIter(cx); 1590 AbstractFramePtr framePtr = iter.abstractFramePtr(); 1591 1592 if (!framePtr.isWasmDebugFrame()) { 1593 script = framePtr.script(); 1594 } 1595 } else { 1596 MOZ_ASSERT(frame->isSuspended()); 1597 script = frame->generatorInfo()->generatorScript(); 1598 } 1599 // The async promise value is only provided for async functions and 1600 // async generator functions. 1601 if (!script || !script->isAsync()) { 1602 args.rval().setUndefined(); 1603 return true; 1604 } 1605 1606 Rooted<DebuggerObject*> result(cx); 1607 if (!DebuggerFrame::getAsyncPromise(cx, frame, &result)) { 1608 return false; 1609 } 1610 1611 args.rval().setObjectOrNull(result); 1612 return true; 1613 } 1614 1615 bool DebuggerFrame::CallData::olderSavedFrameGetter() { 1616 if (!ensureOnStackOrSuspended()) { 1617 return false; 1618 } 1619 1620 Rooted<SavedFrame*> result(cx); 1621 if (!DebuggerFrame::getOlderSavedFrame(cx, frame, &result)) { 1622 return false; 1623 } 1624 1625 args.rval().setObjectOrNull(result); 1626 return true; 1627 } 1628 1629 /* static */ 1630 bool DebuggerFrame::getOlderSavedFrame(JSContext* cx, 1631 Handle<DebuggerFrame*> frame, 1632 MutableHandle<SavedFrame*> result) { 1633 if (frame->isOnStack()) { 1634 Debugger* dbg = frame->owner(); 1635 FrameIter iter = frame->getFrameIter(cx); 1636 1637 while (true) { 1638 Activation& activation = *iter.activation(); 1639 ++iter; 1640 1641 // If the parent frame crosses an explicit async stack boundary, or we 1642 // have hit the end of the synchronous frames, we want to switch over 1643 // to using SavedFrames. 1644 if (iter.activation() != &activation && activation.asyncStack() && 1645 (activation.asyncCallIsExplicit() || iter.done())) { 1646 const char* cause = activation.asyncCause(); 1647 Rooted<JSAtom*> causeAtom(cx, 1648 AtomizeUTF8Chars(cx, cause, strlen(cause))); 1649 if (!causeAtom) { 1650 return false; 1651 } 1652 Rooted<SavedFrame*> stackObj(cx, activation.asyncStack()); 1653 1654 return cx->realm()->savedStacks().copyAsyncStack( 1655 cx, stackObj, causeAtom, result, mozilla::Nothing()); 1656 } 1657 1658 // If there are no more parent frames, we're done. 1659 if (iter.done()) { 1660 break; 1661 } 1662 1663 // If we hit another frame that we observe, then there is no saved 1664 // frame that we'd want to return. 1665 if (dbg->observesFrame(iter)) { 1666 break; 1667 } 1668 } 1669 } else { 1670 MOZ_ASSERT(frame->isSuspended()); 1671 } 1672 1673 result.set(nullptr); 1674 return true; 1675 } 1676 1677 bool DebuggerFrame::CallData::thisGetter() { 1678 if (!ensureOnStackOrSuspended()) { 1679 return false; 1680 } 1681 1682 return DebuggerFrame::getThis(cx, frame, args.rval()); 1683 } 1684 1685 bool DebuggerFrame::CallData::olderGetter() { 1686 if (!ensureOnStackOrSuspended()) { 1687 return false; 1688 } 1689 1690 Rooted<DebuggerFrame*> result(cx); 1691 if (!DebuggerFrame::getOlder(cx, frame, &result)) { 1692 return false; 1693 } 1694 1695 args.rval().setObjectOrNull(result); 1696 return true; 1697 } 1698 1699 // The getter used for each element of frame.arguments. 1700 // See DebuggerFrame::getArguments. 1701 static bool DebuggerArguments_getArg(JSContext* cx, unsigned argc, Value* vp) { 1702 CallArgs args = CallArgsFromVp(argc, vp); 1703 int32_t i = args.callee().as<JSFunction>().getExtendedSlot(0).toInt32(); 1704 1705 // Check that the this value is an Arguments object. 1706 RootedObject argsobj(cx, RequireObject(cx, args.thisv())); 1707 if (!argsobj) { 1708 return false; 1709 } 1710 if (argsobj->getClass() != &DebuggerArguments::class_) { 1711 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1712 JSMSG_INCOMPATIBLE_PROTO, "Arguments", 1713 "getArgument", argsobj->getClass()->name); 1714 return false; 1715 } 1716 1717 RootedValue framev(cx, argsobj->as<NativeObject>().getReservedSlot( 1718 JSSLOT_DEBUGARGUMENTS_FRAME)); 1719 Rooted<DebuggerFrame*> thisobj(cx, DebuggerFrame::check(cx, framev)); 1720 if (!thisobj || !EnsureOnStack(cx, thisobj)) { 1721 return false; 1722 } 1723 1724 FrameIter iter = thisobj->getFrameIter(cx); 1725 AbstractFramePtr frame = iter.abstractFramePtr(); 1726 1727 // TODO handle wasm frame arguments -- they are not yet reflectable. 1728 MOZ_ASSERT(!frame.isWasmDebugFrame(), "a wasm frame args"); 1729 1730 // Since getters can be extracted and applied to other objects, 1731 // there is no guarantee this object has an ith argument. 1732 MOZ_ASSERT(i >= 0); 1733 RootedValue arg(cx); 1734 RootedScript script(cx); 1735 if (unsigned(i) < frame.numActualArgs()) { 1736 script = frame.script(); 1737 if (unsigned(i) < frame.numFormalArgs()) { 1738 for (PositionalFormalParameterIter fi(script); fi; fi++) { 1739 if (fi.argumentSlot() == unsigned(i)) { 1740 // We might've been called before the CallObject was created or 1741 // initialized in the prologue. 1742 if (fi.closedOver() && frame.hasInitialEnvironment() && 1743 iter.pc() >= script->main()) { 1744 arg = frame.callObj().aliasedBinding(fi); 1745 } else { 1746 arg = frame.unaliasedActual(i, DONT_CHECK_ALIASING); 1747 } 1748 break; 1749 } 1750 } 1751 } else if (script->argsObjAliasesFormals() && frame.hasArgsObj()) { 1752 arg = frame.argsObj().arg(i); 1753 } else { 1754 arg = frame.unaliasedActual(i, DONT_CHECK_ALIASING); 1755 } 1756 } else { 1757 arg.setUndefined(); 1758 } 1759 1760 if (!thisobj->owner()->wrapDebuggeeValue(cx, &arg)) { 1761 return false; 1762 } 1763 args.rval().set(arg); 1764 return true; 1765 } 1766 1767 /* static */ 1768 DebuggerArguments* DebuggerArguments::create(JSContext* cx, HandleObject proto, 1769 Handle<DebuggerFrame*> frame) { 1770 AbstractFramePtr referent = DebuggerFrame::getReferent(frame); 1771 1772 Rooted<DebuggerArguments*> obj( 1773 cx, NewObjectWithGivenProto<DebuggerArguments>(cx, proto)); 1774 if (!obj) { 1775 return nullptr; 1776 } 1777 1778 JS::SetReservedSlot(obj, FRAME_SLOT, ObjectValue(*frame)); 1779 1780 MOZ_ASSERT(referent.numActualArgs() <= 0x7fffffff); 1781 unsigned fargc = referent.numActualArgs(); 1782 RootedValue fargcVal(cx, Int32Value(fargc)); 1783 if (!NativeDefineDataProperty(cx, obj, cx->names().length, fargcVal, 1784 JSPROP_PERMANENT | JSPROP_READONLY)) { 1785 return nullptr; 1786 } 1787 1788 Rooted<jsid> id(cx); 1789 for (unsigned i = 0; i < fargc; i++) { 1790 RootedFunction getobj(cx); 1791 getobj = NewNativeFunction(cx, DebuggerArguments_getArg, 0, nullptr, 1792 gc::AllocKind::FUNCTION_EXTENDED); 1793 if (!getobj) { 1794 return nullptr; 1795 } 1796 id = PropertyKey::Int(i); 1797 if (!NativeDefineAccessorProperty(cx, obj, id, getobj, nullptr, 1798 JSPROP_ENUMERATE)) { 1799 return nullptr; 1800 } 1801 getobj->setExtendedSlot(0, Int32Value(i)); 1802 } 1803 1804 return obj; 1805 } 1806 1807 bool DebuggerFrame::CallData::argumentsGetter() { 1808 if (!ensureOnStack()) { 1809 return false; 1810 } 1811 1812 Rooted<DebuggerArguments*> result(cx); 1813 if (!DebuggerFrame::getArguments(cx, frame, &result)) { 1814 return false; 1815 } 1816 1817 args.rval().setObjectOrNull(result); 1818 return true; 1819 } 1820 1821 bool DebuggerFrame::CallData::getScript() { 1822 if (!ensureOnStackOrSuspended()) { 1823 return false; 1824 } 1825 1826 Rooted<DebuggerScript*> scriptObject(cx); 1827 1828 Debugger* debug = frame->owner(); 1829 if (frame->isOnStack()) { 1830 FrameIter iter = frame->getFrameIter(cx); 1831 AbstractFramePtr framePtr = iter.abstractFramePtr(); 1832 1833 if (framePtr.isWasmDebugFrame()) { 1834 Rooted<WasmInstanceObject*> instance(cx, 1835 framePtr.wasmInstance()->object()); 1836 scriptObject = debug->wrapWasmScript(cx, instance); 1837 } else { 1838 RootedScript script(cx, framePtr.script()); 1839 scriptObject = debug->wrapScript(cx, script); 1840 } 1841 } else { 1842 MOZ_ASSERT(frame->isSuspended()); 1843 RootedScript script(cx, frame->generatorInfo()->generatorScript()); 1844 scriptObject = debug->wrapScript(cx, script); 1845 } 1846 if (!scriptObject) { 1847 return false; 1848 } 1849 1850 args.rval().setObject(*scriptObject); 1851 return true; 1852 } 1853 1854 bool DebuggerFrame::CallData::offsetGetter() { 1855 if (!ensureOnStackOrSuspended()) { 1856 return false; 1857 } 1858 1859 size_t result; 1860 if (!DebuggerFrame::getOffset(cx, frame, result)) { 1861 return false; 1862 } 1863 1864 args.rval().setNumber(double(result)); 1865 return true; 1866 } 1867 1868 bool DebuggerFrame::CallData::liveGetter() { 1869 JS_ReportErrorASCII( 1870 cx, "Debugger.Frame.prototype.live has been renamed to .onStack"); 1871 return false; 1872 } 1873 1874 bool DebuggerFrame::CallData::onStackGetter() { 1875 args.rval().setBoolean(frame->isOnStack()); 1876 return true; 1877 } 1878 1879 bool DebuggerFrame::CallData::terminatedGetter() { 1880 args.rval().setBoolean(!frame->isOnStack() && !frame->isSuspended()); 1881 return true; 1882 } 1883 1884 static bool IsValidHook(const Value& v) { 1885 return v.isUndefined() || (v.isObject() && v.toObject().isCallable()); 1886 } 1887 1888 bool DebuggerFrame::CallData::onStepGetter() { 1889 OnStepHandler* handler = frame->onStepHandler(); 1890 RootedValue value( 1891 cx, handler ? ObjectOrNullValue(handler->object()) : UndefinedValue()); 1892 MOZ_ASSERT(IsValidHook(value)); 1893 args.rval().set(value); 1894 return true; 1895 } 1896 1897 bool DebuggerFrame::CallData::onStepSetter() { 1898 if (!args.requireAtLeast(cx, "Debugger.Frame.set onStep", 1)) { 1899 return false; 1900 } 1901 if (!IsValidHook(args[0])) { 1902 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1903 JSMSG_NOT_CALLABLE_OR_UNDEFINED); 1904 return false; 1905 } 1906 1907 UniquePtr<ScriptedOnStepHandler> handler; 1908 if (!args[0].isUndefined()) { 1909 handler = cx->make_unique<ScriptedOnStepHandler>(&args[0].toObject()); 1910 if (!handler) { 1911 return false; 1912 } 1913 } 1914 1915 if (!DebuggerFrame::setOnStepHandler(cx, frame, std::move(handler))) { 1916 return false; 1917 } 1918 1919 args.rval().setUndefined(); 1920 return true; 1921 } 1922 1923 bool DebuggerFrame::CallData::onPopGetter() { 1924 OnPopHandler* handler = frame->onPopHandler(); 1925 RootedValue value( 1926 cx, handler ? ObjectValue(*handler->object()) : UndefinedValue()); 1927 MOZ_ASSERT(IsValidHook(value)); 1928 args.rval().set(value); 1929 return true; 1930 } 1931 1932 bool DebuggerFrame::CallData::onPopSetter() { 1933 if (!args.requireAtLeast(cx, "Debugger.Frame.set onPop", 1)) { 1934 return false; 1935 } 1936 if (!IsValidHook(args[0])) { 1937 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1938 JSMSG_NOT_CALLABLE_OR_UNDEFINED); 1939 return false; 1940 } 1941 1942 ScriptedOnPopHandler* handler = nullptr; 1943 if (!args[0].isUndefined()) { 1944 handler = cx->new_<ScriptedOnPopHandler>(&args[0].toObject()); 1945 if (!handler) { 1946 return false; 1947 } 1948 } 1949 1950 frame->setOnPopHandler(cx, handler); 1951 1952 args.rval().setUndefined(); 1953 return true; 1954 } 1955 1956 bool DebuggerFrame::CallData::evalMethod() { 1957 if (!ensureOnStack()) { 1958 return false; 1959 } 1960 1961 if (!args.requireAtLeast(cx, "Debugger.Frame.prototype.eval", 1)) { 1962 return false; 1963 } 1964 1965 AutoStableStringChars stableChars(cx); 1966 if (!ValueToStableChars(cx, "Debugger.Frame.prototype.eval", args[0], 1967 stableChars)) { 1968 return false; 1969 } 1970 mozilla::Range<const char16_t> chars = stableChars.twoByteRange(); 1971 1972 EvalOptions options(EvalOptions::EnvKind::Frame); 1973 if (!ParseEvalOptions(cx, args.get(1), options)) { 1974 return false; 1975 } 1976 1977 Rooted<Completion> comp(cx); 1978 JS_TRY_VAR_OR_RETURN_FALSE( 1979 cx, comp, DebuggerFrame::eval(cx, frame, chars, nullptr, options)); 1980 return comp.get().buildCompletionValue(cx, frame->owner(), args.rval()); 1981 } 1982 1983 bool DebuggerFrame::CallData::evalWithBindingsMethod() { 1984 if (!ensureOnStack()) { 1985 return false; 1986 } 1987 1988 if (!args.requireAtLeast(cx, "Debugger.Frame.prototype.evalWithBindings", 1989 2)) { 1990 return false; 1991 } 1992 1993 AutoStableStringChars stableChars(cx); 1994 if (!ValueToStableChars(cx, "Debugger.Frame.prototype.evalWithBindings", 1995 args[0], stableChars)) { 1996 return false; 1997 } 1998 mozilla::Range<const char16_t> chars = stableChars.twoByteRange(); 1999 2000 RootedObject bindings(cx, RequireObject(cx, args[1])); 2001 if (!bindings) { 2002 return false; 2003 } 2004 2005 EvalOptions options(EvalOptions::EnvKind::FrameWithExtraBindings); 2006 if (!ParseEvalOptions(cx, args.get(2), options)) { 2007 return false; 2008 } 2009 2010 Rooted<Completion> comp(cx); 2011 JS_TRY_VAR_OR_RETURN_FALSE( 2012 cx, comp, DebuggerFrame::eval(cx, frame, chars, bindings, options)); 2013 return comp.get().buildCompletionValue(cx, frame->owner(), args.rval()); 2014 } 2015 2016 /* static */ 2017 bool DebuggerFrame::construct(JSContext* cx, unsigned argc, Value* vp) { 2018 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR, 2019 "Debugger.Frame"); 2020 return false; 2021 } 2022 2023 const JSPropertySpec DebuggerFrame::properties_[] = { 2024 JS_DEBUG_PSG("arguments", argumentsGetter), 2025 JS_DEBUG_PSG("callee", calleeGetter), 2026 JS_DEBUG_PSG("constructing", constructingGetter), 2027 JS_DEBUG_PSG("environment", environmentGetter), 2028 JS_DEBUG_PSG("generator", generatorGetter), 2029 JS_DEBUG_PSG("live", liveGetter), 2030 JS_DEBUG_PSG("onStack", onStackGetter), 2031 JS_DEBUG_PSG("terminated", terminatedGetter), 2032 JS_DEBUG_PSG("offset", offsetGetter), 2033 JS_DEBUG_PSG("older", olderGetter), 2034 JS_DEBUG_PSG("olderSavedFrame", olderSavedFrameGetter), 2035 JS_DEBUG_PSG("script", getScript), 2036 JS_DEBUG_PSG("this", thisGetter), 2037 JS_DEBUG_PSG("asyncPromise", asyncPromiseGetter), 2038 JS_DEBUG_PSG("type", typeGetter), 2039 JS_DEBUG_PSG("implementation", implementationGetter), 2040 JS_DEBUG_PSGS("onStep", onStepGetter, onStepSetter), 2041 JS_DEBUG_PSGS("onPop", onPopGetter, onPopSetter), 2042 JS_PS_END, 2043 }; 2044 2045 const JSFunctionSpec DebuggerFrame::methods_[] = { 2046 JS_DEBUG_FN("eval", evalMethod, 1), 2047 JS_DEBUG_FN("evalWithBindings", evalWithBindingsMethod, 1), 2048 JS_FS_END, 2049 }; 2050 2051 JSObject* js::IdVectorToArray(JSContext* cx, HandleIdVector ids) { 2052 if (MOZ_UNLIKELY(ids.length() > UINT32_MAX)) { 2053 ReportAllocationOverflow(cx); 2054 return nullptr; 2055 } 2056 2057 Rooted<ArrayObject*> arr(cx, NewDenseFullyAllocatedArray(cx, ids.length())); 2058 if (!arr) { 2059 return nullptr; 2060 } 2061 2062 arr->ensureDenseInitializedLength(0, ids.length()); 2063 2064 for (size_t i = 0, len = ids.length(); i < len; i++) { 2065 jsid id = ids[i]; 2066 Value v; 2067 if (id.isInt()) { 2068 JSString* str = Int32ToString<CanGC>(cx, id.toInt()); 2069 if (!str) { 2070 return nullptr; 2071 } 2072 v = StringValue(str); 2073 } else if (id.isAtom()) { 2074 v = StringValue(id.toAtom()); 2075 } else if (id.isSymbol()) { 2076 v = SymbolValue(id.toSymbol()); 2077 } else { 2078 MOZ_CRASH("IdVector must contain only string, int, and Symbol jsids"); 2079 } 2080 2081 arr->initDenseElement(i, v); 2082 } 2083 2084 return arr; 2085 }