Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  Object.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "debugger/Object-inl.h"

#include "mozilla/Maybe.h"   // for Maybe, Nothing, Some
#include "mozilla/Range.h"   // for Range
#include "mozilla/Result.h"  // for Result
#include "mozilla/Vector.h"  // for Vector

#include <algorithm>
#include <string.h>     // for size_t, strlen
#include <type_traits>  // for remove_reference<>::type
#include <utility>      // for move

#include "jsapi.h"  // for CallArgs, RootedObject, Rooted

#include "builtin/Array.h"       // for NewDenseCopiedArray
#include "builtin/Promise.h"     // for PromiseReactionRecordBuilder
#include "debugger/Debugger.h"   // for Completion, Debugger
#include "debugger/Frame.h"      // for DebuggerFrame
#include "debugger/NoExecute.h"  // for LeaveDebuggeeNoExecute
#include "debugger/Script.h"     // for DebuggerScript
#include "debugger/Source.h"     // for DebuggerSource
#include "gc/Tracer.h"        // for TraceManuallyBarrieredCrossCompartmentEdge
#include "js/ColumnNumber.h"  // JS::ColumnNumberOneOrigin
#include "js/CompilationAndEvaluation.h"  //  for Compile
#include "js/Conversions.h"               // for ToObject
#include "js/experimental/JitInfo.h"      // for JSJitInfo
#include "js/friend/ErrorMessages.h"      // for GetErrorMessage, JSMSG_*
#include "js/friend/WindowProxy.h"  // for IsWindow, IsWindowProxy, ToWindowIfWindowProxy
#include "js/HeapAPI.h"             // for IsInsideNursery
#include "js/Promise.h"             // for PromiseState
#include "js/PropertyAndElement.h"       // for JS_GetProperty
#include "js/Proxy.h"                    // for PropertyDescriptor
#include "js/SourceText.h"               // for SourceText
#include "js/StableStringChars.h"        // for AutoStableStringChars
#include "js/String.h"                   // for JS::StringHasLatin1Chars
#include "proxy/ScriptedProxyHandler.h"  // for ScriptedProxyHandler
#include "vm/ArgumentsObject.h"          // for ARGS_LENGTH_MAX
#include "vm/ArrayObject.h"              // for ArrayObject
#include "vm/AsyncFunction.h"            // for AsyncGeneratorObject
#include "vm/AsyncIteration.h"           // for AsyncFunctionGeneratorObject
#include "vm/BoundFunctionObject.h"      // for BoundFunctionObject
#include "vm/BytecodeUtil.h"             // for JSDVG_SEARCH_STACK
#include "vm/Compartment.h"              // for Compartment
#include "vm/EnvironmentObject.h"        // for GetDebugEnvironmentForFunction
#include "vm/ErrorObject.h"              // for JSObject::is, ErrorObject
#include "vm/GeneratorObject.h"          // for AbstractGeneratorObject
#include "vm/GlobalObject.h"             // for JSObject::is, GlobalObject
#include "vm/Interpreter.h"              // for Call
#include "vm/JSAtomUtils.h"              // for Atomize, AtomizeString
#include "vm/JSContext.h"                // for JSContext, ReportValueError
#include "vm/JSFunction.h"               // for JSFunction
#include "vm/JSObject.h"                 // for GenericObject, NewObjectKind
#include "vm/JSScript.h"                 // for JSScript
#include "vm/NativeObject.h"             // for NativeObject, JSObject::is
#include "vm/ObjectOperations.h"         // for DefineProperty
#include "vm/PlainObject.h"              // for js::PlainObject
#include "vm/PromiseObject.h"            // for js::PromiseObject
#include "vm/Realm.h"                    // for AutoRealm, ErrorCopier, Realm
#include "vm/Runtime.h"                  // for JSAtomState
#include "vm/SavedFrame.h"               // for SavedFrame
#include "vm/Scope.h"                    // for PositionalFormalParameterIter
#include "vm/SelfHosting.h"              // for GetClonedSelfHostedFunctionName
#include "vm/Shape.h"                    // for Shape
#include "vm/Stack.h"                    // for InvokeArgs
#include "vm/StringType.h"               // for JSAtom, PropertyName
#include "vm/WrapperObject.h"            // for JSObject::is, WrapperObject

#include "gc/StableCellHasher-inl.h"
#include "vm/Compartment-inl.h"  // for Compartment::wrap
#include "vm/JSObject-inl.h"  // for GetObjectClassName, InitClass, NewObjectWithGivenProtoAndKind, ToPropertyKey
#include "vm/NativeObject-inl.h"      // for NativeObject::global
#include "vm/ObjectOperations-inl.h"  // for DeleteProperty, GetProperty
#include "vm/Realm-inl.h"             // for AutoRealm::AutoRealm

using namespace js;

using JS::AutoStableStringChars;
using mozilla::Maybe;
using mozilla::Nothing;
using mozilla::Some;

const JSClassOps DebuggerObject::classOps_ = {
    nullptr,                          // addProperty
    nullptr,                          // delProperty
    nullptr,                          // enumerate
    nullptr,                          // newEnumerate
    nullptr,                          // resolve
    nullptr,                          // mayResolve
    nullptr,                          // finalize
    nullptr,                          // call
    nullptr,                          // construct
    CallTraceMethod<DebuggerObject>,  // trace
};

const JSClass DebuggerObject::class_ = {
    "Object",
    JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS),
    &classOps_,
};

void DebuggerObject::trace(JSTracer* trc) {
  // There is a barrier on private pointers, so the Unbarriered marking
  // is okay.
  if (JSObject* referent = maybeReferent()) {
    TraceManuallyBarrieredCrossCompartmentEdge(trc, this, &referent,
                                               "Debugger.Object referent");
    if (referent != maybeReferent()) {
      setReservedSlotGCThingAsPrivateUnbarriered(OBJECT_SLOT, referent);
    }
  }
}

static DebuggerObject* DebuggerObject_checkThis(JSContext* cx,
                                                const CallArgs& args) {
  JSObject* thisobj = RequireObject(cx, args.thisv());
  if (!thisobj) {
    return nullptr;
  }
  if (!thisobj->is<DebuggerObject>()) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_INCOMPATIBLE_PROTO, "Debugger.Object",
                              "method", thisobj->getClass()->name);
    return nullptr;
  }

  return &thisobj->as<DebuggerObject>();
}

/* static */
bool DebuggerObject::construct(JSContext* cx, unsigned argc, Value* vp) {
  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
                            "Debugger.Object");
  return false;
}

struct MOZ_STACK_CLASS DebuggerObject::CallData {
  JSContext* cx;
  const CallArgs& args;

  Handle<DebuggerObject*> object;
  RootedObject referent;

  CallData(JSContext* cx, const CallArgs& args, Handle<DebuggerObject*> obj)
      : cx(cx), args(args), object(obj), referent(cx, obj->referent()) {}

  // JSNative properties
  bool callableGetter();
  bool isBoundFunctionGetter();
  bool isArrowFunctionGetter();
  bool isAsyncFunctionGetter();
  bool isClassConstructorGetter();
  bool isGeneratorFunctionGetter();
  bool protoGetter();
  bool classGetter();
  bool nameGetter();
  bool displayNameGetter();
  bool parameterNamesGetter();
  bool scriptGetter();
  bool environmentGetter();
  bool boundTargetFunctionGetter();
  bool boundThisGetter();
  bool boundArgumentsGetter();
  bool allocationSiteGetter();
  bool isErrorGetter();
  bool errorMessageNameGetter();
  bool errorNotesGetter();
  bool errorLineNumberGetter();
  bool errorColumnNumberGetter();
  bool isProxyGetter();
  bool proxyTargetGetter();
  bool proxyHandlerGetter();
  bool isPromiseGetter();
  bool promiseStateGetter();
  bool promiseValueGetter();
  bool promiseReasonGetter();
  bool promiseLifetimeGetter();
  bool promiseTimeToResolutionGetter();
  bool promiseAllocationSiteGetter();
  bool promiseResolutionSiteGetter();
  bool promiseIDGetter();
  bool promiseDependentPromisesGetter();

  // JSNative methods
  bool isExtensibleMethod();
  bool isSealedMethod();
  bool isFrozenMethod();
  bool getPropertyMethod();
  bool setPropertyMethod();
  bool getOwnPropertyNamesMethod();
  bool getOwnPropertyNamesLengthMethod();
  bool getOwnPropertySymbolsMethod();
  bool getOwnPrivatePropertiesMethod();
  bool getOwnPropertyDescriptorMethod();
  bool preventExtensionsMethod();
  bool sealMethod();
  bool freezeMethod();
  bool definePropertyMethod();
  bool definePropertiesMethod();
  bool deletePropertyMethod();
  bool callMethod();
  bool applyMethod();
  bool asEnvironmentMethod();
  bool forceLexicalInitializationByNameMethod();
  bool executeInGlobalMethod();
  bool executeInGlobalWithBindingsMethod();
  bool createSource();
  bool makeDebuggeeValueMethod();
  bool isSameNativeMethod();
  bool isSameNativeWithJitInfoMethod();
  bool isNativeGetterWithJitInfo();
  bool unsafeDereferenceMethod();
  bool unwrapMethod();
  bool getPromiseReactionsMethod();

