Backed out changeset 4191b252db9b (bug 1886734) for causing build bustages @netwerk...
[gecko.git] / js / src / debugger / Object.cpp
blob17528b0fd99ce8274e702746ff5674de4c3d4f2d
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/. */
7 #include "debugger/Object-inl.h"
9 #include "mozilla/Maybe.h" // for Maybe, Nothing, Some
10 #include "mozilla/Range.h" // for Range
11 #include "mozilla/Result.h" // for Result
12 #include "mozilla/Vector.h" // for Vector
14 #include <algorithm>
15 #include <string.h> // for size_t, strlen
16 #include <type_traits> // for remove_reference<>::type
17 #include <utility> // for move
19 #include "jsapi.h" // for CallArgs, RootedObject, Rooted
21 #include "builtin/Array.h" // for NewDenseCopiedArray
22 #include "builtin/Promise.h" // for PromiseReactionRecordBuilder
23 #include "debugger/Debugger.h" // for Completion, Debugger
24 #include "debugger/Frame.h" // for DebuggerFrame
25 #include "debugger/NoExecute.h" // for LeaveDebuggeeNoExecute
26 #include "debugger/Script.h" // for DebuggerScript
27 #include "debugger/Source.h" // for DebuggerSource
28 #include "gc/Tracer.h" // for TraceManuallyBarrieredCrossCompartmentEdge
29 #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
30 #include "js/CompilationAndEvaluation.h" // for Compile
31 #include "js/Conversions.h" // for ToObject
32 #include "js/experimental/JitInfo.h" // for JSJitInfo
33 #include "js/friend/ErrorMessages.h" // for GetErrorMessage, JSMSG_*
34 #include "js/friend/WindowProxy.h" // for IsWindow, IsWindowProxy, ToWindowIfWindowProxy
35 #include "js/HeapAPI.h" // for IsInsideNursery
36 #include "js/Promise.h" // for PromiseState
37 #include "js/PropertyAndElement.h" // for JS_GetProperty
38 #include "js/Proxy.h" // for PropertyDescriptor
39 #include "js/SourceText.h" // for SourceText
40 #include "js/StableStringChars.h" // for AutoStableStringChars
41 #include "js/String.h" // for JS::StringHasLatin1Chars
42 #include "proxy/ScriptedProxyHandler.h" // for ScriptedProxyHandler
43 #include "vm/ArgumentsObject.h" // for ARGS_LENGTH_MAX
44 #include "vm/ArrayObject.h" // for ArrayObject
45 #include "vm/AsyncFunction.h" // for AsyncGeneratorObject
46 #include "vm/AsyncIteration.h" // for AsyncFunctionGeneratorObject
47 #include "vm/BoundFunctionObject.h" // for BoundFunctionObject
48 #include "vm/BytecodeUtil.h" // for JSDVG_SEARCH_STACK
49 #include "vm/Compartment.h" // for Compartment
50 #include "vm/EnvironmentObject.h" // for GetDebugEnvironmentForFunction
51 #include "vm/ErrorObject.h" // for JSObject::is, ErrorObject
52 #include "vm/GeneratorObject.h" // for AbstractGeneratorObject
53 #include "vm/GlobalObject.h" // for JSObject::is, GlobalObject
54 #include "vm/Interpreter.h" // for Call
55 #include "vm/JSAtomUtils.h" // for Atomize, AtomizeString
56 #include "vm/JSContext.h" // for JSContext, ReportValueError
57 #include "vm/JSFunction.h" // for JSFunction
58 #include "vm/JSObject.h" // for GenericObject, NewObjectKind
59 #include "vm/JSScript.h" // for JSScript
60 #include "vm/NativeObject.h" // for NativeObject, JSObject::is
61 #include "vm/ObjectOperations.h" // for DefineProperty
62 #include "vm/PlainObject.h" // for js::PlainObject
63 #include "vm/PromiseObject.h" // for js::PromiseObject
64 #include "vm/Realm.h" // for AutoRealm, ErrorCopier, Realm
65 #include "vm/Runtime.h" // for JSAtomState
66 #include "vm/SavedFrame.h" // for SavedFrame
67 #include "vm/Scope.h" // for PositionalFormalParameterIter
68 #include "vm/SelfHosting.h" // for GetClonedSelfHostedFunctionName
69 #include "vm/Shape.h" // for Shape
70 #include "vm/Stack.h" // for InvokeArgs
71 #include "vm/StringType.h" // for JSAtom, PropertyName
72 #include "vm/WrapperObject.h" // for JSObject::is, WrapperObject
74 #include "gc/StableCellHasher-inl.h"
75 #include "vm/Compartment-inl.h" // for Compartment::wrap
76 #include "vm/JSObject-inl.h" // for GetObjectClassName, InitClass, NewObjectWithGivenProtoAndKind, ToPropertyKey
77 #include "vm/NativeObject-inl.h" // for NativeObject::global
78 #include "vm/ObjectOperations-inl.h" // for DeleteProperty, GetProperty
79 #include "vm/Realm-inl.h" // for AutoRealm::AutoRealm
81 using namespace js;
83 using JS::AutoStableStringChars;
84 using mozilla::Maybe;
85 using mozilla::Nothing;
86 using mozilla::Some;
88 const JSClassOps DebuggerObject::classOps_ = {
89 nullptr, // addProperty
90 nullptr, // delProperty
91 nullptr, // enumerate
92 nullptr, // newEnumerate
93 nullptr, // resolve
94 nullptr, // mayResolve
95 nullptr, // finalize
96 nullptr, // call
97 nullptr, // construct
98 CallTraceMethod<DebuggerObject>, // trace
101 const JSClass DebuggerObject::class_ = {
102 "Object", JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS), &classOps_};
104 void DebuggerObject::trace(JSTracer* trc) {
105 // There is a barrier on private pointers, so the Unbarriered marking
106 // is okay.
107 if (JSObject* referent = maybeReferent()) {
108 TraceManuallyBarrieredCrossCompartmentEdge(trc, this, &referent,
109 "Debugger.Object referent");
110 if (referent != maybeReferent()) {
111 setReservedSlotGCThingAsPrivateUnbarriered(OBJECT_SLOT, referent);
116 static DebuggerObject* DebuggerObject_checkThis(JSContext* cx,
117 const CallArgs& args) {
118 JSObject* thisobj = RequireObject(cx, args.thisv());
119 if (!thisobj) {
120 return nullptr;
122 if (!thisobj->is<DebuggerObject>()) {
123 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
124 JSMSG_INCOMPATIBLE_PROTO, "Debugger.Object",
125 "method", thisobj->getClass()->name);
126 return nullptr;
129 return &thisobj->as<DebuggerObject>();
132 /* static */
133 bool DebuggerObject::construct(JSContext* cx, unsigned argc, Value* vp) {
134 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
135 "Debugger.Object");
136 return false;
139 struct MOZ_STACK_CLASS DebuggerObject::CallData {
140 JSContext* cx;
141 const CallArgs& args;
143 Handle<DebuggerObject*> object;
144 RootedObject referent;
146 CallData(JSContext* cx, const CallArgs& args, Handle<DebuggerObject*> obj)
147 : cx(cx), args(args), object(obj), referent(cx, obj->referent()) {}
149 // JSNative properties
150 bool callableGetter();
151 bool isBoundFunctionGetter();
152 bool isArrowFunctionGetter();
153 bool isAsyncFunctionGetter();
154 bool isClassConstructorGetter();
155 bool isGeneratorFunctionGetter();
156 bool protoGetter();
157 bool classGetter();
158 bool nameGetter();
159 bool displayNameGetter();
160 bool parameterNamesGetter();
161 bool scriptGetter();
162 bool environmentGetter();
163 bool boundTargetFunctionGetter();
164 bool boundThisGetter();
165 bool boundArgumentsGetter();
166 bool allocationSiteGetter();
167 bool isErrorGetter();
168 bool errorMessageNameGetter();
169 bool errorNotesGetter();
170 bool errorLineNumberGetter();
171 bool errorColumnNumberGetter();
172 bool isProxyGetter();
173 bool proxyTargetGetter();
174 bool proxyHandlerGetter();
175 bool isPromiseGetter();
176 bool promiseStateGetter();
177 bool promiseValueGetter();
178 bool promiseReasonGetter();
179 bool promiseLifetimeGetter();
180 bool promiseTimeToResolutionGetter();
181 bool promiseAllocationSiteGetter();
182 bool promiseResolutionSiteGetter();
183 bool promiseIDGetter();
184 bool promiseDependentPromisesGetter();
186 // JSNative methods
187 bool isExtensibleMethod();
188 bool isSealedMethod();
189 bool isFrozenMethod();
190 bool getPropertyMethod();
191 bool setPropertyMethod();
192 bool getOwnPropertyNamesMethod();
193 bool getOwnPropertyNamesLengthMethod();
194 bool getOwnPropertySymbolsMethod();
195 bool getOwnPrivatePropertiesMethod();
196 bool getOwnPropertyDescriptorMethod();
197 bool preventExtensionsMethod();
198 bool sealMethod();
199 bool freezeMethod();
200 bool definePropertyMethod();
201 bool definePropertiesMethod();
202 bool deletePropertyMethod();
203 bool callMethod();
204 bool applyMethod();
205 bool asEnvironmentMethod();
206 bool forceLexicalInitializationByNameMethod();
207 bool executeInGlobalMethod();
208 bool executeInGlobalWithBindingsMethod();
209 bool createSource();
210 bool makeDebuggeeValueMethod();
211 bool isSameNativeMethod();
212 bool isSameNativeWithJitInfoMethod();
213 bool isNativeGetterWithJitInfo();
214 bool unsafeDereferenceMethod();
215 bool unwrapMethod();
216 bool getPromiseReactionsMethod();
218 using Method = bool (CallData::*)();
220 template <Method MyMethod>
221 static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
224 template <DebuggerObject::CallData::Method MyMethod>
225 /* static */
226 bool DebuggerObject::CallData::ToNative(JSContext* cx, unsigned argc,
227 Value* vp) {
228 CallArgs args = CallArgsFromVp(argc, vp);
230 Rooted<DebuggerObject*> obj(cx, DebuggerObject_checkThis(cx, args));
231 if (!obj) {
232 return false;
235 CallData data(cx, args, obj);
236 return (data.*MyMethod)();
239 bool DebuggerObject::CallData::callableGetter() {
240 args.rval().setBoolean(object->isCallable());
241 return true;
244 bool DebuggerObject::CallData::isBoundFunctionGetter() {
245 args.rval().setBoolean(object->isBoundFunction());
246 return true;
249 bool DebuggerObject::CallData::isArrowFunctionGetter() {
250 if (!object->isDebuggeeFunction()) {
251 args.rval().setUndefined();
252 return true;
255 args.rval().setBoolean(object->isArrowFunction());
256 return true;
259 bool DebuggerObject::CallData::isAsyncFunctionGetter() {
260 if (!object->isDebuggeeFunction()) {
261 args.rval().setUndefined();
262 return true;
265 args.rval().setBoolean(object->isAsyncFunction());
266 return true;
269 bool DebuggerObject::CallData::isGeneratorFunctionGetter() {
270 if (!object->isDebuggeeFunction()) {
271 args.rval().setUndefined();
272 return true;
275 args.rval().setBoolean(object->isGeneratorFunction());
276 return true;
279 bool DebuggerObject::CallData::isClassConstructorGetter() {
280 if (!object->isDebuggeeFunction()) {
281 args.rval().setUndefined();
282 return true;
285 args.rval().setBoolean(object->isClassConstructor());
286 return true;
289 bool DebuggerObject::CallData::protoGetter() {
290 Rooted<DebuggerObject*> result(cx);
291 if (!DebuggerObject::getPrototypeOf(cx, object, &result)) {
292 return false;
295 args.rval().setObjectOrNull(result);
296 return true;
299 bool DebuggerObject::CallData::classGetter() {
300 RootedString result(cx);
301 if (!DebuggerObject::getClassName(cx, object, &result)) {
302 return false;
305 args.rval().setString(result);
306 return true;
309 bool DebuggerObject::CallData::nameGetter() {
310 if (!object->isFunction() && !object->isBoundFunction()) {
311 args.rval().setUndefined();
312 return true;
315 JS::Rooted<JSAtom*> result(cx);
316 if (!object->name(cx, &result)) {
317 return false;
320 if (result) {
321 args.rval().setString(result);
322 } else {
323 args.rval().setUndefined();
325 return true;
328 bool DebuggerObject::CallData::displayNameGetter() {
329 if (!object->isFunction() && !object->isBoundFunction()) {
330 args.rval().setUndefined();
331 return true;
334 JS::Rooted<JSAtom*> result(cx);
335 if (!object->displayName(cx, &result)) {
336 return false;
338 if (result) {
339 args.rval().setString(result);
340 } else {
341 args.rval().setUndefined();
343 return true;
346 bool DebuggerObject::CallData::parameterNamesGetter() {
347 if (!object->isDebuggeeFunction()) {
348 args.rval().setUndefined();
349 return true;
352 RootedFunction referent(cx, &object->referent()->as<JSFunction>());
354 ArrayObject* arr = GetFunctionParameterNamesArray(cx, referent);
355 if (!arr) {
356 return false;
359 args.rval().setObject(*arr);
360 return true;
363 bool DebuggerObject::CallData::scriptGetter() {
364 Debugger* dbg = object->owner();
366 if (!referent->is<JSFunction>()) {
367 args.rval().setUndefined();
368 return true;
371 RootedFunction fun(cx, &referent->as<JSFunction>());
372 if (!IsInterpretedNonSelfHostedFunction(fun)) {
373 args.rval().setUndefined();
374 return true;
377 RootedScript script(cx, GetOrCreateFunctionScript(cx, fun));
378 if (!script) {
379 return false;
382 // Only hand out debuggee scripts.
383 if (!dbg->observesScript(script)) {
384 args.rval().setNull();
385 return true;
388 Rooted<DebuggerScript*> scriptObject(cx, dbg->wrapScript(cx, script));
389 if (!scriptObject) {
390 return false;
393 args.rval().setObject(*scriptObject);
394 return true;
397 bool DebuggerObject::CallData::environmentGetter() {
398 Debugger* dbg = object->owner();
400 // Don't bother switching compartments just to check obj's type and get its
401 // env.
402 if (!referent->is<JSFunction>()) {
403 args.rval().setUndefined();
404 return true;
407 RootedFunction fun(cx, &referent->as<JSFunction>());
408 if (!IsInterpretedNonSelfHostedFunction(fun)) {
409 args.rval().setUndefined();
410 return true;
413 // Only hand out environments of debuggee functions.
414 if (!dbg->observesGlobal(&fun->global())) {
415 args.rval().setNull();
416 return true;
419 Rooted<Env*> env(cx);
421 AutoRealm ar(cx, fun);
422 env = GetDebugEnvironmentForFunction(cx, fun);
423 if (!env) {
424 return false;
428 return dbg->wrapEnvironment(cx, env, args.rval());
431 bool DebuggerObject::CallData::boundTargetFunctionGetter() {
432 if (!object->isDebuggeeBoundFunction()) {
433 args.rval().setUndefined();
434 return true;
437 Rooted<DebuggerObject*> result(cx);
438 if (!DebuggerObject::getBoundTargetFunction(cx, object, &result)) {
439 return false;
442 args.rval().setObject(*result);
443 return true;
446 bool DebuggerObject::CallData::boundThisGetter() {
447 if (!object->isDebuggeeBoundFunction()) {
448 args.rval().setUndefined();
449 return true;
452 return DebuggerObject::getBoundThis(cx, object, args.rval());
455 bool DebuggerObject::CallData::boundArgumentsGetter() {
456 if (!object->isDebuggeeBoundFunction()) {
457 args.rval().setUndefined();
458 return true;
461 Rooted<ValueVector> result(cx, ValueVector(cx));
462 if (!DebuggerObject::getBoundArguments(cx, object, &result)) {
463 return false;
466 RootedObject obj(cx,
467 NewDenseCopiedArray(cx, result.length(), result.begin()));
468 if (!obj) {
469 return false;
472 args.rval().setObject(*obj);
473 return true;
476 bool DebuggerObject::CallData::allocationSiteGetter() {
477 RootedObject result(cx);
478 if (!DebuggerObject::getAllocationSite(cx, object, &result)) {
479 return false;
482 args.rval().setObjectOrNull(result);
483 return true;
486 // Returns the "name" field (see js/public/friend/ErrorNumbers.msg), which may
487 // be used as a unique identifier, for any error object with a JSErrorReport or
488 // undefined if the object has no JSErrorReport.
489 bool DebuggerObject::CallData::errorMessageNameGetter() {
490 RootedString result(cx);
491 if (!DebuggerObject::getErrorMessageName(cx, object, &result)) {
492 return false;
495 if (result) {
496 args.rval().setString(result);
497 } else {
498 args.rval().setUndefined();
500 return true;
503 bool DebuggerObject::CallData::isErrorGetter() {
504 args.rval().setBoolean(object->isError());
505 return true;
508 bool DebuggerObject::CallData::errorNotesGetter() {
509 return DebuggerObject::getErrorNotes(cx, object, args.rval());
512 bool DebuggerObject::CallData::errorLineNumberGetter() {
513 return DebuggerObject::getErrorLineNumber(cx, object, args.rval());
516 bool DebuggerObject::CallData::errorColumnNumberGetter() {
517 return DebuggerObject::getErrorColumnNumber(cx, object, args.rval());
520 bool DebuggerObject::CallData::isProxyGetter() {
521 args.rval().setBoolean(object->isScriptedProxy());
522 return true;
525 bool DebuggerObject::CallData::proxyTargetGetter() {
526 if (!object->isScriptedProxy()) {
527 args.rval().setUndefined();
528 return true;
531 Rooted<DebuggerObject*> result(cx);
532 if (!DebuggerObject::getScriptedProxyTarget(cx, object, &result)) {
533 return false;
536 args.rval().setObjectOrNull(result);
537 return true;
540 bool DebuggerObject::CallData::proxyHandlerGetter() {
541 if (!object->isScriptedProxy()) {
542 args.rval().setUndefined();
543 return true;
545 Rooted<DebuggerObject*> result(cx);
546 if (!DebuggerObject::getScriptedProxyHandler(cx, object, &result)) {
547 return false;
550 args.rval().setObjectOrNull(result);
551 return true;
554 bool DebuggerObject::CallData::isPromiseGetter() {
555 args.rval().setBoolean(object->isPromise());
556 return true;
559 bool DebuggerObject::CallData::promiseStateGetter() {
560 if (!DebuggerObject::requirePromise(cx, object)) {
561 return false;
564 RootedValue result(cx);
565 switch (object->promiseState()) {
566 case JS::PromiseState::Pending:
567 result.setString(cx->names().pending);
568 break;
569 case JS::PromiseState::Fulfilled:
570 result.setString(cx->names().fulfilled);
571 break;
572 case JS::PromiseState::Rejected:
573 result.setString(cx->names().rejected);
574 break;
577 args.rval().set(result);
578 return true;
581 bool DebuggerObject::CallData::promiseValueGetter() {
582 if (!DebuggerObject::requirePromise(cx, object)) {
583 return false;
586 if (object->promiseState() != JS::PromiseState::Fulfilled) {
587 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
588 JSMSG_DEBUG_PROMISE_NOT_FULFILLED);
589 return false;
592 return DebuggerObject::getPromiseValue(cx, object, args.rval());
596 bool DebuggerObject::CallData::promiseReasonGetter() {
597 if (!DebuggerObject::requirePromise(cx, object)) {
598 return false;
601 if (object->promiseState() != JS::PromiseState::Rejected) {
602 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
603 JSMSG_DEBUG_PROMISE_NOT_REJECTED);
604 return false;
607 return DebuggerObject::getPromiseReason(cx, object, args.rval());
610 bool DebuggerObject::CallData::promiseLifetimeGetter() {
611 if (!DebuggerObject::requirePromise(cx, object)) {
612 return false;
615 args.rval().setNumber(object->promiseLifetime());
616 return true;
619 bool DebuggerObject::CallData::promiseTimeToResolutionGetter() {
620 if (!DebuggerObject::requirePromise(cx, object)) {
621 return false;
624 if (object->promiseState() == JS::PromiseState::Pending) {
625 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
626 JSMSG_DEBUG_PROMISE_NOT_RESOLVED);
627 return false;
630 args.rval().setNumber(object->promiseTimeToResolution());
631 return true;
634 static PromiseObject* EnsurePromise(JSContext* cx, HandleObject referent) {
635 // We only care about promises, so CheckedUnwrapStatic is OK.
636 RootedObject obj(cx, CheckedUnwrapStatic(referent));
637 if (!obj) {
638 ReportAccessDenied(cx);
639 return nullptr;
641 if (!obj->is<PromiseObject>()) {
642 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
643 JSMSG_NOT_EXPECTED_TYPE, "Debugger", "Promise",
644 obj->getClass()->name);
645 return nullptr;
647 return &obj->as<PromiseObject>();
650 bool DebuggerObject::CallData::promiseAllocationSiteGetter() {
651 Rooted<PromiseObject*> promise(cx, EnsurePromise(cx, referent));
652 if (!promise) {
653 return false;
656 RootedObject allocSite(cx, promise->allocationSite());
657 if (!allocSite) {
658 args.rval().setNull();
659 return true;
662 if (!cx->compartment()->wrap(cx, &allocSite)) {
663 return false;
665 args.rval().set(ObjectValue(*allocSite));
666 return true;
669 bool DebuggerObject::CallData::promiseResolutionSiteGetter() {
670 Rooted<PromiseObject*> promise(cx, EnsurePromise(cx, referent));
671 if (!promise) {
672 return false;
675 if (promise->state() == JS::PromiseState::Pending) {
676 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
677 JSMSG_DEBUG_PROMISE_NOT_RESOLVED);
678 return false;
681 RootedObject resolutionSite(cx, promise->resolutionSite());
682 if (!resolutionSite) {
683 args.rval().setNull();
684 return true;
687 if (!cx->compartment()->wrap(cx, &resolutionSite)) {
688 return false;
690 args.rval().set(ObjectValue(*resolutionSite));
691 return true;
694 bool DebuggerObject::CallData::promiseIDGetter() {
695 Rooted<PromiseObject*> promise(cx, EnsurePromise(cx, referent));
696 if (!promise) {
697 return false;
700 args.rval().setNumber(double(promise->getID()));
701 return true;
704 bool DebuggerObject::CallData::promiseDependentPromisesGetter() {
705 Debugger* dbg = object->owner();
707 Rooted<PromiseObject*> promise(cx, EnsurePromise(cx, referent));
708 if (!promise) {
709 return false;
712 Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
714 JSAutoRealm ar(cx, promise);
715 if (!promise->dependentPromises(cx, &values)) {
716 return false;
719 for (size_t i = 0; i < values.length(); i++) {
720 if (!dbg->wrapDebuggeeValue(cx, values[i])) {
721 return false;
724 Rooted<ArrayObject*> promises(cx);
725 if (values.length() == 0) {
726 promises = NewDenseEmptyArray(cx);
727 } else {
728 promises = NewDenseCopiedArray(cx, values.length(), values[0].address());
730 if (!promises) {
731 return false;
733 args.rval().setObject(*promises);
734 return true;
737 bool DebuggerObject::CallData::isExtensibleMethod() {
738 bool result;
739 if (!DebuggerObject::isExtensible(cx, object, result)) {
740 return false;
743 args.rval().setBoolean(result);
744 return true;
747 bool DebuggerObject::CallData::isSealedMethod() {
748 bool result;
749 if (!DebuggerObject::isSealed(cx, object, result)) {
750 return false;
753 args.rval().setBoolean(result);
754 return true;
757 bool DebuggerObject::CallData::isFrozenMethod() {
758 bool result;
759 if (!DebuggerObject::isFrozen(cx, object, result)) {
760 return false;
763 args.rval().setBoolean(result);
764 return true;
767 bool DebuggerObject::CallData::getOwnPropertyNamesMethod() {
768 RootedIdVector ids(cx);
769 if (!DebuggerObject::getOwnPropertyNames(cx, object, &ids)) {
770 return false;
773 JSObject* obj = IdVectorToArray(cx, ids);
774 if (!obj) {
775 return false;
778 args.rval().setObject(*obj);
779 return true;
782 bool DebuggerObject::CallData::getOwnPropertyNamesLengthMethod() {
783 size_t ownPropertiesLength;
784 if (!DebuggerObject::getOwnPropertyNamesLength(cx, object,
785 &ownPropertiesLength)) {
786 return false;
789 args.rval().setNumber(ownPropertiesLength);
790 return true;
793 bool DebuggerObject::CallData::getOwnPropertySymbolsMethod() {
794 RootedIdVector ids(cx);
795 if (!DebuggerObject::getOwnPropertySymbols(cx, object, &ids)) {
796 return false;
799 JSObject* obj = IdVectorToArray(cx, ids);
800 if (!obj) {
801 return false;
804 args.rval().setObject(*obj);
805 return true;
808 bool DebuggerObject::CallData::getOwnPrivatePropertiesMethod() {
809 RootedIdVector ids(cx);
810 if (!DebuggerObject::getOwnPrivateProperties(cx, object, &ids)) {
811 return false;
814 JSObject* obj = IdVectorToArray(cx, ids);
815 if (!obj) {
816 return false;
819 args.rval().setObject(*obj);
820 return true;
823 bool DebuggerObject::CallData::getOwnPropertyDescriptorMethod() {
824 RootedId id(cx);
825 if (!ToPropertyKey(cx, args.get(0), &id)) {
826 return false;
829 Rooted<Maybe<PropertyDescriptor>> desc(cx);
830 if (!DebuggerObject::getOwnPropertyDescriptor(cx, object, id, &desc)) {
831 return false;
834 return JS::FromPropertyDescriptor(cx, desc, args.rval());
837 bool DebuggerObject::CallData::preventExtensionsMethod() {
838 if (!DebuggerObject::preventExtensions(cx, object)) {
839 return false;
842 args.rval().setUndefined();
843 return true;
846 bool DebuggerObject::CallData::sealMethod() {
847 if (!DebuggerObject::seal(cx, object)) {
848 return false;
851 args.rval().setUndefined();
852 return true;
855 bool DebuggerObject::CallData::freezeMethod() {
856 if (!DebuggerObject::freeze(cx, object)) {
857 return false;
860 args.rval().setUndefined();
861 return true;
864 bool DebuggerObject::CallData::definePropertyMethod() {
865 if (!args.requireAtLeast(cx, "Debugger.Object.defineProperty", 2)) {
866 return false;
869 RootedId id(cx);
870 if (!ToPropertyKey(cx, args[0], &id)) {
871 return false;
874 Rooted<PropertyDescriptor> desc(cx);
875 if (!ToPropertyDescriptor(cx, args[1], false, &desc)) {
876 return false;
879 if (!DebuggerObject::defineProperty(cx, object, id, desc)) {
880 return false;
883 args.rval().setUndefined();
884 return true;
887 bool DebuggerObject::CallData::definePropertiesMethod() {
888 if (!args.requireAtLeast(cx, "Debugger.Object.defineProperties", 1)) {
889 return false;
892 RootedValue arg(cx, args[0]);
893 RootedObject props(cx, ToObject(cx, arg));
894 if (!props) {
895 return false;
897 RootedIdVector ids(cx);
898 Rooted<PropertyDescriptorVector> descs(cx, PropertyDescriptorVector(cx));
899 if (!ReadPropertyDescriptors(cx, props, false, &ids, &descs)) {
900 return false;
902 Rooted<IdVector> ids2(cx, IdVector(cx));
903 if (!ids2.append(ids.begin(), ids.end())) {
904 return false;
907 if (!DebuggerObject::defineProperties(cx, object, ids2, descs)) {
908 return false;
911 args.rval().setUndefined();
912 return true;
916 * This does a non-strict delete, as a matter of API design. The case where the
917 * property is non-configurable isn't necessarily exceptional here.
919 bool DebuggerObject::CallData::deletePropertyMethod() {
920 RootedId id(cx);
921 if (!ToPropertyKey(cx, args.get(0), &id)) {
922 return false;
925 ObjectOpResult result;
926 if (!DebuggerObject::deleteProperty(cx, object, id, result)) {
927 return false;
930 args.rval().setBoolean(result.ok());
931 return true;
934 bool DebuggerObject::CallData::callMethod() {
935 RootedValue thisv(cx, args.get(0));
937 Rooted<ValueVector> nargs(cx, ValueVector(cx));
938 if (args.length() >= 2) {
939 if (!nargs.growBy(args.length() - 1)) {
940 return false;
942 for (size_t i = 1; i < args.length(); ++i) {
943 nargs[i - 1].set(args[i]);
947 Rooted<Maybe<Completion>> completion(
948 cx, DebuggerObject::call(cx, object, thisv, nargs));
949 if (!completion.get()) {
950 return false;
953 return completion->buildCompletionValue(cx, object->owner(), args.rval());
956 bool DebuggerObject::CallData::getPropertyMethod() {
957 Debugger* dbg = object->owner();
959 RootedId id(cx);
960 if (!ToPropertyKey(cx, args.get(0), &id)) {
961 return false;
964 RootedValue receiver(cx,
965 args.length() < 2 ? ObjectValue(*object) : args.get(1));
967 Rooted<Completion> comp(cx);
968 JS_TRY_VAR_OR_RETURN_FALSE(cx, comp, getProperty(cx, object, id, receiver));
969 return comp.get().buildCompletionValue(cx, dbg, args.rval());
972 bool DebuggerObject::CallData::setPropertyMethod() {
973 Debugger* dbg = object->owner();
975 RootedId id(cx);
976 if (!ToPropertyKey(cx, args.get(0), &id)) {
977 return false;
980 RootedValue value(cx, args.get(1));
982 RootedValue receiver(cx,
983 args.length() < 3 ? ObjectValue(*object) : args.get(2));
985 Rooted<Completion> comp(cx);
986 JS_TRY_VAR_OR_RETURN_FALSE(cx, comp,
987 setProperty(cx, object, id, value, receiver));
988 return comp.get().buildCompletionValue(cx, dbg, args.rval());
991 bool DebuggerObject::CallData::applyMethod() {
992 RootedValue thisv(cx, args.get(0));
994 Rooted<ValueVector> nargs(cx, ValueVector(cx));
995 if (args.length() >= 2 && !args[1].isNullOrUndefined()) {
996 if (!args[1].isObject()) {
997 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
998 JSMSG_BAD_APPLY_ARGS, "apply");
999 return false;
1002 RootedObject argsobj(cx, &args[1].toObject());
1004 uint64_t argc = 0;
1005 if (!GetLengthProperty(cx, argsobj, &argc)) {
1006 return false;
1008 argc = std::min(argc, uint64_t(ARGS_LENGTH_MAX));
1010 if (!nargs.growBy(argc) || !GetElements(cx, argsobj, argc, nargs.begin())) {
1011 return false;
1015 Rooted<Maybe<Completion>> completion(
1016 cx, DebuggerObject::call(cx, object, thisv, nargs));
1017 if (!completion.get()) {
1018 return false;
1021 return completion->buildCompletionValue(cx, object->owner(), args.rval());
1024 static void EnterDebuggeeObjectRealm(JSContext* cx, Maybe<AutoRealm>& ar,
1025 JSObject* referent) {
1026 // |referent| may be a cross-compartment wrapper and CCWs normally
1027 // shouldn't be used with AutoRealm, but here we use an arbitrary realm for
1028 // now because we don't really have another option.
1029 ar.emplace(cx, referent->maybeCCWRealm()->maybeGlobal());
1032 static bool RequireGlobalObject(JSContext* cx, HandleValue dbgobj,
1033 HandleObject referent) {
1034 RootedObject obj(cx, referent);
1036 if (!obj->is<GlobalObject>()) {
1037 const char* isWrapper = "";
1038 const char* isWindowProxy = "";
1040 // Help the poor programmer by pointing out wrappers around globals...
1041 if (obj->is<WrapperObject>()) {
1042 obj = js::UncheckedUnwrap(obj);
1043 isWrapper = "a wrapper around ";
1046 // ... and WindowProxies around Windows.
1047 if (IsWindowProxy(obj)) {
1048 obj = ToWindowIfWindowProxy(obj);
1049 isWindowProxy = "a WindowProxy referring to ";
1052 if (obj->is<GlobalObject>()) {
1053 ReportValueError(cx, JSMSG_DEBUG_WRAPPER_IN_WAY, JSDVG_SEARCH_STACK,
1054 dbgobj, nullptr, isWrapper, isWindowProxy);
1055 } else {
1056 ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK, dbgobj,
1057 nullptr, "a global object");
1059 return false;
1062 return true;
1065 bool DebuggerObject::CallData::asEnvironmentMethod() {
1066 Debugger* dbg = object->owner();
1068 if (!RequireGlobalObject(cx, args.thisv(), referent)) {
1069 return false;
1072 Rooted<Env*> env(cx);
1074 AutoRealm ar(cx, referent);
1075 env = GetDebugEnvironmentForGlobalLexicalEnvironment(cx);
1076 if (!env) {
1077 return false;
1081 return dbg->wrapEnvironment(cx, env, args.rval());
1084 // Lookup a binding on the referent's global scope and change it to undefined
1085 // if it is an uninitialized lexical, otherwise do nothing. The method's
1086 // JavaScript return value is true _only_ when an uninitialized lexical has been
1087 // altered, otherwise it is false.
1088 bool DebuggerObject::CallData::forceLexicalInitializationByNameMethod() {
1089 if (!args.requireAtLeast(
1090 cx, "Debugger.Object.prototype.forceLexicalInitializationByName",
1091 1)) {
1092 return false;
1095 if (!DebuggerObject::requireGlobal(cx, object)) {
1096 return false;
1099 RootedId id(cx);
1100 if (!ValueToIdentifier(cx, args[0], &id)) {
1101 return false;
1104 bool result;
1105 if (!DebuggerObject::forceLexicalInitializationByName(cx, object, id,
1106 result)) {
1107 return false;
1110 args.rval().setBoolean(result);
1111 return true;
1114 bool DebuggerObject::CallData::executeInGlobalMethod() {
1115 if (!args.requireAtLeast(cx, "Debugger.Object.prototype.executeInGlobal",
1116 1)) {
1117 return false;
1120 if (!DebuggerObject::requireGlobal(cx, object)) {
1121 return false;
1124 AutoStableStringChars stableChars(cx);
1125 if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobal",
1126 args[0], stableChars)) {
1127 return false;
1129 mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
1131 EvalOptions options(EvalOptions::EnvKind::Global);
1132 if (!ParseEvalOptions(cx, args.get(1), options)) {
1133 return false;
1136 Rooted<Completion> comp(cx);
1137 JS_TRY_VAR_OR_RETURN_FALSE(
1138 cx, comp,
1139 DebuggerObject::executeInGlobal(cx, object, chars, nullptr, options));
1140 return comp.get().buildCompletionValue(cx, object->owner(), args.rval());
1143 bool DebuggerObject::CallData::executeInGlobalWithBindingsMethod() {
1144 if (!args.requireAtLeast(
1145 cx, "Debugger.Object.prototype.executeInGlobalWithBindings", 2)) {
1146 return false;
1149 if (!DebuggerObject::requireGlobal(cx, object)) {
1150 return false;
1153 AutoStableStringChars stableChars(cx);
1154 if (!ValueToStableChars(
1155 cx, "Debugger.Object.prototype.executeInGlobalWithBindings", args[0],
1156 stableChars)) {
1157 return false;
1159 mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
1161 RootedObject bindings(cx, RequireObject(cx, args[1]));
1162 if (!bindings) {
1163 return false;
1166 EvalOptions options(EvalOptions::EnvKind::GlobalWithExtraOuterBindings);
1167 if (!ParseEvalOptions(cx, args.get(2), options)) {
1168 return false;
1171 Rooted<Completion> comp(cx);
1172 JS_TRY_VAR_OR_RETURN_FALSE(
1173 cx, comp,
1174 DebuggerObject::executeInGlobal(cx, object, chars, bindings, options));
1175 return comp.get().buildCompletionValue(cx, object->owner(), args.rval());
1178 // Copy a narrow or wide string to a vector, appending a null terminator.
1179 template <typename T>
1180 static bool CopyStringToVector(JSContext* cx, JSString* str, Vector<T>& chars) {
1181 JSLinearString* linear = str->ensureLinear(cx);
1182 if (!linear) {
1183 return false;
1185 if (!chars.appendN(0, linear->length() + 1)) {
1186 return false;
1188 CopyChars(chars.begin(), *linear);
1189 return true;
1192 bool DebuggerObject::CallData::createSource() {
1193 if (!args.requireAtLeast(cx, "Debugger.Object.prototype.createSource", 1)) {
1194 return false;
1197 if (!DebuggerObject::requireGlobal(cx, object)) {
1198 return false;
1201 Debugger* dbg = object->owner();
1202 if (!dbg->isDebuggeeUnbarriered(referent->as<GlobalObject>().realm())) {
1203 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1204 JSMSG_DEBUG_NOT_DEBUGGEE, "Debugger.Object",
1205 "global");
1206 return false;
1209 RootedObject options(cx, ToObject(cx, args[0]));
1210 if (!options) {
1211 return false;
1214 RootedValue v(cx);
1215 if (!JS_GetProperty(cx, options, "text", &v)) {
1216 return false;
1219 RootedString text(cx, ToString<CanGC>(cx, v));
1220 if (!text) {
1221 return false;
1224 if (!JS_GetProperty(cx, options, "url", &v)) {
1225 return false;
1228 RootedString url(cx, ToString<CanGC>(cx, v));
1229 if (!url) {
1230 return false;
1233 if (!JS_GetProperty(cx, options, "startLine", &v)) {
1234 return false;
1237 uint32_t startLine;
1238 if (!ToUint32(cx, v, &startLine)) {
1239 return false;
1242 if (!JS_GetProperty(cx, options, "startColumn", &v)) {
1243 return false;
1246 uint32_t startColumn;
1247 if (!ToUint32(cx, v, &startColumn)) {
1248 return false;
1250 if (startColumn == 0) {
1251 startColumn = 1;
1254 if (!JS_GetProperty(cx, options, "sourceMapURL", &v)) {
1255 return false;
1258 RootedString sourceMapURL(cx);
1259 if (!v.isUndefined()) {
1260 sourceMapURL = ToString<CanGC>(cx, v);
1261 if (!sourceMapURL) {
1262 return false;
1266 if (!JS_GetProperty(cx, options, "isScriptElement", &v)) {
1267 return false;
1270 bool isScriptElement = ToBoolean(v);
1272 JS::CompileOptions compileOptions(cx);
1273 compileOptions.lineno = startLine;
1274 compileOptions.column = JS::ColumnNumberOneOrigin(startColumn);
1276 if (!JS::StringHasLatin1Chars(url)) {
1277 JS_ReportErrorASCII(cx, "URL must be a narrow string");
1278 return false;
1281 Vector<Latin1Char> urlChars(cx);
1282 if (!CopyStringToVector(cx, url, urlChars)) {
1283 return false;
1285 compileOptions.setFile((const char*)urlChars.begin());
1287 Vector<char16_t> sourceMapURLChars(cx);
1288 if (sourceMapURL) {
1289 if (!CopyStringToVector(cx, sourceMapURL, sourceMapURLChars)) {
1290 return false;
1292 compileOptions.setSourceMapURL(sourceMapURLChars.begin());
1295 if (isScriptElement) {
1296 // The introduction type must be a statically allocated string.
1297 compileOptions.setIntroductionType("inlineScript");
1300 AutoStableStringChars linearChars(cx);
1301 if (!linearChars.initTwoByte(cx, text)) {
1302 return false;
1304 JS::SourceText<char16_t> srcBuf;
1305 if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
1306 return false;
1309 RootedScript script(cx);
1311 AutoRealm ar(cx, referent);
1312 script = JS::Compile(cx, compileOptions, srcBuf);
1313 if (!script) {
1314 return false;
1318 Rooted<ScriptSourceObject*> sso(cx, script->sourceObject());
1319 RootedObject wrapped(cx, dbg->wrapSource(cx, sso));
1320 if (!wrapped) {
1321 return false;
1324 args.rval().setObject(*wrapped);
1325 return true;
1328 bool DebuggerObject::CallData::makeDebuggeeValueMethod() {
1329 if (!args.requireAtLeast(cx, "Debugger.Object.prototype.makeDebuggeeValue",
1330 1)) {
1331 return false;
1334 return DebuggerObject::makeDebuggeeValue(cx, object, args[0], args.rval());
1337 bool DebuggerObject::CallData::isSameNativeMethod() {
1338 if (!args.requireAtLeast(cx, "Debugger.Object.prototype.isSameNative", 1)) {
1339 return false;
1342 return DebuggerObject::isSameNative(cx, object, args[0], CheckJitInfo::No,
1343 args.rval());
1346 bool DebuggerObject::CallData::isSameNativeWithJitInfoMethod() {
1347 if (!args.requireAtLeast(
1348 cx, "Debugger.Object.prototype.isSameNativeWithJitInfo", 1)) {
1349 return false;
1352 return DebuggerObject::isSameNative(cx, object, args[0], CheckJitInfo::Yes,
1353 args.rval());
1356 bool DebuggerObject::CallData::isNativeGetterWithJitInfo() {
1357 return DebuggerObject::isNativeGetterWithJitInfo(cx, object, args.rval());
1360 bool DebuggerObject::CallData::unsafeDereferenceMethod() {
1361 RootedObject result(cx);
1362 if (!DebuggerObject::unsafeDereference(cx, object, &result)) {
1363 return false;
1366 args.rval().setObject(*result);
1367 return true;
1370 bool DebuggerObject::CallData::unwrapMethod() {
1371 Rooted<DebuggerObject*> result(cx);
1372 if (!DebuggerObject::unwrap(cx, object, &result)) {
1373 return false;
1376 args.rval().setObjectOrNull(result);
1377 return true;
1380 struct DebuggerObject::PromiseReactionRecordBuilder
1381 : js::PromiseReactionRecordBuilder {
1382 Debugger* dbg;
1383 Handle<ArrayObject*> records;
1385 PromiseReactionRecordBuilder(Debugger* dbg, Handle<ArrayObject*> records)
1386 : dbg(dbg), records(records) {}
1388 bool then(JSContext* cx, HandleObject resolve, HandleObject reject,
1389 HandleObject result) override {
1390 Rooted<PlainObject*> record(cx, NewPlainObject(cx));
1391 if (!record) {
1392 return false;
1395 if (!setIfNotNull(cx, record, cx->names().resolve, resolve) ||
1396 !setIfNotNull(cx, record, cx->names().reject, reject) ||
1397 !setIfNotNull(cx, record, cx->names().result, result)) {
1398 return false;
1401 return push(cx, record);
1404 bool direct(JSContext* cx, Handle<PromiseObject*> unwrappedPromise) override {
1405 RootedValue v(cx, ObjectValue(*unwrappedPromise));
1406 return dbg->wrapDebuggeeValue(cx, &v) && push(cx, v);
1409 bool asyncFunction(
1410 JSContext* cx,
1411 Handle<AsyncFunctionGeneratorObject*> unwrappedGenerator) override {
1412 return maybePushGenerator(cx, unwrappedGenerator);
1415 bool asyncGenerator(
1416 JSContext* cx,
1417 Handle<AsyncGeneratorObject*> unwrappedGenerator) override {
1418 return maybePushGenerator(cx, unwrappedGenerator);
1421 private:
1422 bool push(JSContext* cx, HandleObject record) {
1423 RootedValue recordVal(cx, ObjectValue(*record));
1424 return push(cx, recordVal);
1427 bool push(JSContext* cx, HandleValue recordVal) {
1428 return NewbornArrayPush(cx, records, recordVal);
1431 bool maybePushGenerator(JSContext* cx,
1432 Handle<AbstractGeneratorObject*> unwrappedGenerator) {
1433 Rooted<DebuggerFrame*> frame(cx);
1434 if (unwrappedGenerator->isClosed()) {
1435 // If the generator is closed, we can't generate a DebuggerFrame for it,
1436 // so we ignore it.
1437 return true;
1439 if (!unwrappedGenerator->realm()->isDebuggee()) {
1440 // Caller can keep the reference to the debugger object even after
1441 // removing the realm from debuggee. Do nothing for this case.
1442 return true;
1444 return dbg->getFrame(cx, unwrappedGenerator, &frame) && push(cx, frame);
1447 bool setIfNotNull(JSContext* cx, Handle<PlainObject*> obj,
1448 Handle<PropertyName*> name, HandleObject prop) {
1449 if (!prop) {
1450 return true;
1453 RootedValue v(cx, ObjectValue(*prop));
1454 if (!dbg->wrapDebuggeeValue(cx, &v) ||
1455 !DefineDataProperty(cx, obj, name, v)) {
1456 return false;
1459 return true;
1463 bool DebuggerObject::CallData::getPromiseReactionsMethod() {
1464 Debugger* dbg = object->owner();
1466 Rooted<PromiseObject*> unwrappedPromise(cx, EnsurePromise(cx, referent));
1467 if (!unwrappedPromise) {
1468 return false;
1471 Rooted<ArrayObject*> holder(cx, NewDenseEmptyArray(cx));
1472 if (!holder) {
1473 return false;
1476 PromiseReactionRecordBuilder builder(dbg, holder);
1477 if (!unwrappedPromise->forEachReactionRecord(cx, builder)) {
1478 return false;
1481 args.rval().setObject(*builder.records);
1482 return true;
1485 const JSPropertySpec DebuggerObject::properties_[] = {
1486 JS_DEBUG_PSG("callable", callableGetter),
1487 JS_DEBUG_PSG("isBoundFunction", isBoundFunctionGetter),
1488 JS_DEBUG_PSG("isArrowFunction", isArrowFunctionGetter),
1489 JS_DEBUG_PSG("isGeneratorFunction", isGeneratorFunctionGetter),
1490 JS_DEBUG_PSG("isAsyncFunction", isAsyncFunctionGetter),
1491 JS_DEBUG_PSG("isClassConstructor", isClassConstructorGetter),
1492 JS_DEBUG_PSG("proto", protoGetter),
1493 JS_DEBUG_PSG("class", classGetter),
1494 JS_DEBUG_PSG("name", nameGetter),
1495 JS_DEBUG_PSG("displayName", displayNameGetter),
1496 JS_DEBUG_PSG("parameterNames", parameterNamesGetter),
1497 JS_DEBUG_PSG("script", scriptGetter),
1498 JS_DEBUG_PSG("environment", environmentGetter),
1499 JS_DEBUG_PSG("boundTargetFunction", boundTargetFunctionGetter),
1500 JS_DEBUG_PSG("boundThis", boundThisGetter),
1501 JS_DEBUG_PSG("boundArguments", boundArgumentsGetter),
1502 JS_DEBUG_PSG("allocationSite", allocationSiteGetter),
1503 JS_DEBUG_PSG("isError", isErrorGetter),
1504 JS_DEBUG_PSG("errorMessageName", errorMessageNameGetter),
1505 JS_DEBUG_PSG("errorNotes", errorNotesGetter),
1506 JS_DEBUG_PSG("errorLineNumber", errorLineNumberGetter),
1507 JS_DEBUG_PSG("errorColumnNumber", errorColumnNumberGetter),
1508 JS_DEBUG_PSG("isProxy", isProxyGetter),
1509 JS_DEBUG_PSG("proxyTarget", proxyTargetGetter),
1510 JS_DEBUG_PSG("proxyHandler", proxyHandlerGetter),
1511 JS_PS_END};
1513 const JSPropertySpec DebuggerObject::promiseProperties_[] = {
1514 JS_DEBUG_PSG("isPromise", isPromiseGetter),
1515 JS_DEBUG_PSG("promiseState", promiseStateGetter),
1516 JS_DEBUG_PSG("promiseValue", promiseValueGetter),
1517 JS_DEBUG_PSG("promiseReason", promiseReasonGetter),
1518 JS_DEBUG_PSG("promiseLifetime", promiseLifetimeGetter),
1519 JS_DEBUG_PSG("promiseTimeToResolution", promiseTimeToResolutionGetter),
1520 JS_DEBUG_PSG("promiseAllocationSite", promiseAllocationSiteGetter),
1521 JS_DEBUG_PSG("promiseResolutionSite", promiseResolutionSiteGetter),
1522 JS_DEBUG_PSG("promiseID", promiseIDGetter),
1523 JS_DEBUG_PSG("promiseDependentPromises", promiseDependentPromisesGetter),
1524 JS_PS_END};
1526 const JSFunctionSpec DebuggerObject::methods_[] = {
1527 JS_DEBUG_FN("isExtensible", isExtensibleMethod, 0),
1528 JS_DEBUG_FN("isSealed", isSealedMethod, 0),
1529 JS_DEBUG_FN("isFrozen", isFrozenMethod, 0),
1530 JS_DEBUG_FN("getProperty", getPropertyMethod, 0),
1531 JS_DEBUG_FN("setProperty", setPropertyMethod, 0),
1532 JS_DEBUG_FN("getOwnPropertyNames", getOwnPropertyNamesMethod, 0),
1533 JS_DEBUG_FN("getOwnPropertyNamesLength", getOwnPropertyNamesLengthMethod,
1535 JS_DEBUG_FN("getOwnPropertySymbols", getOwnPropertySymbolsMethod, 0),
1536 JS_DEBUG_FN("getOwnPrivateProperties", getOwnPrivatePropertiesMethod, 0),
1537 JS_DEBUG_FN("getOwnPropertyDescriptor", getOwnPropertyDescriptorMethod, 1),
1538 JS_DEBUG_FN("preventExtensions", preventExtensionsMethod, 0),
1539 JS_DEBUG_FN("seal", sealMethod, 0),
1540 JS_DEBUG_FN("freeze", freezeMethod, 0),
1541 JS_DEBUG_FN("defineProperty", definePropertyMethod, 2),
1542 JS_DEBUG_FN("defineProperties", definePropertiesMethod, 1),
1543 JS_DEBUG_FN("deleteProperty", deletePropertyMethod, 1),
1544 JS_DEBUG_FN("call", callMethod, 0),
1545 JS_DEBUG_FN("apply", applyMethod, 0),
1546 JS_DEBUG_FN("asEnvironment", asEnvironmentMethod, 0),
1547 JS_DEBUG_FN("forceLexicalInitializationByName",
1548 forceLexicalInitializationByNameMethod, 1),
1549 JS_DEBUG_FN("executeInGlobal", executeInGlobalMethod, 1),
1550 JS_DEBUG_FN("executeInGlobalWithBindings",
1551 executeInGlobalWithBindingsMethod, 2),
1552 JS_DEBUG_FN("createSource", createSource, 1),
1553 JS_DEBUG_FN("makeDebuggeeValue", makeDebuggeeValueMethod, 1),
1554 JS_DEBUG_FN("isSameNative", isSameNativeMethod, 1),
1555 JS_DEBUG_FN("isSameNativeWithJitInfo", isSameNativeWithJitInfoMethod, 1),
1556 JS_DEBUG_FN("isNativeGetterWithJitInfo", isNativeGetterWithJitInfo, 1),
1557 JS_DEBUG_FN("unsafeDereference", unsafeDereferenceMethod, 0),
1558 JS_DEBUG_FN("unwrap", unwrapMethod, 0),
1559 JS_DEBUG_FN("getPromiseReactions", getPromiseReactionsMethod, 0),
1560 JS_FS_END};
1562 /* static */
1563 NativeObject* DebuggerObject::initClass(JSContext* cx,
1564 Handle<GlobalObject*> global,
1565 HandleObject debugCtor) {
1566 Rooted<NativeObject*> objectProto(
1567 cx, InitClass(cx, debugCtor, nullptr, nullptr, "Object", construct, 0,
1568 properties_, methods_, nullptr, nullptr));
1570 if (!objectProto) {
1571 return nullptr;
1574 if (!DefinePropertiesAndFunctions(cx, objectProto, promiseProperties_,
1575 nullptr)) {
1576 return nullptr;
1579 return objectProto;
1582 /* static */
1583 DebuggerObject* DebuggerObject::create(JSContext* cx, HandleObject proto,
1584 HandleObject referent,
1585 Handle<NativeObject*> debugger) {
1586 DebuggerObject* obj =
1587 IsInsideNursery(referent)
1588 ? NewObjectWithGivenProto<DebuggerObject>(cx, proto)
1589 : NewTenuredObjectWithGivenProto<DebuggerObject>(cx, proto);
1590 if (!obj) {
1591 return nullptr;
1594 obj->setReservedSlotGCThingAsPrivate(OBJECT_SLOT, referent);
1595 obj->setReservedSlot(OWNER_SLOT, ObjectValue(*debugger));
1597 return obj;
1600 bool DebuggerObject::isCallable() const { return referent()->isCallable(); }
1602 bool DebuggerObject::isFunction() const { return referent()->is<JSFunction>(); }
1604 bool DebuggerObject::isDebuggeeFunction() const {
1605 return referent()->is<JSFunction>() &&
1606 owner()->observesGlobal(&referent()->as<JSFunction>().global());
1609 bool DebuggerObject::isBoundFunction() const {
1610 return referent()->is<BoundFunctionObject>();
1613 bool DebuggerObject::isDebuggeeBoundFunction() const {
1614 return referent()->is<BoundFunctionObject>() &&
1615 owner()->observesGlobal(
1616 &referent()->as<BoundFunctionObject>().global());
1619 bool DebuggerObject::isArrowFunction() const {
1620 MOZ_ASSERT(isDebuggeeFunction());
1622 return referent()->as<JSFunction>().isArrow();
1625 bool DebuggerObject::isAsyncFunction() const {
1626 MOZ_ASSERT(isDebuggeeFunction());
1628 return referent()->as<JSFunction>().isAsync();
1631 bool DebuggerObject::isGeneratorFunction() const {
1632 MOZ_ASSERT(isDebuggeeFunction());
1634 return referent()->as<JSFunction>().isGenerator();
1637 bool DebuggerObject::isClassConstructor() const {
1638 MOZ_ASSERT(isDebuggeeFunction());
1640 return referent()->as<JSFunction>().isClassConstructor();
1643 bool DebuggerObject::isGlobal() const { return referent()->is<GlobalObject>(); }
1645 bool DebuggerObject::isScriptedProxy() const {
1646 return js::IsScriptedProxy(referent());
1649 bool DebuggerObject::isPromise() const {
1650 JSObject* referent = this->referent();
1652 if (IsCrossCompartmentWrapper(referent)) {
1653 // We only care about promises, so CheckedUnwrapStatic is OK.
1654 referent = CheckedUnwrapStatic(referent);
1655 if (!referent) {
1656 return false;
1660 return referent->is<PromiseObject>();
1663 bool DebuggerObject::isError() const {
1664 JSObject* referent = this->referent();
1666 if (IsCrossCompartmentWrapper(referent)) {
1667 // We only check for error classes, so CheckedUnwrapStatic is OK.
1668 referent = CheckedUnwrapStatic(referent);
1669 if (!referent) {
1670 return false;
1674 return referent->is<ErrorObject>();
1677 /* static */
1678 bool DebuggerObject::getClassName(JSContext* cx, Handle<DebuggerObject*> object,
1679 MutableHandleString result) {
1680 RootedObject referent(cx, object->referent());
1682 const char* className;
1684 Maybe<AutoRealm> ar;
1685 EnterDebuggeeObjectRealm(cx, ar, referent);
1686 className = GetObjectClassName(cx, referent);
1689 JSAtom* str = Atomize(cx, className, strlen(className));
1690 if (!str) {
1691 return false;
1694 result.set(str);
1695 return true;
1698 bool DebuggerObject::name(JSContext* cx,
1699 JS::MutableHandle<JSAtom*> result) const {
1700 if (isFunction()) {
1701 JSFunction* fun = &referent()->as<JSFunction>();
1702 if (!fun->isAccessorWithLazyName()) {
1703 result.set(fun->fullExplicitName());
1704 if (result) {
1705 cx->markAtom(result);
1707 return true;
1711 Maybe<AutoRealm> ar;
1712 EnterDebuggeeObjectRealm(cx, ar, fun);
1714 result.set(fun->getAccessorNameForLazy(cx));
1715 if (!result) {
1716 return false;
1719 cx->markAtom(result);
1720 return true;
1723 MOZ_ASSERT(isBoundFunction());
1725 // Bound functions have a configurable `name` data property and currently
1726 // don't store the original name. Try a pure lookup to get this name and if
1727 // this fails use "bound".
1728 Rooted<BoundFunctionObject*> bound(cx,
1729 &referent()->as<BoundFunctionObject>());
1731 Maybe<AutoRealm> ar;
1732 EnterDebuggeeObjectRealm(cx, ar, bound);
1734 Value v;
1735 bool found;
1736 if (GetOwnPropertyPure(cx, bound, NameToId(cx->names().name), &v, &found) &&
1737 found && v.isString()) {
1738 result.set(AtomizeString(cx, v.toString()));
1739 if (!result) {
1740 return false;
1742 } else {
1743 result.set(cx->names().bound);
1747 cx->markAtom(result);
1748 return true;
1751 bool DebuggerObject::displayName(JSContext* cx,
1752 JS::MutableHandle<JSAtom*> result) const {
1753 if (isFunction()) {
1755 JS::Rooted<JSFunction*> fun(cx, &referent()->as<JSFunction>());
1757 Maybe<AutoRealm> ar;
1758 EnterDebuggeeObjectRealm(cx, ar, fun);
1760 if (!fun->getDisplayAtom(cx, result)) {
1761 return false;
1764 if (result) {
1765 cx->markAtom(result);
1767 return true;
1770 MOZ_ASSERT(isBoundFunction());
1771 return name(cx, result);
1774 JS::PromiseState DebuggerObject::promiseState() const {
1775 return promise()->state();
1778 double DebuggerObject::promiseLifetime() const { return promise()->lifetime(); }
1780 double DebuggerObject::promiseTimeToResolution() const {
1781 MOZ_ASSERT(promiseState() != JS::PromiseState::Pending);
1783 return promise()->timeToResolution();
1786 /* static */
1787 bool DebuggerObject::getBoundTargetFunction(
1788 JSContext* cx, Handle<DebuggerObject*> object,
1789 MutableHandle<DebuggerObject*> result) {
1790 MOZ_ASSERT(object->isBoundFunction());
1792 Rooted<BoundFunctionObject*> referent(
1793 cx, &object->referent()->as<BoundFunctionObject>());
1794 Debugger* dbg = object->owner();
1796 RootedObject target(cx, referent->getTarget());
1797 return dbg->wrapDebuggeeObject(cx, target, result);
1800 /* static */
1801 bool DebuggerObject::getBoundThis(JSContext* cx, Handle<DebuggerObject*> object,
1802 MutableHandleValue result) {
1803 MOZ_ASSERT(object->isBoundFunction());
1805 Rooted<BoundFunctionObject*> referent(
1806 cx, &object->referent()->as<BoundFunctionObject>());
1807 Debugger* dbg = object->owner();
1809 result.set(referent->getBoundThis());
1810 return dbg->wrapDebuggeeValue(cx, result);
1813 /* static */
1814 bool DebuggerObject::getBoundArguments(JSContext* cx,
1815 Handle<DebuggerObject*> object,
1816 MutableHandle<ValueVector> result) {
1817 MOZ_ASSERT(object->isBoundFunction());
1819 Rooted<BoundFunctionObject*> referent(
1820 cx, &object->referent()->as<BoundFunctionObject>());
1821 Debugger* dbg = object->owner();
1823 size_t length = referent->numBoundArgs();
1824 if (!result.resize(length)) {
1825 return false;
1827 for (size_t i = 0; i < length; i++) {
1828 result[i].set(referent->getBoundArg(i));
1829 if (!dbg->wrapDebuggeeValue(cx, result[i])) {
1830 return false;
1833 return true;
1836 /* static */
1837 SavedFrame* Debugger::getObjectAllocationSite(JSObject& obj) {
1838 JSObject* metadata = GetAllocationMetadata(&obj);
1839 if (!metadata) {
1840 return nullptr;
1843 MOZ_ASSERT(!metadata->is<WrapperObject>());
1844 return metadata->is<SavedFrame>() ? &metadata->as<SavedFrame>() : nullptr;
1847 /* static */
1848 bool DebuggerObject::getAllocationSite(JSContext* cx,
1849 Handle<DebuggerObject*> object,
1850 MutableHandleObject result) {
1851 RootedObject referent(cx, object->referent());
1853 RootedObject allocSite(cx, Debugger::getObjectAllocationSite(*referent));
1854 if (!cx->compartment()->wrap(cx, &allocSite)) {
1855 return false;
1858 result.set(allocSite);
1859 return true;
1862 /* static */
1863 bool DebuggerObject::getErrorReport(JSContext* cx, HandleObject maybeError,
1864 JSErrorReport*& report) {
1865 JSObject* obj = maybeError;
1866 if (IsCrossCompartmentWrapper(obj)) {
1867 /* We only care about Error objects, so CheckedUnwrapStatic is OK. */
1868 obj = CheckedUnwrapStatic(obj);
1871 if (!obj) {
1872 ReportAccessDenied(cx);
1873 return false;
1876 if (!obj->is<ErrorObject>()) {
1877 report = nullptr;
1878 return true;
1881 report = obj->as<ErrorObject>().getErrorReport();
1882 return true;
1885 /* static */
1886 bool DebuggerObject::getErrorMessageName(JSContext* cx,
1887 Handle<DebuggerObject*> object,
1888 MutableHandleString result) {
1889 RootedObject referent(cx, object->referent());
1890 JSErrorReport* report;
1891 if (!getErrorReport(cx, referent, report)) {
1892 return false;
1895 if (!report || !report->errorMessageName) {
1896 result.set(nullptr);
1897 return true;
1900 RootedString str(cx, JS_NewStringCopyZ(cx, report->errorMessageName));
1901 if (!str) {
1902 return false;
1904 result.set(str);
1905 return true;
1908 /* static */
1909 bool DebuggerObject::getErrorNotes(JSContext* cx,
1910 Handle<DebuggerObject*> object,
1911 MutableHandleValue result) {
1912 RootedObject referent(cx, object->referent());
1913 JSErrorReport* report;
1914 if (!getErrorReport(cx, referent, report)) {
1915 return false;
1918 if (!report) {
1919 result.setUndefined();
1920 return true;
1923 RootedObject errorNotesArray(cx, CreateErrorNotesArray(cx, report));
1924 if (!errorNotesArray) {
1925 return false;
1928 if (!cx->compartment()->wrap(cx, &errorNotesArray)) {
1929 return false;
1931 result.setObject(*errorNotesArray);
1932 return true;
1935 /* static */
1936 bool DebuggerObject::getErrorLineNumber(JSContext* cx,
1937 Handle<DebuggerObject*> object,
1938 MutableHandleValue result) {
1939 RootedObject referent(cx, object->referent());
1940 JSErrorReport* report;
1941 if (!getErrorReport(cx, referent, report)) {
1942 return false;
1945 if (!report) {
1946 result.setUndefined();
1947 return true;
1950 result.setNumber(report->lineno);
1951 return true;
1954 /* static */
1955 bool DebuggerObject::getErrorColumnNumber(JSContext* cx,
1956 Handle<DebuggerObject*> object,
1957 MutableHandleValue result) {
1958 RootedObject referent(cx, object->referent());
1959 JSErrorReport* report;
1960 if (!getErrorReport(cx, referent, report)) {
1961 return false;
1964 if (!report) {
1965 result.setUndefined();
1966 return true;
1969 result.setNumber(report->column.oneOriginValue());
1970 return true;
1973 /* static */
1974 bool DebuggerObject::getPromiseValue(JSContext* cx,
1975 Handle<DebuggerObject*> object,
1976 MutableHandleValue result) {
1977 MOZ_ASSERT(object->promiseState() == JS::PromiseState::Fulfilled);
1979 result.set(object->promise()->value());
1980 return object->owner()->wrapDebuggeeValue(cx, result);
1983 /* static */
1984 bool DebuggerObject::getPromiseReason(JSContext* cx,
1985 Handle<DebuggerObject*> object,
1986 MutableHandleValue result) {
1987 MOZ_ASSERT(object->promiseState() == JS::PromiseState::Rejected);
1989 result.set(object->promise()->reason());
1990 return object->owner()->wrapDebuggeeValue(cx, result);
1993 /* static */
1994 bool DebuggerObject::isExtensible(JSContext* cx, Handle<DebuggerObject*> object,
1995 bool& result) {
1996 RootedObject referent(cx, object->referent());
1998 Maybe<AutoRealm> ar;
1999 EnterDebuggeeObjectRealm(cx, ar, referent);
2001 ErrorCopier ec(ar);
2002 return IsExtensible(cx, referent, &result);
2005 /* static */
2006 bool DebuggerObject::isSealed(JSContext* cx, Handle<DebuggerObject*> object,
2007 bool& result) {
2008 RootedObject referent(cx, object->referent());
2010 Maybe<AutoRealm> ar;
2011 EnterDebuggeeObjectRealm(cx, ar, referent);
2013 ErrorCopier ec(ar);
2014 return TestIntegrityLevel(cx, referent, IntegrityLevel::Sealed, &result);
2017 /* static */
2018 bool DebuggerObject::isFrozen(JSContext* cx, Handle<DebuggerObject*> object,
2019 bool& result) {
2020 RootedObject referent(cx, object->referent());
2022 Maybe<AutoRealm> ar;
2023 EnterDebuggeeObjectRealm(cx, ar, referent);
2025 ErrorCopier ec(ar);
2026 return TestIntegrityLevel(cx, referent, IntegrityLevel::Frozen, &result);
2029 /* static */
2030 bool DebuggerObject::getPrototypeOf(JSContext* cx,
2031 Handle<DebuggerObject*> object,
2032 MutableHandle<DebuggerObject*> result) {
2033 RootedObject referent(cx, object->referent());
2034 Debugger* dbg = object->owner();
2036 RootedObject proto(cx);
2038 Maybe<AutoRealm> ar;
2039 EnterDebuggeeObjectRealm(cx, ar, referent);
2040 if (!GetPrototype(cx, referent, &proto)) {
2041 return false;
2045 return dbg->wrapNullableDebuggeeObject(cx, proto, result);
2048 /* static */
2049 bool DebuggerObject::getOwnPropertyNames(JSContext* cx,
2050 Handle<DebuggerObject*> object,
2051 MutableHandleIdVector result) {
2052 MOZ_ASSERT(result.empty());
2054 RootedObject referent(cx, object->referent());
2056 Maybe<AutoRealm> ar;
2057 EnterDebuggeeObjectRealm(cx, ar, referent);
2059 ErrorCopier ec(ar);
2060 if (!GetPropertyKeys(cx, referent, JSITER_OWNONLY | JSITER_HIDDEN,
2061 result)) {
2062 return false;
2066 for (size_t i = 0; i < result.length(); i++) {
2067 cx->markId(result[i]);
2070 return true;
2073 /* static */
2074 bool DebuggerObject::getOwnPropertyNamesLength(JSContext* cx,
2075 Handle<DebuggerObject*> object,
2076 size_t* result) {
2077 RootedObject referent(cx, object->referent());
2079 RootedIdVector ids(cx);
2081 Maybe<AutoRealm> ar;
2082 EnterDebuggeeObjectRealm(cx, ar, referent);
2084 ErrorCopier ec(ar);
2085 if (!GetPropertyKeys(cx, referent, JSITER_OWNONLY | JSITER_HIDDEN, &ids)) {
2086 return false;
2090 *result = ids.length();
2091 return true;
2094 static bool GetSymbolPropertyKeys(JSContext* cx, Handle<DebuggerObject*> object,
2095 JS::MutableHandleIdVector props,
2096 bool includePrivate) {
2097 RootedObject referent(cx, object->referent());
2100 Maybe<AutoRealm> ar;
2101 EnterDebuggeeObjectRealm(cx, ar, referent);
2103 ErrorCopier ec(ar);
2105 unsigned flags =
2106 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS | JSITER_SYMBOLSONLY;
2107 if (includePrivate) {
2108 flags = flags | JSITER_PRIVATE;
2110 if (!GetPropertyKeys(cx, referent, flags, props)) {
2111 return false;
2115 return true;
2118 /* static */
2119 bool DebuggerObject::getOwnPropertySymbols(JSContext* cx,
2120 Handle<DebuggerObject*> object,
2121 MutableHandleIdVector result) {
2122 MOZ_ASSERT(result.empty());
2124 if (!GetSymbolPropertyKeys(cx, object, result, false)) {
2125 return false;
2128 for (size_t i = 0; i < result.length(); i++) {
2129 cx->markAtom(result[i].toSymbol());
2132 return true;
2135 /* static */
2136 bool DebuggerObject::getOwnPrivateProperties(JSContext* cx,
2137 Handle<DebuggerObject*> object,
2138 MutableHandleIdVector result) {
2139 MOZ_ASSERT(result.empty());
2141 if (!GetSymbolPropertyKeys(cx, object, result, true)) {
2142 return false;
2145 result.eraseIf([](PropertyKey key) {
2146 if (!key.isPrivateName()) {
2147 return true;
2149 // Private *methods* create a Private Brand, a special private name
2150 // stamped onto the symbol, to indicate it is possible to execute private
2151 // methods from the class on this object. We don't want to return such
2152 // items here, so we check if we're dealing with a private property, e.g.
2153 // the Symbol description starts with a "#" character.
2154 JSAtom* privateDescription = key.toSymbol()->description();
2155 if (privateDescription->length() == 0) {
2156 return true;
2158 char16_t firstChar = privateDescription->latin1OrTwoByteChar(0);
2159 return firstChar != '#';
2162 for (size_t i = 0; i < result.length(); i++) {
2163 cx->markAtom(result[i].toSymbol());
2166 return true;
2169 /* static */
2170 bool DebuggerObject::getOwnPropertyDescriptor(
2171 JSContext* cx, Handle<DebuggerObject*> object, HandleId id,
2172 MutableHandle<Maybe<PropertyDescriptor>> desc_) {
2173 RootedObject referent(cx, object->referent());
2174 Debugger* dbg = object->owner();
2176 // Bug: This can cause the debuggee to run!
2178 Maybe<AutoRealm> ar;
2179 EnterDebuggeeObjectRealm(cx, ar, referent);
2181 cx->markId(id);
2183 ErrorCopier ec(ar);
2184 if (!GetOwnPropertyDescriptor(cx, referent, id, desc_)) {
2185 return false;
2189 if (desc_.isSome()) {
2190 Rooted<PropertyDescriptor> desc(cx, *desc_);
2192 if (desc.hasValue()) {
2193 // Rewrap the debuggee values in desc for the debugger.
2194 if (!dbg->wrapDebuggeeValue(cx, desc.value())) {
2195 return false;
2198 if (desc.hasGetter()) {
2199 RootedValue get(cx, ObjectOrNullValue(desc.getter()));
2200 if (!dbg->wrapDebuggeeValue(cx, &get)) {
2201 return false;
2203 desc.setGetter(get.toObjectOrNull());
2205 if (desc.hasSetter()) {
2206 RootedValue set(cx, ObjectOrNullValue(desc.setter()));
2207 if (!dbg->wrapDebuggeeValue(cx, &set)) {
2208 return false;
2210 desc.setSetter(set.toObjectOrNull());
2213 desc_.set(mozilla::Some(desc.get()));
2216 return true;
2219 /* static */
2220 bool DebuggerObject::preventExtensions(JSContext* cx,
2221 Handle<DebuggerObject*> object) {
2222 RootedObject referent(cx, object->referent());
2224 Maybe<AutoRealm> ar;
2225 EnterDebuggeeObjectRealm(cx, ar, referent);
2227 ErrorCopier ec(ar);
2228 return PreventExtensions(cx, referent);
2231 /* static */
2232 bool DebuggerObject::seal(JSContext* cx, Handle<DebuggerObject*> object) {
2233 RootedObject referent(cx, object->referent());
2235 Maybe<AutoRealm> ar;
2236 EnterDebuggeeObjectRealm(cx, ar, referent);
2238 ErrorCopier ec(ar);
2239 return SetIntegrityLevel(cx, referent, IntegrityLevel::Sealed);
2242 /* static */
2243 bool DebuggerObject::freeze(JSContext* cx, Handle<DebuggerObject*> object) {
2244 RootedObject referent(cx, object->referent());
2246 Maybe<AutoRealm> ar;
2247 EnterDebuggeeObjectRealm(cx, ar, referent);
2249 ErrorCopier ec(ar);
2250 return SetIntegrityLevel(cx, referent, IntegrityLevel::Frozen);
2253 /* static */
2254 bool DebuggerObject::defineProperty(JSContext* cx,
2255 Handle<DebuggerObject*> object, HandleId id,
2256 Handle<PropertyDescriptor> desc_) {
2257 RootedObject referent(cx, object->referent());
2258 Debugger* dbg = object->owner();
2260 Rooted<PropertyDescriptor> desc(cx, desc_);
2261 if (!dbg->unwrapPropertyDescriptor(cx, referent, &desc)) {
2262 return false;
2264 JS_TRY_OR_RETURN_FALSE(cx, CheckPropertyDescriptorAccessors(cx, desc));
2266 Maybe<AutoRealm> ar;
2267 EnterDebuggeeObjectRealm(cx, ar, referent);
2269 if (!cx->compartment()->wrap(cx, &desc)) {
2270 return false;
2272 cx->markId(id);
2274 ErrorCopier ec(ar);
2275 return DefineProperty(cx, referent, id, desc);
2278 /* static */
2279 bool DebuggerObject::defineProperties(JSContext* cx,
2280 Handle<DebuggerObject*> object,
2281 Handle<IdVector> ids,
2282 Handle<PropertyDescriptorVector> descs_) {
2283 RootedObject referent(cx, object->referent());
2284 Debugger* dbg = object->owner();
2286 Rooted<PropertyDescriptorVector> descs(cx, PropertyDescriptorVector(cx));
2287 if (!descs.append(descs_.begin(), descs_.end())) {
2288 return false;
2290 for (size_t i = 0; i < descs.length(); i++) {
2291 if (!dbg->unwrapPropertyDescriptor(cx, referent, descs[i])) {
2292 return false;
2294 JS_TRY_OR_RETURN_FALSE(cx, CheckPropertyDescriptorAccessors(cx, descs[i]));
2297 Maybe<AutoRealm> ar;
2298 EnterDebuggeeObjectRealm(cx, ar, referent);
2300 for (size_t i = 0; i < descs.length(); i++) {
2301 if (!cx->compartment()->wrap(cx, descs[i])) {
2302 return false;
2304 cx->markId(ids[i]);
2307 ErrorCopier ec(ar);
2308 for (size_t i = 0; i < descs.length(); i++) {
2309 if (!DefineProperty(cx, referent, ids[i], descs[i])) {
2310 return false;
2314 return true;
2317 /* static */
2318 bool DebuggerObject::deleteProperty(JSContext* cx,
2319 Handle<DebuggerObject*> object, HandleId id,
2320 ObjectOpResult& result) {
2321 RootedObject referent(cx, object->referent());
2323 Maybe<AutoRealm> ar;
2324 EnterDebuggeeObjectRealm(cx, ar, referent);
2326 cx->markId(id);
2328 ErrorCopier ec(ar);
2329 return DeleteProperty(cx, referent, id, result);
2332 /* static */
2333 Result<Completion> DebuggerObject::getProperty(JSContext* cx,
2334 Handle<DebuggerObject*> object,
2335 HandleId id,
2336 HandleValue receiver_) {
2337 RootedObject referent(cx, object->referent());
2338 Debugger* dbg = object->owner();
2340 // Unwrap Debugger.Objects. This happens in the debugger's compartment since
2341 // that is where any exceptions must be reported.
2342 RootedValue receiver(cx, receiver_);
2343 if (!dbg->unwrapDebuggeeValue(cx, &receiver)) {
2344 return cx->alreadyReportedError();
2347 // Enter the debuggee compartment and rewrap all input value for that
2348 // compartment. (Rewrapping always takes place in the destination
2349 // compartment.)
2350 Maybe<AutoRealm> ar;
2351 EnterDebuggeeObjectRealm(cx, ar, referent);
2352 if (!cx->compartment()->wrap(cx, &referent) ||
2353 !cx->compartment()->wrap(cx, &receiver)) {
2354 return cx->alreadyReportedError();
2356 cx->markId(id);
2358 LeaveDebuggeeNoExecute nnx(cx);
2360 RootedValue result(cx);
2361 bool ok = GetProperty(cx, referent, receiver, id, &result);
2362 return Completion::fromJSResult(cx, ok, result);
2365 /* static */
2366 Result<Completion> DebuggerObject::setProperty(JSContext* cx,
2367 Handle<DebuggerObject*> object,
2368 HandleId id, HandleValue value_,
2369 HandleValue receiver_) {
2370 RootedObject referent(cx, object->referent());
2371 Debugger* dbg = object->owner();
2373 // Unwrap Debugger.Objects. This happens in the debugger's compartment since
2374 // that is where any exceptions must be reported.
2375 RootedValue value(cx, value_);
2376 RootedValue receiver(cx, receiver_);
2377 if (!dbg->unwrapDebuggeeValue(cx, &value) ||
2378 !dbg->unwrapDebuggeeValue(cx, &receiver)) {
2379 return cx->alreadyReportedError();
2382 // Enter the debuggee compartment and rewrap all input value for that
2383 // compartment. (Rewrapping always takes place in the destination
2384 // compartment.)
2385 Maybe<AutoRealm> ar;
2386 EnterDebuggeeObjectRealm(cx, ar, referent);
2387 if (!cx->compartment()->wrap(cx, &referent) ||
2388 !cx->compartment()->wrap(cx, &value) ||
2389 !cx->compartment()->wrap(cx, &receiver)) {
2390 return cx->alreadyReportedError();
2392 cx->markId(id);
2394 LeaveDebuggeeNoExecute nnx(cx);
2396 ObjectOpResult opResult;
2397 bool ok = SetProperty(cx, referent, id, value, receiver, opResult);
2399 return Completion::fromJSResult(cx, ok, BooleanValue(ok && opResult.ok()));
2402 /* static */
2403 Maybe<Completion> DebuggerObject::call(JSContext* cx,
2404 Handle<DebuggerObject*> object,
2405 HandleValue thisv_,
2406 Handle<ValueVector> args) {
2407 RootedObject referent(cx, object->referent());
2408 Debugger* dbg = object->owner();
2410 if (!referent->isCallable()) {
2411 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2412 JSMSG_INCOMPATIBLE_PROTO, "Debugger.Object",
2413 "call", referent->getClass()->name);
2414 return Nothing();
2417 RootedValue calleev(cx, ObjectValue(*referent));
2419 // Unwrap Debugger.Objects. This happens in the debugger's compartment since
2420 // that is where any exceptions must be reported.
2421 RootedValue thisv(cx, thisv_);
2422 if (!dbg->unwrapDebuggeeValue(cx, &thisv)) {
2423 return Nothing();
2425 Rooted<ValueVector> args2(cx, ValueVector(cx));
2426 if (!args2.append(args.begin(), args.end())) {
2427 return Nothing();
2429 for (size_t i = 0; i < args2.length(); ++i) {
2430 if (!dbg->unwrapDebuggeeValue(cx, args2[i])) {
2431 return Nothing();
2435 // Enter the debuggee compartment and rewrap all input value for that
2436 // compartment. (Rewrapping always takes place in the destination
2437 // compartment.)
2438 Maybe<AutoRealm> ar;
2439 EnterDebuggeeObjectRealm(cx, ar, referent);
2440 if (!cx->compartment()->wrap(cx, &calleev) ||
2441 !cx->compartment()->wrap(cx, &thisv)) {
2442 return Nothing();
2444 for (size_t i = 0; i < args2.length(); ++i) {
2445 if (!cx->compartment()->wrap(cx, args2[i])) {
2446 return Nothing();
2450 // Note whether we are in an evaluation that might invoke the OnNativeCall
2451 // hook, so that the JITs will be disabled.
2452 Maybe<AutoNoteExclusiveDebuggerOnEval> noteEvaluation;
2453 if (dbg->isExclusiveDebuggerOnEval()) {
2454 noteEvaluation.emplace(cx, dbg);
2457 // Call the function.
2458 LeaveDebuggeeNoExecute nnx(cx);
2460 RootedValue result(cx);
2461 bool ok;
2463 InvokeArgs invokeArgs(cx);
2465 ok = invokeArgs.init(cx, args2.length());
2466 if (ok) {
2467 for (size_t i = 0; i < args2.length(); ++i) {
2468 invokeArgs[i].set(args2[i]);
2471 ok = js::Call(cx, calleev, thisv, invokeArgs, &result);
2475 Rooted<Completion> completion(cx, Completion::fromJSResult(cx, ok, result));
2476 ar.reset();
2477 return Some(std::move(completion.get()));
2480 /* static */
2481 bool DebuggerObject::forceLexicalInitializationByName(
2482 JSContext* cx, Handle<DebuggerObject*> object, HandleId id, bool& result) {
2483 if (!id.isString()) {
2484 JS_ReportErrorNumberASCII(
2485 cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
2486 "Debugger.Object.prototype.forceLexicalInitializationByName", "string",
2487 InformalValueTypeName(IdToValue(id)));
2488 return false;
2491 MOZ_ASSERT(object->isGlobal());
2493 Rooted<GlobalObject*> referent(cx, &object->referent()->as<GlobalObject>());
2495 // Shape::search can end up allocating a new BaseShape in Shape::cachify so
2496 // we need to be in the right compartment here.
2497 Maybe<AutoRealm> ar;
2498 EnterDebuggeeObjectRealm(cx, ar, referent);
2500 RootedObject globalLexical(cx, &referent->lexicalEnvironment());
2501 RootedObject pobj(cx);
2502 PropertyResult prop;
2503 if (!LookupProperty(cx, globalLexical, id, &pobj, &prop)) {
2504 return false;
2507 result = false;
2508 if (prop.isFound()) {
2509 MOZ_ASSERT(prop.isNativeProperty());
2510 PropertyInfo propInfo = prop.propertyInfo();
2511 Value v = globalLexical->as<NativeObject>().getSlot(propInfo.slot());
2512 if (propInfo.isDataProperty() && v.isMagic() &&
2513 v.whyMagic() == JS_UNINITIALIZED_LEXICAL) {
2514 globalLexical->as<NativeObject>().setSlot(propInfo.slot(),
2515 UndefinedValue());
2516 result = true;
2520 return true;
2523 /* static */
2524 Result<Completion> DebuggerObject::executeInGlobal(
2525 JSContext* cx, Handle<DebuggerObject*> object,
2526 mozilla::Range<const char16_t> chars, HandleObject bindings,
2527 const EvalOptions& options) {
2528 MOZ_ASSERT(object->isGlobal());
2530 Rooted<GlobalObject*> referent(cx, &object->referent()->as<GlobalObject>());
2531 Debugger* dbg = object->owner();
2533 RootedObject globalLexical(cx, &referent->lexicalEnvironment());
2534 return DebuggerGenericEval(cx, chars, bindings, options, dbg, globalLexical,
2535 nullptr);
2538 /* static */
2539 bool DebuggerObject::makeDebuggeeValue(JSContext* cx,
2540 Handle<DebuggerObject*> object,
2541 HandleValue value_,
2542 MutableHandleValue result) {
2543 RootedObject referent(cx, object->referent());
2544 Debugger* dbg = object->owner();
2546 RootedValue value(cx, value_);
2548 // Non-objects are already debuggee values.
2549 if (value.isObject()) {
2550 // Enter this Debugger.Object's referent's compartment, and wrap the
2551 // argument as appropriate for references from there.
2553 Maybe<AutoRealm> ar;
2554 EnterDebuggeeObjectRealm(cx, ar, referent);
2555 if (!cx->compartment()->wrap(cx, &value)) {
2556 return false;
2560 // Back in the debugger's compartment, produce a new Debugger.Object
2561 // instance referring to the wrapped argument.
2562 if (!dbg->wrapDebuggeeValue(cx, &value)) {
2563 return false;
2567 result.set(value);
2568 return true;
2571 static JSFunction* EnsureNativeFunction(const Value& value) {
2572 if (!value.isObject() || !value.toObject().is<JSFunction>()) {
2573 return nullptr;
2576 JSFunction* fun = &value.toObject().as<JSFunction>();
2577 if (!fun->isNativeFun()) {
2578 return nullptr;
2581 return fun;
2584 static JSAtom* MaybeGetSelfHostedFunctionName(const Value& v) {
2585 if (!v.isObject() || !v.toObject().is<JSFunction>()) {
2586 return nullptr;
2589 JSFunction* fun = &v.toObject().as<JSFunction>();
2590 if (!fun->isSelfHostedBuiltin()) {
2591 return nullptr;
2594 return GetClonedSelfHostedFunctionName(fun);
2597 static bool IsSameNative(JSFunction* a, JSFunction* b,
2598 DebuggerObject::CheckJitInfo checkJitInfo) {
2599 if (a->native() != b->native()) {
2600 return false;
2603 if (checkJitInfo == DebuggerObject::CheckJitInfo::No) {
2604 return true;
2607 // Both function should agree with the existence of JitInfo.
2609 if (a->hasJitInfo() != b->hasJitInfo()) {
2610 return false;
2613 if (!a->hasJitInfo()) {
2614 return true;
2617 if (a->jitInfo() == b->jitInfo()) {
2618 return true;
2621 return false;
2624 /* static */
2625 bool DebuggerObject::isSameNative(JSContext* cx, Handle<DebuggerObject*> object,
2626 HandleValue value, CheckJitInfo checkJitInfo,
2627 MutableHandleValue result) {
2628 RootedValue referentValue(cx, ObjectValue(*object->referent()));
2630 RootedValue nonCCWValue(
2631 cx, value.isObject() ? ObjectValue(*UncheckedUnwrap(&value.toObject()))
2632 : value);
2634 RootedFunction fun(cx, EnsureNativeFunction(nonCCWValue));
2635 if (!fun) {
2636 Rooted<JSAtom*> selfHostedName(cx,
2637 MaybeGetSelfHostedFunctionName(nonCCWValue));
2638 if (!selfHostedName) {
2639 JS_ReportErrorASCII(cx, "Need native function");
2640 return false;
2643 result.setBoolean(selfHostedName ==
2644 MaybeGetSelfHostedFunctionName(referentValue));
2645 return true;
2648 RootedFunction referentFun(cx, EnsureNativeFunction(referentValue));
2650 result.setBoolean(referentFun &&
2651 IsSameNative(referentFun, fun, checkJitInfo));
2652 return true;
2655 static bool IsNativeGetterWithJitInfo(JSFunction* fun) {
2656 return fun->isNativeFun() && fun->hasJitInfo() &&
2657 fun->jitInfo()->type() == JSJitInfo::Getter;
2660 /* static */
2661 bool DebuggerObject::isNativeGetterWithJitInfo(JSContext* cx,
2662 Handle<DebuggerObject*> object,
2663 MutableHandleValue result) {
2664 RootedValue referentValue(cx, ObjectValue(*object->referent()));
2665 RootedFunction referentFun(cx, EnsureNativeFunction(referentValue));
2666 result.setBoolean(referentFun && IsNativeGetterWithJitInfo(referentFun));
2667 return true;
2670 /* static */
2671 bool DebuggerObject::unsafeDereference(JSContext* cx,
2672 Handle<DebuggerObject*> object,
2673 MutableHandleObject result) {
2674 RootedObject referent(cx, object->referent());
2676 if (!cx->compartment()->wrap(cx, &referent)) {
2677 return false;
2680 // Wrapping should return the WindowProxy.
2681 MOZ_ASSERT(!IsWindow(referent));
2683 result.set(referent);
2684 return true;
2687 /* static */
2688 bool DebuggerObject::unwrap(JSContext* cx, Handle<DebuggerObject*> object,
2689 MutableHandle<DebuggerObject*> result) {
2690 RootedObject referent(cx, object->referent());
2691 Debugger* dbg = object->owner();
2693 RootedObject unwrapped(cx, UnwrapOneCheckedStatic(referent));
2695 // Don't allow unwrapping to create a D.O whose referent is in an
2696 // invisible-to-Debugger compartment. (If our referent is a *wrapper* to such,
2697 // and the wrapper is in a visible compartment, that's fine.)
2698 if (unwrapped && unwrapped->compartment()->invisibleToDebugger()) {
2699 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2700 JSMSG_DEBUG_INVISIBLE_COMPARTMENT);
2701 return false;
2704 return dbg->wrapNullableDebuggeeObject(cx, unwrapped, result);
2707 /* static */
2708 bool DebuggerObject::requireGlobal(JSContext* cx,
2709 Handle<DebuggerObject*> object) {
2710 if (!object->isGlobal()) {
2711 RootedObject referent(cx, object->referent());
2713 const char* isWrapper = "";
2714 const char* isWindowProxy = "";
2716 // Help the poor programmer by pointing out wrappers around globals...
2717 if (referent->is<WrapperObject>()) {
2718 referent = js::UncheckedUnwrap(referent);
2719 isWrapper = "a wrapper around ";
2722 // ... and WindowProxies around Windows.
2723 if (IsWindowProxy(referent)) {
2724 referent = ToWindowIfWindowProxy(referent);
2725 isWindowProxy = "a WindowProxy referring to ";
2728 RootedValue dbgobj(cx, ObjectValue(*object));
2729 if (referent->is<GlobalObject>()) {
2730 ReportValueError(cx, JSMSG_DEBUG_WRAPPER_IN_WAY, JSDVG_SEARCH_STACK,
2731 dbgobj, nullptr, isWrapper, isWindowProxy);
2732 } else {
2733 ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK, dbgobj,
2734 nullptr, "a global object");
2736 return false;
2739 return true;
2742 /* static */
2743 bool DebuggerObject::requirePromise(JSContext* cx,
2744 Handle<DebuggerObject*> object) {
2745 RootedObject referent(cx, object->referent());
2747 if (IsCrossCompartmentWrapper(referent)) {
2748 /* We only care about promises, so CheckedUnwrapStatic is OK. */
2749 referent = CheckedUnwrapStatic(referent);
2750 if (!referent) {
2751 ReportAccessDenied(cx);
2752 return false;
2756 if (!referent->is<PromiseObject>()) {
2757 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2758 JSMSG_NOT_EXPECTED_TYPE, "Debugger", "Promise",
2759 object->getClass()->name);
2760 return false;
2763 return true;
2766 /* static */
2767 bool DebuggerObject::getScriptedProxyTarget(
2768 JSContext* cx, Handle<DebuggerObject*> object,
2769 MutableHandle<DebuggerObject*> result) {
2770 MOZ_ASSERT(object->isScriptedProxy());
2771 RootedObject referent(cx, object->referent());
2772 Debugger* dbg = object->owner();
2773 RootedObject unwrapped(cx, js::GetProxyTargetObject(referent));
2775 return dbg->wrapNullableDebuggeeObject(cx, unwrapped, result);
2778 /* static */
2779 bool DebuggerObject::getScriptedProxyHandler(
2780 JSContext* cx, Handle<DebuggerObject*> object,
2781 MutableHandle<DebuggerObject*> result) {
2782 MOZ_ASSERT(object->isScriptedProxy());
2783 RootedObject referent(cx, object->referent());
2784 Debugger* dbg = object->owner();
2785 RootedObject unwrapped(cx, ScriptedProxyHandler::handlerObject(referent));
2786 return dbg->wrapNullableDebuggeeObject(cx, unwrapped, result);