  using Method = bool (CallData::*)();

  template <Method MyMethod>
  static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
};

template <DebuggerObject::CallData::Method MyMethod>
/* static */
bool DebuggerObject::CallData::ToNative(JSContext* cx, unsigned argc,
                                        Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  Rooted<DebuggerObject*> obj(cx, DebuggerObject_checkThis(cx, args));
  if (!obj) {
    return false;
  }

  CallData data(cx, args, obj);
  return (data.*MyMethod)();
}

bool DebuggerObject::CallData::callableGetter() {
  args.rval().setBoolean(object->isCallable());
  return true;
}

bool DebuggerObject::CallData::isBoundFunctionGetter() {
  args.rval().setBoolean(object->isBoundFunction());
  return true;
}

bool DebuggerObject::CallData::isArrowFunctionGetter() {
  if (!object->isDebuggeeFunction()) {
    args.rval().setUndefined();
    return true;
  }

  args.rval().setBoolean(object->isArrowFunction());
  return true;
}

bool DebuggerObject::CallData::isAsyncFunctionGetter() {
  if (!object->isDebuggeeFunction()) {
    args.rval().setUndefined();
    return true;
  }

  args.rval().setBoolean(object->isAsyncFunction());
  return true;
}

bool DebuggerObject::CallData::isGeneratorFunctionGetter() {
  if (!object->isDebuggeeFunction()) {
    args.rval().setUndefined();
    return true;
  }

  args.rval().setBoolean(object->isGeneratorFunction());
  return true;
}

bool DebuggerObject::CallData::isClassConstructorGetter() {
  if (!object->isDebuggeeFunction()) {
    args.rval().setUndefined();
    return true;
  }

  args.rval().setBoolean(object->isClassConstructor());
  return true;
}

bool DebuggerObject::CallData::protoGetter() {
  Rooted<DebuggerObject*> result(cx);
  if (!DebuggerObject::getPrototypeOf(cx, object, &result)) {
    return false;
  }

  args.rval().setObjectOrNull(result);
  return true;
}

bool DebuggerObject::CallData::classGetter() {
  RootedString result(cx);
  if (!DebuggerObject::getClassName(cx, object, &result)) {
    return false;
  }

  args.rval().setString(result);
  return true;
}

bool DebuggerObject::CallData::nameGetter() {
  if (!object->isFunction() && !object->isBoundFunction()) {
    args.rval().setUndefined();
    return true;
  }

  JS::Rooted<JSAtom*> result(cx);
  if (!object->name(cx, &result)) {
    return false;
  }

  if (result) {
    args.rval().setString(result);
  } else {
    args.rval().setUndefined();
  }
  return true;
}

bool DebuggerObject::CallData::displayNameGetter() {
  if (!object->isFunction() && !object->isBoundFunction()) {
    args.rval().setUndefined();
    return true;
  }

  JS::Rooted<JSAtom*> result(cx);
  if (!object->displayName(cx, &result)) {
    return false;
  }
  if (result) {
    args.rval().setString(result);
  } else {
    args.rval().setUndefined();
  }
  return true;
}

bool DebuggerObject::CallData::parameterNamesGetter() {
  if (!object->isDebuggeeFunction()) {
    args.rval().setUndefined();
    return true;
  }

  RootedFunction referent(cx, &object->referent()->as<JSFunction>());

  ArrayObject* arr = GetFunctionParameterNamesArray(cx, referent);
  if (!arr) {
    return false;
  }

  args.rval().setObject(*arr);
  return true;
}

bool DebuggerObject::CallData::scriptGetter() {
  Debugger* dbg = object->owner();

  if (!referent->is<JSFunction>()) {
    args.rval().setUndefined();
    return true;
  }

  RootedFunction fun(cx, &referent->as<JSFunction>());
  if (!IsInterpretedNonSelfHostedFunction(fun)) {
    args.rval().setUndefined();
    return true;
  }

  RootedScript script(cx, GetOrCreateFunctionScript(cx, fun));
  if (!script) {
    return false;
  }

  // Only hand out debuggee scripts.
  if (!dbg->observesScript(script)) {
    args.rval().setNull();
    return true;
  }

  Rooted<DebuggerScript*> scriptObject(cx, dbg->wrapScript(cx, script));
  if (!scriptObject) {
    return false;
  }

  args.rval().setObject(*scriptObject);
  return true;
}

bool DebuggerObject::CallData::environmentGetter() {
  Debugger* dbg = object->owner();

  // Don't bother switching compartments just to check obj's type and get its
  // env.
  if (!referent->is<JSFunction>()) {
    args.rval().setUndefined();
    return true;
  }

  RootedFunction fun(cx, &referent->as<JSFunction>());
  if (!IsInterpretedNonSelfHostedFunction(fun)) {
    args.rval().setUndefined();
    return true;
  }

  // Only hand out environments of debuggee functions.
  if (!dbg->observesGlobal(&fun->global())) {
    args.rval().setNull();
    return true;
  }

  Rooted<Env*> env(cx);
  {
    AutoRealm ar(cx, fun);
    env = GetDebugEnvironmentForFunction(cx, fun);
    if (!env) {
      return false;
    }
  }

  return dbg->wrapEnvironment(cx, env, args.rval());
}

bool DebuggerObject::CallData::boundTargetFunctionGetter() {
  if (!object->isDebuggeeBoundFunction()) {
    args.rval().setUndefined();
    return true;
  }

  Rooted<DebuggerObject*> result(cx);
  if (!DebuggerObject::getBoundTargetFunction(cx, object, &result)) {
    return false;
  }

  args.rval().setObject(*result);
  return true;
}

bool DebuggerObject::CallData::boundThisGetter() {
  if (!object->isDebuggeeBoundFunction()) {
    args.rval().setUndefined();
    return true;
  }

  return DebuggerObject::getBoundThis(cx, object, args.rval());
}

bool DebuggerObject::CallData::boundArgumentsGetter() {
  if (!object->isDebuggeeBoundFunction()) {
    args.rval().setUndefined();
    return true;
  }

  Rooted<ValueVector> result(cx, ValueVector(cx));
  if (!DebuggerObject::getBoundArguments(cx, object, &result)) {
    return false;
  }

  RootedObject obj(cx,
                   NewDenseCopiedArray(cx, result.length(), result.begin()));
  if (!obj) {
    return false;
  }

  args.rval().setObject(*obj);
  return true;
}

bool DebuggerObject::CallData::allocationSiteGetter() {
  RootedObject result(cx);
  if (!DebuggerObject::getAllocationSite(cx, object, &result)) {
    return false;
  }

  args.rval().setObjectOrNull(result);
  return true;
}

// Returns the "name" field (see js/public/friend/ErrorNumbers.msg), which may
// be used as a unique identifier, for any error object with a JSErrorReport or
// undefined if the object has no JSErrorReport.
bool DebuggerObject::CallData::errorMessageNameGetter() {
  RootedString result(cx);
  if (!DebuggerObject::getErrorMessageName(cx, object, &result)) {
    return false;
  }

  if (result) {
    args.rval().setString(result);
  } else {
    args.rval().setUndefined();
  }
  return true;
}

bool DebuggerObject::CallData::isErrorGetter() {
  args.rval().setBoolean(object->isError());
  return true;
}

bool DebuggerObject::CallData::errorNotesGetter() {
  return DebuggerObject::getErrorNotes(cx, object, args.rval());
}

bool DebuggerObject::CallData::errorLineNumberGetter() {
  return DebuggerObject::getErrorLineNumber(cx, object, args.rval());
}

bool DebuggerObject::CallData::errorColumnNumberGetter() {
  return DebuggerObject::getErrorColumnNumber(cx, object, args.rval());
}

bool DebuggerObject::CallData::isProxyGetter() {
  args.rval().setBoolean(object->isScriptedProxy());
  return true;
}

bool DebuggerObject::CallData::proxyTargetGetter() {
  if (!object->isScriptedProxy()) {
    args.rval().setUndefined();
    return true;
  }

  Rooted<DebuggerObject*> result(cx);
  if (!DebuggerObject::getScriptedProxyTarget(cx, object, &result)) {
    return false;
  }

  args.rval().setObjectOrNull(result);
  return true;
}

bool DebuggerObject::CallData::proxyHandlerGetter() {
  if (!object->isScriptedProxy()) {
    args.rval().setUndefined();
    return true;
  }
  Rooted<DebuggerObject*> result(cx);
  if (!DebuggerObject::getScriptedProxyHandler(cx, object, &result)) {
    return false;
  }

  args.rval().setObjectOrNull(result);
  return true;
}

bool DebuggerObject::CallData::isPromiseGetter() {
  args.rval().setBoolean(object->isPromise());
  return true;
}

bool DebuggerObject::CallData::promiseStateGetter() {
  if (!DebuggerObject::requirePromise(cx, object)) {
    return false;
  }

  RootedValue result(cx);
  switch (object->promiseState()) {
    case JS::PromiseState::Pending:
      result.setString(cx->names().pending);
      break;
    case JS::PromiseState::Fulfilled:
      result.setString(cx->names().fulfilled);
      break;
    case JS::PromiseState::Rejected:
      result.setString(cx->names().rejected);
      break;
  }

  args.rval().set(result);
  return true;
}

bool DebuggerObject::CallData::promiseValueGetter() {
  if (!DebuggerObject::requirePromise(cx, object)) {
    return false;
  }

  if (object->promiseState() != JS::PromiseState::Fulfilled) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_DEBUG_PROMISE_NOT_FULFILLED);
    return false;
  }

  return DebuggerObject::getPromiseValue(cx, object, args.rval());
  ;
}

bool DebuggerObject::CallData::promiseReasonGetter() {
  if (!DebuggerObject::requirePromise(cx, object)) {
    return false;
  }

  if (object->promiseState() != JS::PromiseState::Rejected) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_DEBUG_PROMISE_NOT_REJECTED);
    return false;
  }

  return DebuggerObject::getPromiseReason(cx, object, args.rval());
}

bool DebuggerObject::CallData::promiseLifetimeGetter() {
  if (!DebuggerObject::requirePromise(cx, object)) {
    return false;
  }

  args.rval().setNumber(object->promiseLifetime());
  return true;
}

bool DebuggerObject::CallData::promiseTimeToResolutionGetter() {
  if (!DebuggerObject::requirePromise(cx, object)) {
    return false;
  }

  if (object->promiseState() == JS::PromiseState::Pending) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_DEBUG_PROMISE_NOT_RESOLVED);
    return false;
  }

  args.rval().setNumber(object->promiseTimeToResolution());
  return true;
}

static PromiseObject* EnsurePromise(JSContext* cx, HandleObject referent) {
  // We only care about promises, so CheckedUnwrapStatic is OK.
  RootedObject obj(cx, CheckedUnwrapStatic(referent));
  if (!obj) {
    ReportAccessDenied(cx);
    return nullptr;
  }
  if (!obj->is<PromiseObject>()) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_NOT_EXPECTED_TYPE, "Debugger""Promise",
                              obj->getClass()->name);
    return nullptr;
  }
  return &obj->as<PromiseObject>();
}

bool DebuggerObject::CallData::promiseAllocationSiteGetter() {
  Rooted<PromiseObject*> promise(cx, EnsurePromise(cx, referent));
  if (!promise) {
    return false;
  }

  RootedObject allocSite(cx, promise->allocationSite());
  if (!allocSite) {
    args.rval().setNull();
    return true;
  }

  if (!cx->compartment()->wrap(cx, &allocSite)) {
    return false;
  }
  args.rval().set(ObjectValue(*allocSite));
  return true;
}

bool DebuggerObject::CallData::promiseResolutionSiteGetter() {
  Rooted<PromiseObject*> promise(cx, EnsurePromise(cx, referent));
  if (!promise) {
    return false;
  }

  if (promise->state() == JS::PromiseState::Pending) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_DEBUG_PROMISE_NOT_RESOLVED);
    return false;
  }

  RootedObject resolutionSite(cx, promise->resolutionSite());
  if (!resolutionSite) {
    args.rval().setNull();
    return true;
  }

  if (!cx->compartment()->wrap(cx, &resolutionSite)) {
    return false;
  }
  args.rval().set(ObjectValue(*resolutionSite));
  return true;
}

bool DebuggerObject::CallData::promiseIDGetter() {
  Rooted<PromiseObject*> promise(cx, EnsurePromise(cx, referent));
  if (!promise) {
    return false;
  }

  args.rval().setNumber(double(promise->getID()));
  return true;
}

bool DebuggerObject::CallData::promiseDependentPromisesGetter() {
  Debugger* dbg = object->owner();

  Rooted<PromiseObject*> promise(cx, EnsurePromise(cx, referent));
  if (!promise) {
    return false;
  }

  Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
  {
    JSAutoRealm ar(cx, promise);
    if (!promise->dependentPromises(cx, &values)) {
      return false;
    }
  }
  for (size_t i = 0; i < values.length(); i++) {
    if (!dbg->wrapDebuggeeValue(cx, values[i])) {
      return false;
    }
  }
  Rooted<ArrayObject*> promises(cx);
  if (values.length() == 0) {
    promises = NewDenseEmptyArray(cx);
  } else {
    promises = NewDenseCopiedArray(cx, values.length(), values[0].address());
  }
  if (!promises) {
    return false;
  }
  args.rval().setObject(*promises);
  return true;
}

bool DebuggerObject::CallData::isExtensibleMethod() {
  bool result;
  if (!DebuggerObject::isExtensible(cx, object, result)) {
    return false;
  }

  args.rval().setBoolean(result);
  return true;
}

bool DebuggerObject::CallData::isSealedMethod() {
  bool result;
  if (!DebuggerObject::isSealed(cx, object, result)) {
    return false;
  }

  args.rval().setBoolean(result);
  return true;
}

bool DebuggerObject::CallData::isFrozenMethod() {
  bool result;
  if (!DebuggerObject::isFrozen(cx, object, result)) {
    return false;
  }

  args.rval().setBoolean(result);
  return true;
}

bool DebuggerObject::CallData::getOwnPropertyNamesMethod() {
  RootedIdVector ids(cx);
  if (!DebuggerObject::getOwnPropertyNames(cx, object, &ids)) {
    return false;
  }

  JSObject* obj = IdVectorToArray(cx, ids);
  if (!obj) {
    return false;
  }

  args.rval().setObject(*obj);
  return true;
}

bool DebuggerObject::CallData::getOwnPropertyNamesLengthMethod() {
  size_t ownPropertiesLength;
  if (!DebuggerObject::getOwnPropertyNamesLength(cx, object,
                                                 &ownPropertiesLength)) {
    return false;
  }

  args.rval().setNumber(ownPropertiesLength);
  return true;
}

bool DebuggerObject::CallData::getOwnPropertySymbolsMethod() {
  RootedIdVector ids(cx);
  if (!DebuggerObject::getOwnPropertySymbols(cx, object, &ids)) {
    return false;
  }

  JSObject* obj = IdVectorToArray(cx, ids);
  if (!obj) {
    return false;
  }

  args.rval().setObject(*obj);
  return true;
}

bool DebuggerObject::CallData::getOwnPrivatePropertiesMethod() {
  RootedIdVector ids(cx);
  if (!DebuggerObject::getOwnPrivateProperties(cx, object, &ids)) {
    return false;
  }

  JSObject* obj = IdVectorToArray(cx, ids);
  if (!obj) {
    return false;
  }

  args.rval().setObject(*obj);
  return true;
}

bool DebuggerObject::CallData::getOwnPropertyDescriptorMethod() {
  RootedId id(cx);
  if (!ToPropertyKey(cx, args.get(0), &id)) {
    return false;
  }

  Rooted<Maybe<PropertyDescriptor>> desc(cx);
  if (!DebuggerObject::getOwnPropertyDescriptor(cx, object, id, &desc)) {
    return false;
  }

  return JS::FromPropertyDescriptor(cx, desc, args.rval());
}

bool DebuggerObject::CallData::preventExtensionsMethod() {
  if (!DebuggerObject::preventExtensions(cx, object)) {
    return false;
  }

  args.rval().setUndefined();
  return true;
}

bool DebuggerObject::CallData::sealMethod() {
  if (!DebuggerObject::seal(cx, object)) {
    return false;
  }

  args.rval().setUndefined();
  return true;
}

bool DebuggerObject::CallData::freezeMethod() {
  if (!DebuggerObject::freeze(cx, object)) {
    return false;
  }

  args.rval().setUndefined();
  return true;
}

bool DebuggerObject::CallData::definePropertyMethod() {
  if (!args.requireAtLeast(cx, "Debugger.Object.defineProperty", 2)) {
    return false;
  }

  RootedId id(cx);
  if (!ToPropertyKey(cx, args[0], &id)) {
    return false;
  }

  Rooted<PropertyDescriptor> desc(cx);
  if (!ToPropertyDescriptor(cx, args[1], false, &desc)) {
    return false;
  }

  if (!DebuggerObject::defineProperty(cx, object, id, desc)) {
    return false;
  }

  args.rval().setUndefined();
  return true;
}

bool DebuggerObject::CallData::definePropertiesMethod() {
  if (!args.requireAtLeast(cx, "Debugger.Object.defineProperties", 1)) {
    return false;
  }

  RootedValue arg(cx, args[0]);
  RootedObject props(cx, ToObject(cx, arg));
  if (!props) {
    return false;
  }
  RootedIdVector ids(cx);
  Rooted<PropertyDescriptorVector> descs(cx, PropertyDescriptorVector(cx));
  if (!ReadPropertyDescriptors(cx, props, false, &ids, &descs)) {
    return false;
  }
  Rooted<IdVector> ids2(cx, IdVector(cx));
  if (!ids2.append(ids.begin(), ids.end())) {
    return false;
  }

  if (!DebuggerObject::defineProperties(cx, object, ids2, descs)) {
    return false;
  }

  args.rval().setUndefined();
  return true;
}

/*
 * This does a non-strict delete, as a matter of API design. The case where the
 * property is non-configurable isn't necessarily exceptional here.
 */

bool DebuggerObject::CallData::deletePropertyMethod() {
  RootedId id(cx);
  if (!ToPropertyKey(cx, args.get(0), &id)) {
    return false;
  }

  ObjectOpResult result;
  if (!DebuggerObject::deleteProperty(cx, object, id, result)) {
    return false;
  }

  args.rval().setBoolean(result.ok());
  return true;
}

bool DebuggerObject::CallData::callMethod() {
  RootedValue thisv(cx, args.get(0));

  Rooted<ValueVector> nargs(cx, ValueVector(cx));
  if (args.length() >= 2) {
    if (!nargs.growBy(args.length() - 1)) {
      return false;
    }
    for (size_t i = 1; i < args.length(); ++i) {
      nargs[i - 1].set(args[i]);
    }
  }

  Rooted<Maybe<Completion>> completion(
      cx, DebuggerObject::call(cx, object, thisv, nargs));
  if (!completion.get()) {
    return false;
  }

  return completion->buildCompletionValue(cx, object->owner(), args.rval());
}

bool DebuggerObject::CallData::getPropertyMethod() {
  Debugger* dbg = object->owner();

  RootedId id(cx);
  if (!ToPropertyKey(cx, args.get(0), &id)) {
    return false;
  }

  RootedValue receiver(cx,
                       args.length() < 2 ? ObjectValue(*object) : args.get(1));

  Rooted<Completion> comp(cx);
  JS_TRY_VAR_OR_RETURN_FALSE(cx, comp, getProperty(cx, object, id, receiver));
  return comp.get().buildCompletionValue(cx, dbg, args.rval());
}

bool DebuggerObject::CallData::setPropertyMethod() {
  Debugger* dbg = object->owner();

  RootedId id(cx);
  if (!ToPropertyKey(cx, args.get(0), &id)) {
    return false;
  }

  RootedValue value(cx, args.get(1));

  RootedValue receiver(cx,
                       args.length() < 3 ? ObjectValue(*object) : args.get(2));

  Rooted<Completion> comp(cx);
  JS_TRY_VAR_OR_RETURN_FALSE(cx, comp,
                             setProperty(cx, object, id, value, receiver));
  return comp.get().buildCompletionValue(cx, dbg, args.rval());
}

bool DebuggerObject::CallData::applyMethod() {
  RootedValue thisv(cx, args.get(0));

  Rooted<ValueVector> nargs(cx, ValueVector(cx));
  if (args.length() >= 2 && !args[1].isNullOrUndefined()) {
    if (!args[1].isObject()) {
      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                JSMSG_BAD_APPLY_ARGS, "apply");
      return false;
    }

    RootedObject argsobj(cx, &args[1].toObject());

    uint64_t argc = 0;
    if (!GetLengthProperty(cx, argsobj, &argc)) {
      return false;
    }
    argc = std::min(argc, uint64_t(ARGS_LENGTH_MAX));

    if (!nargs.growBy(argc) || !GetElements(cx, argsobj, argc, nargs.begin())) {
      return false;
    }
  }

  Rooted<Maybe<Completion>> completion(
      cx, DebuggerObject::call(cx, object, thisv, nargs));
  if (!completion.get()) {
    return false;
  }

  return completion->buildCompletionValue(cx, object->owner(), args.rval());
}

static void EnterDebuggeeObjectRealm(JSContext* cx, Maybe<AutoRealm>& ar,
                                     JSObject* referent) {
  // |referent| may be a cross-compartment wrapper and CCWs normally
  // shouldn't be used with AutoRealm, but here we use an arbitrary realm for
  // now because we don't really have another option.
  ar.emplace(cx, referent->maybeCCWRealm()->maybeGlobal());
}

static bool RequireGlobalObject(JSContext* cx, HandleValue dbgobj,
                                HandleObject referent) {
  RootedObject obj(cx, referent);

  if (!obj->is<GlobalObject>()) {
    const char* isWrapper = "";
    const char* isWindowProxy = "";

    // Help the poor programmer by pointing out wrappers around globals...
    if (obj->is<WrapperObject>()) {
      obj = js::UncheckedUnwrap(obj);
      isWrapper = "a wrapper around ";
    }

    // ... and WindowProxies around Windows.
    if (IsWindowProxy(obj)) {
      obj = ToWindowIfWindowProxy(obj);
      isWindowProxy = "a WindowProxy referring to ";
    }

    if (obj->is<GlobalObject>()) {
      ReportValueError(cx, JSMSG_DEBUG_WRAPPER_IN_WAY, JSDVG_SEARCH_STACK,
                       dbgobj, nullptr, isWrapper, isWindowProxy);
    } else {
      ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK, dbgobj,
                       nullptr, "a global object");
    }
    return false;
  }

  return true;
}

bool DebuggerObject::CallData::asEnvironmentMethod() {
  Debugger* dbg = object->owner();

  if (!RequireGlobalObject(cx, args.thisv(), referent)) {
    return false;
  }

  Rooted<Env*> env(cx);
  {
    AutoRealm ar(cx, referent);
    env = GetDebugEnvironmentForGlobalLexicalEnvironment(cx);
    if (!env) {
      return false;
    }
  }

  return dbg->wrapEnvironment(cx, env, args.rval());
}

// Lookup a binding on the referent's global scope and change it to undefined
// if it is an uninitialized lexical, otherwise do nothing. The method's
// JavaScript return value is true _only_ when an uninitialized lexical has been
// altered, otherwise it is false.
bool DebuggerObject::CallData::forceLexicalInitializationByNameMethod() {
  if (!args.requireAtLeast(
          cx, "Debugger.Object.prototype.forceLexicalInitializationByName",
          1)) {
    return false;
  }

  if (!DebuggerObject::requireGlobal(cx, object)) {
    return false;
  }

  RootedId id(cx);
  if (!ValueToIdentifier(cx, args[0], &id)) {
    return false;
  }

  bool result;
  if (!DebuggerObject::forceLexicalInitializationByName(cx, object, id,
                                                        result)) {
    return false;
  }

  args.rval().setBoolean(result);
  return true;
}

bool DebuggerObject::CallData::executeInGlobalMethod() {
  if (!args.requireAtLeast(cx, "Debugger.Object.prototype.executeInGlobal",
                           1)) {
    return false;
  }

  if (!DebuggerObject::requireGlobal(cx, object)) {
    return false;
  }

  AutoStableStringChars stableChars(cx);
  if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobal",
                          args[0], stableChars)) {
    return false;
  }
  mozilla::Range<const char16_t> chars = stableChars.twoByteRange();

  EvalOptions options(EvalOptions::EnvKind::Global);
  if (!ParseEvalOptions(cx, args.get(1), options)) {
    return false;
  }

  Rooted<Completion> comp(cx);
  JS_TRY_VAR_OR_RETURN_FALSE(
      cx, comp,
      DebuggerObject::executeInGlobal(cx, object, chars, nullptr, options));
  return comp.get().buildCompletionValue(cx, object->owner(), args.rval());
}

bool DebuggerObject::CallData::executeInGlobalWithBindingsMethod() {
  if (!args.requireAtLeast(
          cx, "Debugger.Object.prototype.executeInGlobalWithBindings", 2)) {
    return false;
  }

  if (!DebuggerObject::requireGlobal(cx, object)) {
    return false;
  }

  AutoStableStringChars stableChars(cx);
  if (!ValueToStableChars(
          cx, "Debugger.Object.prototype.executeInGlobalWithBindings", args[0],
          stableChars)) {
    return false;
  }
  mozilla::Range<const char16_t> chars = stableChars.twoByteRange();

  RootedObject bindings(cx, RequireObject(cx, args[1]));
  if (!bindings) {
    return false;
  }

  EvalOptions options(EvalOptions::EnvKind::GlobalWithExtraOuterBindings);
  if (!ParseEvalOptions(cx, args.get(2), options)) {
    return false;
  }

  Rooted<Completion> comp(cx);
  JS_TRY_VAR_OR_RETURN_FALSE(
      cx, comp,
      DebuggerObject::executeInGlobal(cx, object, chars, bindings, options));
  return comp.get().buildCompletionValue(cx, object->owner(), args.rval());
}

// Copy a narrow or wide string to a vector, appending a null terminator.
template <typename T>
static bool CopyStringToVector(JSContext* cx, JSString* str, Vector<T>& chars) {
  JSLinearString* linear = str->ensureLinear(cx);
  if (!linear) {
    return false;
  }
  if (!chars.appendN(0, linear->length() + 1)) {
    return false;
  }
  CopyChars(chars.begin(), *linear);
  return true;
}

bool DebuggerObject::CallData::createSource() {
  if (!args.requireAtLeast(cx, "Debugger.Object.prototype.createSource", 1)) {
    return false;
  }

  if (!DebuggerObject::requireGlobal(cx, object)) {
    return false;
  }

  Debugger* dbg = object->owner();
  if (!dbg->isDebuggeeUnbarriered(referent->as<GlobalObject>().realm())) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_DEBUG_NOT_DEBUGGEE, "Debugger.Object",
                              "global");
    return false;
  }

  RootedObject options(cx, ToObject(cx, args[0]));
  if (!options) {
    return false;
  }

  RootedValue v(cx);
  if (!JS_GetProperty(cx, options, "text", &v)) {
    return false;
  }

  RootedString text(cx, ToString<CanGC>(cx, v));
  if (!text) {
    return false;
  }

  if (!JS_GetProperty(cx, options, "url", &v)) {
    return false;
  }

  RootedString url(cx, ToString<CanGC>(cx, v));
  if (!url) {
    return false;
  }

  if (!JS_GetProperty(cx, options, "startLine", &v)) {
    return false;
  }

  uint32_t startLine;
  if (!ToUint32(cx, v, &startLine)) {
    return false;
  }

  if (!JS_GetProperty(cx, options, "startColumn", &v)) {
    return false;
  }

  uint32_t startColumn;
  if (!ToUint32(cx, v, &startColumn)) {
    return false;
  }
  if (startColumn == 0) {
    startColumn = 1;
  }

  if (!JS_GetProperty(cx, options, "sourceMapURL", &v)) {
    return false;
  }

  RootedString sourceMapURL(cx);
  if (!v.isUndefined()) {
    sourceMapURL = ToString<CanGC>(cx, v);
    if (!sourceMapURL) {
      return false;
    }
  }

  if (!JS_GetProperty(cx, options, "isScriptElement", &v)) {
    return false;
  }

  bool isScriptElement = ToBoolean(v);

  if (!JS_GetProperty(cx, options, "forceEnableAsmJS", &v)) {
    return false;
  }

  bool forceEnableAsmJS = ToBoolean(v);

  RootedScript script(cx);
  {
    AutoRealm ar(cx, referent);

    JS::CompileOptions compileOptions(cx);
    compileOptions.lineno = startLine;
    compileOptions.column = JS::ColumnNumberOneOrigin(startColumn);
    if (forceEnableAsmJS) {
      compileOptions.setAsmJSOption(JS::AsmJSOption::Enabled);
    }

    if (!JS::StringHasLatin1Chars(url)) {
      JS_ReportErrorASCII(cx, "URL must be a narrow string");
      return false;
    }

    UniqueChars urlChars = JS_EncodeStringToUTF8(cx, url);
    if (!urlChars) {
      return false;
    }
    compileOptions.setFile(urlChars.get());

    Vector<char16_t> sourceMapURLChars(cx);
    if (sourceMapURL) {
      if (!CopyStringToVector(cx, sourceMapURL, sourceMapURLChars)) {
        return false;
      }
      compileOptions.setSourceMapURL(sourceMapURLChars.begin());
    }

    if (isScriptElement) {
      // The introduction type must be a statically allocated string.
      compileOptions.setIntroductionType("inlineScript");
    }

    AutoStableStringChars linearChars(cx);
    if (!linearChars.initTwoByte(cx, text)) {
      return false;
    }
    JS::SourceText<char16_t> srcBuf;
    if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
      return false;
    }

    script = JS::Compile(cx, compileOptions, srcBuf);
    if (!script) {
      return false;
    }
  }

  Rooted<ScriptSourceObject*> sso(cx, script->sourceObject());
  RootedObject wrapped(cx, dbg->wrapSource(cx, sso));
  if (!wrapped) {
    return false;
  }

  args.rval().setObject(*wrapped);
  return true;
}

bool DebuggerObject::CallData::makeDebuggeeValueMethod() {
  if (!args.requireAtLeast(cx, "Debugger.Object.prototype.makeDebuggeeValue",
                           1)) {
    return false;
  }

  return DebuggerObject::makeDebuggeeValue(cx, object, args[0], args.rval());
}

bool DebuggerObject::CallData::isSameNativeMethod() {
  if (!args.requireAtLeast(cx, "Debugger.Object.prototype.isSameNative", 1)) {
    return false;
  }

  return DebuggerObject::isSameNative(cx, object, args[0], CheckJitInfo::No,
                                      args.rval());
}

bool DebuggerObject::CallData::isSameNativeWithJitInfoMethod() {
  if (!args.requireAtLeast(
          cx, "Debugger.Object.prototype.isSameNativeWithJitInfo", 1)) {
    return false;
  }

  return DebuggerObject::isSameNative(cx, object, args[0], CheckJitInfo::Yes,
                                      args.rval());
}

bool DebuggerObject::CallData::isNativeGetterWithJitInfo() {
  return DebuggerObject::isNativeGetterWithJitInfo(cx, object, args.rval());
}

bool DebuggerObject::CallData::unsafeDereferenceMethod() {
  RootedObject result(cx);
  if (!DebuggerObject::unsafeDereference(cx, object, &result)) {
    return false;
  }

  args.rval().setObject(*result);
  return true;
}

bool DebuggerObject::CallData::unwrapMethod() {
  Rooted<DebuggerObject*> result(cx);
  if (!DebuggerObject::unwrap(cx, object, &result)) {
    return false;
  }

  args.rval().setObjectOrNull(result);
  return true;
}

struct DebuggerObject::PromiseReactionRecordBuilder
    : js::PromiseReactionRecordBuilder {
  Debugger* dbg;
  Handle<ArrayObject*> records;

  PromiseReactionRecordBuilder(Debugger* dbg, Handle<ArrayObject*> records)
      : dbg(dbg), records(records) {}

  bool then(JSContext* cx, HandleObject resolve, HandleObject reject,
            HandleObject result) override {
    Rooted<PlainObject*> record(cx, NewPlainObject(cx));
    if (!record) {
      return false;
    }

    if (!setIfNotNull(cx, record, cx->names().resolve, resolve) ||
        !setIfNotNull(cx, record, cx->names().reject, reject) ||
        !setIfNotNull(cx, record, cx->names().result, result)) {
      return false;
    }

    return push(cx, record);
  }

  bool direct(JSContext* cx, Handle<PromiseObject*> unwrappedPromise) override {
    RootedValue v(cx, ObjectValue(*unwrappedPromise));
    return dbg->wrapDebuggeeValue(cx, &v) && push(cx, v);
  }

  bool asyncFunction(
      JSContext* cx,
      Handle<AsyncFunctionGeneratorObject*> unwrappedGenerator) override {
    return maybePushGenerator(cx, unwrappedGenerator);
  }

  bool asyncGenerator(
      JSContext* cx,
      Handle<AsyncGeneratorObject*> unwrappedGenerator) override {
    return maybePushGenerator(cx, unwrappedGenerator);
  }

 private:
  bool push(JSContext* cx, HandleObject record) {
    RootedValue recordVal(cx, ObjectValue(*record));
    return push(cx, recordVal);
  }

  bool push(JSContext* cx, HandleValue recordVal) {
    return NewbornArrayPush(cx, records, recordVal);
  }

  bool maybePushGenerator(JSContext* cx,
                          Handle<AbstractGeneratorObject*> unwrappedGenerator) {
    Rooted<DebuggerFrame*> frame(cx);
    if (unwrappedGenerator->isClosed()) {
      // If the generator is closed, we can't generate a DebuggerFrame for it,
      // so we ignore it.
      return true;
    }
    if (!unwrappedGenerator->realm()->isDebuggee()) {
      // Caller can keep the reference to the debugger object even after
      // removing the realm from debuggee.  Do nothing for this case.
      return true;
    }
    return dbg->getFrame(cx, unwrappedGenerator, &frame) && push(cx, frame);
  }

  bool setIfNotNull(JSContext* cx, Handle<PlainObject*> obj,
                    Handle<PropertyName*> name, HandleObject prop) {
    if (!prop) {
      return true;
    }

    RootedValue v(cx, ObjectValue(*prop));
    if (!dbg->wrapDebuggeeValue(cx, &v) ||
        !DefineDataProperty(cx, obj, name, v)) {
      return false;
    }

    return true;
  }
};

bool DebuggerObject::CallData::getPromiseReactionsMethod() {
  Debugger* dbg = object->owner();

  Rooted<PromiseObject*> unwrappedPromise(cx, EnsurePromise(cx, referent));
  if (!unwrappedPromise) {
    return false;
  }

  Rooted<ArrayObject*> holder(cx, NewDenseEmptyArray(cx));
  if (!holder) {
    return false;
  }

  PromiseReactionRecordBuilder builder(dbg, holder);
  if (!unwrappedPromise->forEachReactionRecord(cx, builder)) {
    return false;
  }

  args.rval().setObject(*builder.records);
  return true;
}

const JSPropertySpec DebuggerObject::properties_[] = {
    JS_DEBUG_PSG("callable", callableGetter),
    JS_DEBUG_PSG("isBoundFunction", isBoundFunctionGetter),
    JS_DEBUG_PSG("isArrowFunction", isArrowFunctionGetter),
    JS_DEBUG_PSG("isGeneratorFunction", isGeneratorFunctionGetter),
    JS_DEBUG_PSG("isAsyncFunction", isAsyncFunctionGetter),
    JS_DEBUG_PSG("isClassConstructor", isClassConstructorGetter),
    JS_DEBUG_PSG("proto", protoGetter),
    JS_DEBUG_PSG("class", classGetter),
    JS_DEBUG_PSG("name", nameGetter),
    JS_DEBUG_PSG("displayName", displayNameGetter),
    JS_DEBUG_PSG("parameterNames", parameterNamesGetter),
    JS_DEBUG_PSG("script", scriptGetter),
    JS_DEBUG_PSG("environment", environmentGetter),
    JS_DEBUG_PSG("boundTargetFunction", boundTargetFunctionGetter),
    JS_DEBUG_PSG("boundThis", boundThisGetter),
    JS_DEBUG_PSG("boundArguments", boundArgumentsGetter),
    JS_DEBUG_PSG("allocationSite", allocationSiteGetter),
    JS_DEBUG_PSG("isError", isErrorGetter),
    JS_DEBUG_PSG("errorMessageName", errorMessageNameGetter),
    JS_DEBUG_PSG("errorNotes", errorNotesGetter),
    JS_DEBUG_PSG("errorLineNumber", errorLineNumberGetter),
    JS_DEBUG_PSG("errorColumnNumber", errorColumnNumberGetter),
    JS_DEBUG_PSG("isProxy", isProxyGetter),
    JS_DEBUG_PSG("proxyTarget", proxyTargetGetter),
    JS_DEBUG_PSG("proxyHandler", proxyHandlerGetter),
    JS_PS_END,
};

const JSPropertySpec DebuggerObject::promiseProperties_[] = {
    JS_DEBUG_PSG("isPromise", isPromiseGetter),
    JS_DEBUG_PSG("promiseState", promiseStateGetter),
    JS_DEBUG_PSG("promiseValue", promiseValueGetter),
    JS_DEBUG_PSG("promiseReason", promiseReasonGetter),
    JS_DEBUG_PSG("promiseLifetime", promiseLifetimeGetter),
    JS_DEBUG_PSG("promiseTimeToResolution", promiseTimeToResolutionGetter),
    JS_DEBUG_PSG("promiseAllocationSite", promiseAllocationSiteGetter),
    JS_DEBUG_PSG("promiseResolutionSite", promiseResolutionSiteGetter),
    JS_DEBUG_PSG("promiseID", promiseIDGetter),
    JS_DEBUG_PSG("promiseDependentPromises", promiseDependentPromisesGetter),
    JS_PS_END,
};

const JSFunctionSpec DebuggerObject::methods_[] = {
    JS_DEBUG_FN("isExtensible", isExtensibleMethod, 0),
    JS_DEBUG_FN("isSealed", isSealedMethod, 0),
    JS_DEBUG_FN("isFrozen", isFrozenMethod, 0),
    JS_DEBUG_FN("getProperty", getPropertyMethod, 0),
    JS_DEBUG_FN("setProperty", setPropertyMethod, 0),
    JS_DEBUG_FN("getOwnPropertyNames", getOwnPropertyNamesMethod, 0),
    JS_DEBUG_FN("getOwnPropertyNamesLength", getOwnPropertyNamesLengthMethod,
                0),
    JS_DEBUG_FN("getOwnPropertySymbols", getOwnPropertySymbolsMethod, 0),
    JS_DEBUG_FN("getOwnPrivateProperties", getOwnPrivatePropertiesMethod, 0),
    JS_DEBUG_FN("getOwnPropertyDescriptor", getOwnPropertyDescriptorMethod, 1),
    JS_DEBUG_FN("preventExtensions", preventExtensionsMethod, 0),
    JS_DEBUG_FN("seal", sealMethod, 0),
    JS_DEBUG_FN("freeze", freezeMethod, 0),
    JS_DEBUG_FN("defineProperty", definePropertyMethod, 2),
    JS_DEBUG_FN("defineProperties", definePropertiesMethod, 1),
    JS_DEBUG_FN("deleteProperty", deletePropertyMethod, 1),
    JS_DEBUG_FN("call", callMethod, 0),
    JS_DEBUG_FN("apply", applyMethod, 0),
    JS_DEBUG_FN("asEnvironment", asEnvironmentMethod, 0),
    JS_DEBUG_FN("forceLexicalInitializationByName",
                forceLexicalInitializationByNameMethod, 1),
    JS_DEBUG_FN("executeInGlobal", executeInGlobalMethod, 1),
    JS_DEBUG_FN("executeInGlobalWithBindings",
                executeInGlobalWithBindingsMethod, 2),
    JS_DEBUG_FN("createSource", createSource, 1),
    JS_DEBUG_FN("makeDebuggeeValue", makeDebuggeeValueMethod, 1),
    JS_DEBUG_FN("isSameNative", isSameNativeMethod, 1),
    JS_DEBUG_FN("isSameNativeWithJitInfo", isSameNativeWithJitInfoMethod, 1),
    JS_DEBUG_FN("isNativeGetterWithJitInfo", isNativeGetterWithJitInfo, 1),
    JS_DEBUG_FN("unsafeDereference", unsafeDereferenceMethod, 0),
    JS_DEBUG_FN("unwrap", unwrapMethod, 0),
    JS_DEBUG_FN("getPromiseReactions", getPromiseReactionsMethod, 0),
    JS_FS_END,
};

/* static */
NativeObject* DebuggerObject::initClass(JSContext* cx,
                                        Handle<GlobalObject*> global,
                                        HandleObject debugCtor) {
  Rooted<NativeObject*> objectProto(
      cx, InitClass(cx, debugCtor, nullptr, nullptr, "Object", construct, 0,
                    properties_, methods_, nullptr, nullptr));

  if (!objectProto) {
    return nullptr;
  }

  if (!DefinePropertiesAndFunctions(cx, objectProto, promiseProperties_,
                                    nullptr)) {
    return nullptr;
  }

  return objectProto;
}

/* static */
DebuggerObject* DebuggerObject::create(JSContext* cx, HandleObject proto,
                                       HandleObject referent,
                                       Handle<NativeObject*> debugger) {
  DebuggerObject* obj =
      IsInsideNursery(referent)
          ? NewObjectWithGivenProto<DebuggerObject>(cx, proto)
          : NewTenuredObjectWithGivenProto<DebuggerObject>(cx, proto);
  if (!obj) {
    return nullptr;
  }

  obj->setReservedSlotGCThingAsPrivate(OBJECT_SLOT, referent);
  obj->setReservedSlot(OWNER_SLOT, ObjectValue(*debugger));

  return obj;
}

bool DebuggerObject::isCallable() const { return referent()->isCallable(); }

bool DebuggerObject::isFunction() const { return referent()->is<JSFunction>(); }

bool DebuggerObject::isDebuggeeFunction() const {
  return referent()->is<JSFunction>() &&
         owner()->observesGlobal(&referent()->as<JSFunction>().global());
}

bool DebuggerObject::isBoundFunction() const {
  return referent()->is<BoundFunctionObject>();
}

bool DebuggerObject::isDebuggeeBoundFunction() const {
  return referent()->is<BoundFunctionObject>() &&
         owner()->observesGlobal(
             &referent()->as<BoundFunctionObject>().global());
}

bool DebuggerObject::isArrowFunction() const {
  MOZ_ASSERT(isDebuggeeFunction());

  return referent()->as<JSFunction>().isArrow();
}

bool DebuggerObject::isAsyncFunction() const {
  MOZ_ASSERT(isDebuggeeFunction());

  return referent()->as<JSFunction>().isAsync();
}

bool DebuggerObject::isGeneratorFunction() const {
  MOZ_ASSERT(isDebuggeeFunction());

  return referent()->as<JSFunction>().isGenerator();
}

bool DebuggerObject::isClassConstructor() const {
  MOZ_ASSERT(isDebuggeeFunction());

  return referent()->as<JSFunction>().isClassConstructor();
}

bool DebuggerObject::isGlobal() const { return referent()->is<GlobalObject>(); }

bool DebuggerObject::isScriptedProxy() const {
  return js::IsScriptedProxy(referent());
}

bool DebuggerObject::isPromise() const {
  JSObject* referent = this->referent();

  if (IsCrossCompartmentWrapper(referent)) {
    // We only care about promises, so CheckedUnwrapStatic is OK.
    referent = CheckedUnwrapStatic(referent);
    if (!referent) {
      return false;
    }
  }

  return referent->is<PromiseObject>();
}

bool DebuggerObject::isError() const {
  JSObject* referent = this->referent();

  if (IsCrossCompartmentWrapper(referent)) {
    // We only check for error classes, so CheckedUnwrapStatic is OK.
    referent = CheckedUnwrapStatic(referent);
    if (!referent) {
      return false;
    }
  }

  return referent->is<ErrorObject>();
}

/* static */
bool DebuggerObject::getClassName(JSContext* cx, Handle<DebuggerObject*> object,
                                  MutableHandleString result) {
  RootedObject referent(cx, object->referent());

  const char* className;
  {
    Maybe<AutoRealm> ar;
    EnterDebuggeeObjectRealm(cx, ar, referent);
    className = GetObjectClassName(cx, referent);
  }

  JSAtom* str = Atomize(cx, className, strlen(className));
  if (!str) {
    return false;
  }

  result.set(str);
  return true;
}

bool DebuggerObject::name(JSContext* cx,
                          JS::MutableHandle<JSAtom*> result) const {
  if (isFunction()) {
    JSFunction* fun = &referent()->as<JSFunction>();
    if (!fun->isAccessorWithLazyName()) {
      result.set(fun->fullExplicitName());
      if (result) {
        cx->markAtom(result);
      }
      return true;
    }

    {
      Maybe<AutoRealm> ar;
      EnterDebuggeeObjectRealm(cx, ar, fun);

      result.set(fun->getAccessorNameForLazy(cx));
      if (!result) {
        return false;
      }
    }
    cx->markAtom(result);
    return true;
  }

  MOZ_ASSERT(isBoundFunction());

  // Bound functions have a configurable `name` data property and currently
  // don't store the original name. Try a pure lookup to get this name and if
  // this fails use "bound".
  Rooted<BoundFunctionObject*> bound(cx,
                                     &referent()->as<BoundFunctionObject>());
  {
    Maybe<AutoRealm> ar;
    EnterDebuggeeObjectRealm(cx, ar, bound);

    Value v;
    bool found;
    if (GetOwnPropertyPure(cx, bound, NameToId(cx->names().name), &v, &found) &&
        found && v.isString()) {
      result.set(AtomizeString(cx, v.toString()));
      if (!result) {
        return false;
      }
    } else {
      result.set(cx->names().bound);
    }
  }

  cx->markAtom(result);
  return true;
}

bool DebuggerObject::displayName(JSContext* cx,
                                 JS::MutableHandle<JSAtom*> result) const {
  if (isFunction()) {
    {
      JS::Rooted<JSFunction*> fun(cx, &referent()->as<JSFunction>());

      Maybe<AutoRealm> ar;
      EnterDebuggeeObjectRealm(cx, ar, fun);

      if (!fun->getDisplayAtom(cx, result)) {
        return false;
      }
    }
    if (result) {
      cx->markAtom(result);
    }
    return true;
  }

  MOZ_ASSERT(isBoundFunction());
  return name(cx, result);
}

JS::PromiseState DebuggerObject::promiseState() const {
  return promise()->state();
}

double DebuggerObject::promiseLifetime() const { return promise()->lifetime(); }

double DebuggerObject::promiseTimeToResolution() const {
  MOZ_ASSERT(promiseState() != JS::PromiseState::Pending);

  return promise()->timeToResolution();
}

/* static */
bool DebuggerObject::getBoundTargetFunction(
    JSContext* cx, Handle<DebuggerObject*> object,
    MutableHandle<DebuggerObject*> result) {
  MOZ_ASSERT(object->isBoundFunction());

  Rooted<BoundFunctionObject*> referent(
      cx, &object->referent()->as<BoundFunctionObject>());
  Debugger* dbg = object->owner();

  RootedObject target(cx, referent->getTarget());
  return dbg->wrapDebuggeeObject(cx, target, result);
}

/* static */
bool DebuggerObject::getBoundThis(JSContext* cx, Handle<DebuggerObject*> object,
                                  MutableHandleValue result) {
  MOZ_ASSERT(object->isBoundFunction());

  Rooted<BoundFunctionObject*> referent(
      cx, &object->referent()->as<BoundFunctionObject>());
  Debugger* dbg = object->owner();

  result.set(referent->getBoundThis());
  return dbg->wrapDebuggeeValue(cx, result);
}

/* static */
bool DebuggerObject::getBoundArguments(JSContext* cx,
                                       Handle<DebuggerObject*> object,
                                       MutableHandle<ValueVector> result) {
  MOZ_ASSERT(object->isBoundFunction());

  Rooted<BoundFunctionObject*> referent(
      cx, &object->referent()->as<BoundFunctionObject>());
  Debugger* dbg = object->owner();

  size_t length = referent->numBoundArgs();
  if (!result.resize(length)) {
    return false;
  }
  for (size_t i = 0; i < length; i++) {
    result[i].set(referent->getBoundArg(i));
    if (!dbg->wrapDebuggeeValue(cx, result[i])) {
      return false;
    }
  }
  return true;
}

/* static */
SavedFrame* Debugger::getObjectAllocationSite(JSObject& obj) {
  JSObject* metadata = GetAllocationMetadata(&obj);
  if (!metadata) {
    return nullptr;
  }

  MOZ_ASSERT(!metadata->is<WrapperObject>());
  return metadata->is<SavedFrame>() ? &metadata->as<SavedFrame>() : nullptr;
}

/* static */
bool DebuggerObject::getAllocationSite(JSContext* cx,
                                       Handle<DebuggerObject*> object,
                                       MutableHandleObject result) {
  RootedObject referent(cx, object->referent());

  RootedObject allocSite(cx, Debugger::getObjectAllocationSite(*referent));
  if (!cx->compartment()->wrap(cx, &allocSite)) {
    return false;
  }

  result.set(allocSite);
  return true;
}

/* static */
bool DebuggerObject::getErrorReport(JSContext* cx, HandleObject maybeError,
                                    JSErrorReport*& report) {
  JSObject* obj = maybeError;
  if (IsCrossCompartmentWrapper(obj)) {
    /* We only care about Error objects, so CheckedUnwrapStatic is OK. */
    obj = CheckedUnwrapStatic(obj);
  }

  if (!obj) {
    ReportAccessDenied(cx);
    return false;
  }

  if (!obj->is<ErrorObject>()) {
    report = nullptr;
    return true;
  }

  report = obj->as<ErrorObject>().getErrorReport();
  return true;
}

/* static */
bool DebuggerObject::getErrorMessageName(JSContext* cx,
                                         Handle<DebuggerObject*> object,
                                         MutableHandleString result) {
  RootedObject referent(cx, object->referent());
  JSErrorReport* report;
  if (!getErrorReport(cx, referent, report)) {
    return false;
  }

  if (!report || !report->errorMessageName) {
    result.set(nullptr);
    return true;
  }

  RootedString str(cx, JS_NewStringCopyZ(cx, report->errorMessageName));
  if (!str) {
    return false;
  }
  result.set(str);
  return true;
}

/* static */
bool DebuggerObject::getErrorNotes(JSContext* cx,
                                   Handle<DebuggerObject*> object,
                                   MutableHandleValue result) {
  RootedObject referent(cx, object->referent());
  JSErrorReport* report;
  if (!getErrorReport(cx, referent, report)) {
    return false;
  }

  if (!report) {
    result.setUndefined();
    return true;
  }

  RootedObject errorNotesArray(cx, CreateErrorNotesArray(cx, report));
  if (!errorNotesArray) {
    return false;
  }

  if (!cx->compartment()->wrap(cx, &errorNotesArray)) {
    return false;
  }
  result.setObject(*errorNotesArray);
  return true;
}

/* static */
bool DebuggerObject::getErrorLineNumber(JSContext* cx,
                                        Handle<DebuggerObject*> object,
                                        MutableHandleValue result) {
  RootedObject referent(cx, object->referent());
  JSErrorReport* report;
  if (!getErrorReport(cx, referent, report)) {
    return false;
  }

  if (!report) {
    result.setUndefined();
    return true;
  }

  result.setNumber(report->lineno);
  return true;
}

/* static */
bool DebuggerObject::getErrorColumnNumber(JSContext* cx,
                                          Handle<DebuggerObject*> object,
                                          MutableHandleValue result) {
  RootedObject referent(cx, object->referent());
  JSErrorReport* report;
  if (!getErrorReport(cx, referent, report)) {
    return false;
  }

  if (!report) {
    result.setUndefined();
    return true;
  }

  result.setNumber(report->column.oneOriginValue());
  return true;
}

/* static */
bool DebuggerObject::getPromiseValue(JSContext* cx,
                                     Handle<DebuggerObject*> object,
                                     MutableHandleValue result) {
  MOZ_ASSERT(object->promiseState() == JS::PromiseState::Fulfilled);

  result.set(object->promise()->value());
  return object->owner()->wrapDebuggeeValue(cx, result);
}

/* static */
bool DebuggerObject::getPromiseReason(JSContext* cx,
                                      Handle<DebuggerObject*> object,
                                      MutableHandleValue result) {
  MOZ_ASSERT(object->promiseState() == JS::PromiseState::Rejected);

  result.set(object->promise()->reason());
  return object->owner()->wrapDebuggeeValue(cx, result);
}

/* static */
bool DebuggerObject::isExtensible(JSContext* cx, Handle<DebuggerObject*> object,
                                  bool& result) {
  RootedObject referent(cx, object->referent());

  Maybe<AutoRealm> ar;
  EnterDebuggeeObjectRealm(cx, ar, referent);

  ErrorCopier ec(ar);
  return IsExtensible(cx, referent, &result);
}

/* static */
bool DebuggerObject::isSealed(JSContext* cx, Handle<DebuggerObject*> object,
                              bool& result) {
  RootedObject referent(cx, object->referent());

  Maybe<AutoRealm> ar;
  EnterDebuggeeObjectRealm(cx, ar, referent);

  ErrorCopier ec(ar);
  return TestIntegrityLevel(cx, referent, IntegrityLevel::Sealed, &result);
}

/* static */
bool DebuggerObject::isFrozen(JSContext* cx, Handle<DebuggerObject*> object,
                              bool& result) {
  RootedObject referent(cx, object->referent());

  Maybe<AutoRealm> ar;
  EnterDebuggeeObjectRealm(cx, ar, referent);

  ErrorCopier ec(ar);
  return TestIntegrityLevel(cx, referent, IntegrityLevel::Frozen, &result);
}

/* static */
bool DebuggerObject::getPrototypeOf(JSContext* cx,
                                    Handle<DebuggerObject*> object,
                                    MutableHandle<DebuggerObject*> result) {
  RootedObject referent(cx, object->referent());
  Debugger* dbg = object->owner();

  RootedObject proto(cx);
  {
    Maybe<AutoRealm> ar;
    EnterDebuggeeObjectRealm(cx, ar, referent);
    if (!GetPrototype(cx, referent, &proto)) {
      return false;
    }
  }

  return dbg->wrapNullableDebuggeeObject(cx, proto, result);
}

/* static */
bool DebuggerObject::getOwnPropertyNames(JSContext* cx,
                                         Handle<DebuggerObject*> object,
                                         MutableHandleIdVector result) {
  MOZ_ASSERT(result.empty());

  RootedObject referent(cx, object->referent());
  {
    Maybe<AutoRealm> ar;
    EnterDebuggeeObjectRealm(cx, ar, referent);

    ErrorCopier ec(ar);
    if (!GetPropertyKeys(cx, referent, JSITER_OWNONLY | JSITER_HIDDEN,
                         result)) {
      return false;
    }
  }

  for (size_t i = 0; i < result.length(); i++) {
    cx->markId(result[i]);
  }

  return true;
}

/* static */
bool DebuggerObject::getOwnPropertyNamesLength(JSContext* cx,
                                               Handle<DebuggerObject*> object,
                                               size_t* result) {
  RootedObject referent(cx, object->referent());

  RootedIdVector ids(cx);
  {
    Maybe<AutoRealm> ar;
    EnterDebuggeeObjectRealm(cx, ar, referent);

    ErrorCopier ec(ar);
    if (!GetPropertyKeys(cx, referent, JSITER_OWNONLY | JSITER_HIDDEN, &ids)) {
      return false;
    }
  }

  *result = ids.length();
  return true;
}

static bool GetSymbolPropertyKeys(JSContext* cx, Handle<DebuggerObject*> object,
                                  JS::MutableHandleIdVector props,
                                  bool includePrivate) {
  RootedObject referent(cx, object->referent());

  {
    Maybe<AutoRealm> ar;
    EnterDebuggeeObjectRealm(cx, ar, referent);

    ErrorCopier ec(ar);

    unsigned flags =
        JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS | JSITER_SYMBOLSONLY;
    if (includePrivate) {
      flags = flags | JSITER_PRIVATE;
    }
    if (!GetPropertyKeys(cx, referent, flags, props)) {
      return false;
    }
  }

  return true;
}

/* static */
bool DebuggerObject::getOwnPropertySymbols(JSContext* cx,
                                           Handle<DebuggerObject*> object,
                                           MutableHandleIdVector result) {
  MOZ_ASSERT(result.empty());

  if (!GetSymbolPropertyKeys(cx, object, result, false)) {
    return false;
  }

  for (size_t i = 0; i < result.length(); i++) {
    cx->markAtom(result[i].toSymbol());
  }

  return true;
}

/* static */
bool DebuggerObject::getOwnPrivateProperties(JSContext* cx,
                                             Handle<DebuggerObject*> object,
                                             MutableHandleIdVector result) {
  MOZ_ASSERT(result.empty());

  if (!GetSymbolPropertyKeys(cx, object, result, true)) {
    return false;
  }

  result.eraseIf([](PropertyKey key) {
    if (!key.isPrivateName()) {
      return true;
    }
    // Private *methods* create a Private Brand, a special private name
    // stamped onto the symbol, to indicate it is possible to execute private
    // methods from the class on this object. We don't want to return such
    // items here, so we check if we're dealing with a private property, e.g.
    // the Symbol description starts with a "#" character.
    JSAtom* privateDescription = key.toSymbol()->description();
    if (privateDescription->length() == 0) {
      return true;
    }
    char16_t firstChar = privateDescription->latin1OrTwoByteChar(0);
    return firstChar != '#';
  });

  for (size_t i = 0; i < result.length(); i++) {
    cx->markAtom(result[i].toSymbol());
  }

  return true;
}

/* static */
bool DebuggerObject::getOwnPropertyDescriptor(
    JSContext* cx, Handle<DebuggerObject*> object, HandleId id,
    MutableHandle<Maybe<PropertyDescriptor>> desc_) {
  RootedObject referent(cx, object->referent());
  Debugger* dbg = object->owner();

  // Bug: This can cause the debuggee to run!
  {
    Maybe<AutoRealm> ar;
    EnterDebuggeeObjectRealm(cx, ar, referent);

    cx->markId(id);

    ErrorCopier ec(ar);
    if (!GetOwnPropertyDescriptor(cx, referent, id, desc_)) {
      return false;
    }
  }

  if (desc_.isSome()) {
    Rooted<PropertyDescriptor> desc(cx, *desc_);

    if (desc.hasValue()) {
      // Rewrap the debuggee values in desc for the debugger.
      if (!dbg->wrapDebuggeeValue(cx, desc.value())) {
        return false;
      }
    }
    if (desc.hasGetter()) {
      RootedValue get(cx, ObjectOrNullValue(desc.getter()));
      if (!dbg->wrapDebuggeeValue(cx, &get)) {
        return false;
      }
      desc.setGetter(get.toObjectOrNull());
    }
    if (desc.hasSetter()) {
      RootedValue set(cx, ObjectOrNullValue(desc.setter()));
      if (!dbg->wrapDebuggeeValue(cx, &set)) {
        return false;
      }
      desc.setSetter(set.toObjectOrNull());
    }

    desc_.set(mozilla::Some(desc.get()));
  }

  return true;
}

/* static */
bool DebuggerObject::preventExtensions(JSContext* cx,
                                       Handle<DebuggerObject*> object) {
  RootedObject referent(cx, object->referent());

  Maybe<AutoRealm> ar;
  EnterDebuggeeObjectRealm(cx, ar, referent);

  ErrorCopier ec(ar);
  return PreventExtensions(cx, referent);
}

/* static */
bool DebuggerObject::seal(JSContext* cx, Handle<DebuggerObject*> object) {
  RootedObject referent(cx, object->referent());

  Maybe<AutoRealm> ar;
  EnterDebuggeeObjectRealm(cx, ar, referent);

  ErrorCopier ec(ar);
  return SetIntegrityLevel(cx, referent, IntegrityLevel::Sealed);
}

/* static */
bool DebuggerObject::freeze(JSContext* cx, Handle<DebuggerObject*> object) {
  RootedObject referent(cx, object->referent());

  Maybe<AutoRealm> ar;
  EnterDebuggeeObjectRealm(cx, ar, referent);

  ErrorCopier ec(ar);
  return SetIntegrityLevel(cx, referent, IntegrityLevel::Frozen);
}

/* static */
bool DebuggerObject::defineProperty(JSContext* cx,
                                    Handle<DebuggerObject*> object, HandleId id,
                                    Handle<PropertyDescriptor> desc_) {
  RootedObject referent(cx, object->referent());
  Debugger* dbg = object->owner();

  Rooted<PropertyDescriptor> desc(cx, desc_);
  if (!dbg->unwrapPropertyDescriptor(cx, referent, &desc)) {
    return false;
  }
  JS_TRY_OR_RETURN_FALSE(cx, CheckPropertyDescriptorAccessors(cx, desc));

  Maybe<AutoRealm> ar;
  EnterDebuggeeObjectRealm(cx, ar, referent);

  if (!cx->compartment()->wrap(cx, &desc)) {
    return false;
  }
  cx->markId(id);

  ErrorCopier ec(ar);
  return DefineProperty(cx, referent, id, desc);
}

/* static */
bool DebuggerObject::defineProperties(JSContext* cx,
                                      Handle<DebuggerObject*> object,
                                      Handle<IdVector> ids,
                                      Handle<PropertyDescriptorVector> descs_) {
  RootedObject referent(cx, object->referent());
  Debugger* dbg = object->owner();

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=92 H=94 G=92

¤ Dauer der Verarbeitung: 0.25 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge