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


Quelle  jsapi.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/. */


/*
 * JavaScript API.
 */


#include "jsapi.h"

#include "mozilla/FloatingPoint.h"
#include "mozilla/Maybe.h"
#include "mozilla/PodOperations.h"
#include "mozilla/Sprintf.h"

#include <algorithm>
#include <cstdarg>
#ifdef __linux__
#  include <dlfcn.h>
#endif
#include <iterator>
#include <stdarg.h>
#include <string.h>

#include "jsexn.h"
#include "jsfriendapi.h"
#include "jsmath.h"
#include "jstypes.h"

#include "builtin/AtomicsObject.h"
#include "builtin/Eval.h"
#include "builtin/JSON.h"
#include "builtin/Promise.h"
#include "builtin/Symbol.h"
#include "frontend/FrontendContext.h"  // AutoReportFrontendContext
#include "gc/GC.h"
#include "gc/GCContext.h"
#include "gc/Marking.h"
#include "gc/PublicIterators.h"
#include "jit/JitSpewer.h"
#include "jit/TrampolineNatives.h"
#include "js/CallAndConstruct.h"  // JS::IsCallable
#include "js/CharacterEncoding.h"
#include "js/ColumnNumber.h"  // JS::TaggedColumnNumberOneOrigin, JS::ColumnNumberOneOrigin
#include "js/CompileOptions.h"
#include "js/ContextOptions.h"  // JS::ContextOptions{,Ref}
#include "js/Conversions.h"
#include "js/Date.h"  // JS::GetReduceMicrosecondTimePrecisionCallback
#include "js/ErrorInterceptor.h"
#include "js/ErrorReport.h"           // JSErrorBase
#include "js/experimental/JitInfo.h"  // JSJitInfo
#include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
#include "js/friend/StackLimits.h"    // js::AutoCheckRecursionLimit
#include "js/GlobalObject.h"
#include "js/Initialization.h"
#include "js/Interrupt.h"
#include "js/JSON.h"
#include "js/LocaleSensitive.h"
#include "js/MemoryCallbacks.h"
#include "js/MemoryFunctions.h"
#include "js/Prefs.h"
#include "js/PropertySpec.h"
#include "js/Proxy.h"
#include "js/ScriptPrivate.h"
#include "js/StableStringChars.h"
#include "js/Stack.h"  // JS::NativeStackSize, JS::NativeStackLimitMax, JS::GetNativeStackLimit
#include "js/StreamConsumer.h"
#include "js/String.h"  // JS::MaxStringLength
#include "js/Symbol.h"
#include "js/TelemetryTimers.h"
#include "js/Utility.h"
#include "js/WaitCallbacks.h"
#include "js/WasmModule.h"
#include "js/Wrapper.h"
#include "js/WrapperCallbacks.h"
#include "proxy/DOMProxy.h"
#include "util/Identifier.h"  // IsIdentifier
#include "util/StringBuilder.h"
#include "util/Text.h"
#include "vm/BoundFunctionObject.h"
#include "vm/EnvironmentObject.h"
#include "vm/ErrorObject.h"
#include "vm/ErrorReporting.h"
#include "vm/FunctionPrefixKind.h"
#include "vm/Interpreter.h"
#include "vm/JSAtomState.h"
#include "vm/JSAtomUtils.h"  // Atomize, AtomizeWithoutActiveZone, AtomizeChars, PinAtom, ClassName
#include "vm/JSContext.h"
#include "vm/JSFunction.h"
#include "vm/JSObject.h"
#include "vm/JSScript.h"
#include "vm/Logging.h"
#include "vm/PlainObject.h"    // js::PlainObject
#include "vm/PromiseObject.h"  // js::PromiseObject
#include "vm/Runtime.h"
#include "vm/SavedStacks.h"
#include "vm/StringType.h"
#include "vm/Time.h"
#include "vm/ToSource.h"
#include "vm/Watchtower.h"
#include "vm/WrapperObject.h"
#include "wasm/WasmModule.h"
#include "wasm/WasmProcess.h"

#include "builtin/Promise-inl.h"
#include "debugger/DebugAPI-inl.h"
#include "vm/Compartment-inl.h"
#include "vm/Interpreter-inl.h"
#include "vm/IsGivenTypeObject-inl.h"  // js::IsGivenTypeObject
#include "vm/JSAtomUtils-inl.h"  // AtomToId, PrimitiveValueToId, IndexToId, ClassName
#include "vm/JSFunction-inl.h"
#include "vm/JSScript-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/SavedStacks-inl.h"
#include "vm/StringType-inl.h"

using namespace js;

using mozilla::Maybe;

using JS::AutoStableStringChars;
using JS::CompileOptions;
using JS::ReadOnlyCompileOptions;

// See preprocessor definition of JS_BITS_PER_WORD in jstypes.h; make sure
// JS_64BIT (used internally) agrees with it
#ifdef JS_64BIT
static_assert(JS_BITS_PER_WORD == 64, "values must be in sync");
#else
static_assert(JS_BITS_PER_WORD == 32, "values must be in sync");
#endif

JS_PUBLIC_API void JS::CallArgs::reportMoreArgsNeeded(JSContext* cx,
                                                      const char* fnname,
                                                      unsigned required,
                                                      unsigned actual) {
  char requiredArgsStr[40];
  SprintfLiteral(requiredArgsStr, "%u", required);
  char actualArgsStr[40];
  SprintfLiteral(actualArgsStr, "%u", actual);
  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                            JSMSG_MORE_ARGS_NEEDED, fnname, requiredArgsStr,
                            required == 1 ? "" : "s", actualArgsStr);
}

static bool ErrorTakesArguments(unsigned msg) {
  MOZ_ASSERT(msg < JSErr_Limit);
  unsigned argCount = js_ErrorFormatString[msg].argCount;
  MOZ_ASSERT(argCount <= 2);
  return argCount == 1 || argCount == 2;
}

static bool ErrorTakesObjectArgument(unsigned msg) {
  MOZ_ASSERT(msg < JSErr_Limit);
  unsigned argCount = js_ErrorFormatString[msg].argCount;
  MOZ_ASSERT(argCount <= 2);
  return argCount == 2;
}

bool JS::ObjectOpResult::reportError(JSContext* cx, HandleObject obj,
                                     HandleId id) {
  static_assert(unsigned(OkCode) == unsigned(JSMSG_NOT_AN_ERROR),
                "unsigned value of OkCode must not be an error code");
  MOZ_ASSERT(code_ != Uninitialized);
  MOZ_ASSERT(!ok());
  cx->check(obj);

  if (code_ == JSMSG_OBJECT_NOT_EXTENSIBLE) {
    RootedValue val(cx, ObjectValue(*obj));
    return ReportValueError(cx, code_, JSDVG_IGNORE_STACK, val, nullptr);
  }

  if (ErrorTakesArguments(code_)) {
    UniqueChars propName =
        IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
    if (!propName) {
      return false;
    }

    if (code_ == JSMSG_SET_NON_OBJECT_RECEIVER) {
      // We know that the original receiver was a primitive, so unbox it.
      RootedValue val(cx, ObjectValue(*obj));
      if (!obj->is<ProxyObject>()) {
        if (!Unbox(cx, obj, &val)) {
          return false;
        }
      }
      return ReportValueError(cx, code_, JSDVG_IGNORE_STACK, val, nullptr,
                              propName.get());
    }

    if (ErrorTakesObjectArgument(code_)) {
      JSObject* unwrapped = js::CheckedUnwrapStatic(obj);
      const char* name = unwrapped ? unwrapped->getClass()->name : "Object";
      JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, code_, name,
                               propName.get());
      return false;
    }

    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, code_,
                             propName.get());
    return false;
  }
  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, code_);
  return false;
}

bool JS::ObjectOpResult::reportError(JSContext* cx, HandleObject obj) {
  MOZ_ASSERT(code_ != Uninitialized);
  MOZ_ASSERT(!ok());
  MOZ_ASSERT(!ErrorTakesArguments(code_));
  cx->check(obj);

  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, code_);
  return false;
}

JS_PUBLIC_API bool JS::ObjectOpResult::failCantRedefineProp() {
  return fail(JSMSG_CANT_REDEFINE_PROP);
}

JS_PUBLIC_API bool JS::ObjectOpResult::failReadOnly() {
  return fail(JSMSG_READ_ONLY);
}

JS_PUBLIC_API bool JS::ObjectOpResult::failGetterOnly() {
  return fail(JSMSG_GETTER_ONLY);
}

JS_PUBLIC_API bool JS::ObjectOpResult::failCantDelete() {
  return fail(JSMSG_CANT_DELETE);
}

JS_PUBLIC_API bool JS::ObjectOpResult::failCantSetInterposed() {
  return fail(JSMSG_CANT_SET_INTERPOSED);
}

JS_PUBLIC_API bool JS::ObjectOpResult::failCantDefineWindowElement() {
  return fail(JSMSG_CANT_DEFINE_WINDOW_ELEMENT);
}

JS_PUBLIC_API bool JS::ObjectOpResult::failCantDeleteWindowElement() {
  return fail(JSMSG_CANT_DELETE_WINDOW_ELEMENT);
}

JS_PUBLIC_API bool JS::ObjectOpResult::failCantDefineWindowNamedProperty() {
  return fail(JSMSG_CANT_DEFINE_WINDOW_NAMED_PROPERTY);
}

JS_PUBLIC_API bool JS::ObjectOpResult::failCantDeleteWindowNamedProperty() {
  return fail(JSMSG_CANT_DELETE_WINDOW_NAMED_PROPERTY);
}

JS_PUBLIC_API bool JS::ObjectOpResult::failCantDefineWindowNonConfigurable() {
  return fail(JSMSG_CANT_DEFINE_WINDOW_NC);
}

JS_PUBLIC_API bool JS::ObjectOpResult::failCantPreventExtensions() {
  return fail(JSMSG_CANT_PREVENT_EXTENSIONS);
}

JS_PUBLIC_API bool JS::ObjectOpResult::failCantSetProto() {
  return fail(JSMSG_CANT_SET_PROTO);
}

JS_PUBLIC_API bool JS::ObjectOpResult::failNoNamedSetter() {
  return fail(JSMSG_NO_NAMED_SETTER);
}

JS_PUBLIC_API bool JS::ObjectOpResult::failNoIndexedSetter() {
  return fail(JSMSG_NO_INDEXED_SETTER);
}

JS_PUBLIC_API bool JS::ObjectOpResult::failNotDataDescriptor() {
  return fail(JSMSG_NOT_DATA_DESCRIPTOR);
}

JS_PUBLIC_API bool JS::ObjectOpResult::failInvalidDescriptor() {
  return fail(JSMSG_INVALID_DESCRIPTOR);
}

JS_PUBLIC_API bool JS::ObjectOpResult::failBadArrayLength() {
  return fail(JSMSG_BAD_ARRAY_LENGTH);
}

JS_PUBLIC_API bool JS::ObjectOpResult::failBadIndex() {
  return fail(JSMSG_BAD_INDEX);
}

JS_PUBLIC_API int64_t JS_Now() { return PRMJ_Now(); }

JS_PUBLIC_API Value JS_GetEmptyStringValue(JSContext* cx) {
  return StringValue(cx->runtime()->emptyString);
}

JS_PUBLIC_API JSString* JS_GetEmptyString(JSContext* cx) {
  MOZ_ASSERT(cx->emptyString());
  return cx->emptyString();
}

namespace js {

void AssertHeapIsIdle() { MOZ_ASSERT(!JS::RuntimeHeapIsBusy()); }

}  // namespace js

static void AssertHeapIsIdleOrIterating() {
  MOZ_ASSERT(!JS::RuntimeHeapIsCollecting());
}

JS_PUBLIC_API bool JS_ValueToObject(JSContext* cx, HandleValue value,
                                    MutableHandleObject objp) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->check(value);
  if (value.isNullOrUndefined()) {
    objp.set(nullptr);
    return true;
  }
  JSObject* obj = ToObject(cx, value);
  if (!obj) {
    return false;
  }
  objp.set(obj);
  return true;
}

JS_PUBLIC_API JSFunction* JS_ValueToFunction(JSContext* cx, HandleValue value) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->check(value);
  return ReportIfNotFunction(cx, value);
}

JS_PUBLIC_API JSFunction* JS_ValueToConstructor(JSContext* cx,
                                                HandleValue value) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->check(value);
  return ReportIfNotFunction(cx, value);
}

JS_PUBLIC_API JSString* JS_ValueToSource(JSContext* cx, HandleValue value) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->check(value);
  return ValueToSource(cx, value);
}

JS_PUBLIC_API bool JS_DoubleIsInt32(double d, int32_t* ip) {
  return mozilla::NumberIsInt32(d, ip);
}

JS_PUBLIC_API JSType JS_TypeOfValue(JSContext* cx, HandleValue value) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->check(value);
  return TypeOfValue(value);
}

JS_PUBLIC_API bool JS_IsBuiltinEvalFunction(JSFunction* fun) {
  return IsAnyBuiltinEval(fun);
}

JS_PUBLIC_API bool JS_IsBuiltinFunctionConstructor(JSFunction* fun) {
  return fun->isBuiltinFunctionConstructor();
}

JS_PUBLIC_API bool JS_ObjectIsBoundFunction(JSObject* obj) {
  return obj->is<BoundFunctionObject>();
}

JS_PUBLIC_API JSObject* JS_GetBoundFunctionTarget(JSObject* obj) {
  return obj->is<BoundFunctionObject>()
             ? obj->as<BoundFunctionObject>().getTarget()
             : nullptr;
}

/************************************************************************/

// Prevent functions from being discarded by linker, so that they are callable
// when debugging.
static void PreventDiscardingFunctions() {
  if (reinterpret_cast<uintptr_t>(&PreventDiscardingFunctions) == 1) {
    // Never executed.
    memset((void*)&js::debug::GetMarkInfo, 0, 1);
    memset((void*)&js::debug::GetMarkWordAddress, 0, 1);
    memset((void*)&js::debug::GetMarkMask, 0, 1);
  }
}

JS_PUBLIC_API JSContext* JS_NewContext(uint32_t maxbytes,
                                       JSRuntime* parentRuntime) {
  MOZ_ASSERT(JS::detail::libraryInitState == JS::detail::InitState::Running,
             "must call JS_Init prior to creating any JSContexts");

  // Prevent linker from discarding unused debug functions.
  PreventDiscardingFunctions();

  // Make sure that all parent runtimes are the topmost parent.
  while (parentRuntime && parentRuntime->parentRuntime) {
    parentRuntime = parentRuntime->parentRuntime;
  }

  return NewContext(maxbytes, parentRuntime);
}

JS_PUBLIC_API void JS_DestroyContext(JSContext* cx) { DestroyContext(cx); }

JS_PUBLIC_API void* JS_GetContextPrivate(JSContext* cx) { return cx->data; }

JS_PUBLIC_API void JS_SetContextPrivate(JSContext* cx, void* data) {
  cx->data = data;
}

JS_PUBLIC_API void JS_SetFutexCanWait(JSContext* cx) {
  cx->fx.setCanWait(true);
}

JS_PUBLIC_API JSRuntime* JS_GetParentRuntime(JSContext* cx) {
  return cx->runtime()->parentRuntime ? cx->runtime()->parentRuntime
                                      : cx->runtime();
}

JS_PUBLIC_API JSRuntime* JS_GetRuntime(JSContext* cx) { return cx->runtime(); }

JS_PUBLIC_API JS::ContextOptions& JS::ContextOptionsRef(JSContext* cx) {
  return cx->options();
}

JS::ContextOptions& JS::ContextOptions::setFuzzing(bool flag) {
#ifdef FUZZING
  fuzzing_ = flag;
#endif
  return *this;
}

JS_PUBLIC_API const char* JS_GetImplementationVersion(void) {
  return "JavaScript-C" MOZILLA_VERSION;
}

JS_PUBLIC_API void JS_SetDestroyZoneCallback(JSContext* cx,
                                             JSDestroyZoneCallback callback) {
  cx->runtime()->destroyZoneCallback = callback;
}

JS_PUBLIC_API void JS_SetDestroyCompartmentCallback(
    JSContext* cx, JSDestroyCompartmentCallback callback) {
  cx->runtime()->destroyCompartmentCallback = callback;
}

JS_PUBLIC_API void JS_SetSizeOfIncludingThisCompartmentCallback(
    JSContext* cx, JSSizeOfIncludingThisCompartmentCallback callback) {
  cx->runtime()->sizeOfIncludingThisCompartmentCallback = callback;
}

JS_PUBLIC_API void JS_SetErrorInterceptorCallback(
    JSRuntime* rt, JSErrorInterceptor* callback) {
#if defined(NIGHTLY_BUILD)
  rt->errorInterception.interceptor = callback;
#endif  // defined(NIGHTLY_BUILD)
}

JS_PUBLIC_API JSErrorInterceptor* JS_GetErrorInterceptorCallback(
    JSRuntime* rt) {
#if defined(NIGHTLY_BUILD)
  return rt->errorInterception.interceptor;
#else   // !NIGHTLY_BUILD
  return nullptr;
#endif  // defined(NIGHTLY_BUILD)
}

JS_PUBLIC_API Maybe<JSExnType> JS_GetErrorType(const JS::Value& val) {
  // All errors are objects.
  if (!val.isObject()) {
    return mozilla::Nothing();
  }

  const JSObject& obj = val.toObject();

  // All errors are `ErrorObject`.
  if (!obj.is<js::ErrorObject>()) {
    // Not one of the primitive errors.
    return mozilla::Nothing();
  }

  const js::ErrorObject& err = obj.as<js::ErrorObject>();
  return mozilla::Some(err.type());
}

JS_PUBLIC_API void JS_SetWrapObjectCallbacks(
    JSContext* cx, const JSWrapObjectCallbacks* callbacks) {
  cx->runtime()->wrapObjectCallbacks = callbacks;
}

JS_PUBLIC_API Realm* JS::EnterRealm(JSContext* cx, JSObject* target) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);

  MOZ_DIAGNOSTIC_ASSERT(!js::IsCrossCompartmentWrapper(target));

  Realm* oldRealm = cx->realm();
  cx->enterRealmOf(target);
  return oldRealm;
}

JS_PUBLIC_API void JS::LeaveRealm(JSContext* cx, JS::Realm* oldRealm) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->leaveRealm(oldRealm);
}

JSAutoRealm::JSAutoRealm(JSContext* cx, JSObject* target)
    : cx_(cx), oldRealm_(cx->realm()) {
  MOZ_DIAGNOSTIC_ASSERT(!js::IsCrossCompartmentWrapper(target));
  AssertHeapIsIdleOrIterating();
  cx_->enterRealmOf(target);
}

JSAutoRealm::JSAutoRealm(JSContext* cx, JSScript* target)
    : cx_(cx), oldRealm_(cx->realm()) {
  AssertHeapIsIdleOrIterating();
  cx_->enterRealmOf(target);
}

JSAutoRealm::~JSAutoRealm() { cx_->leaveRealm(oldRealm_); }

JSAutoNullableRealm::JSAutoNullableRealm(JSContext* cx, JSObject* targetOrNull)
    : cx_(cx), oldRealm_(cx->realm()) {
  AssertHeapIsIdleOrIterating();
  if (targetOrNull) {
    MOZ_DIAGNOSTIC_ASSERT(!js::IsCrossCompartmentWrapper(targetOrNull));
    cx_->enterRealmOf(targetOrNull);
  } else {
    cx_->enterNullRealm();
  }
}

JSAutoNullableRealm::~JSAutoNullableRealm() { cx_->leaveRealm(oldRealm_); }

JS_PUBLIC_API void JS_SetCompartmentPrivate(JS::Compartment* compartment,
                                            void* data) {
  compartment->data = data;
}

JS_PUBLIC_API void* JS_GetCompartmentPrivate(JS::Compartment* compartment) {
  return compartment->data;
}

JS_PUBLIC_API void JS_MarkCrossZoneId(JSContext* cx, jsid id) {
  cx->markId(id);
}

JS_PUBLIC_API void JS_MarkCrossZoneIdValue(JSContext* cx, const Value& value) {
  cx->markAtomValue(value);
}

JS_PUBLIC_API void JS_SetZoneUserData(JS::Zone* zone, void* data) {
  zone->data = data;
}

JS_PUBLIC_API void* JS_GetZoneUserData(JS::Zone* zone) { return zone->data; }

JS_PUBLIC_API bool JS_WrapObject(JSContext* cx, MutableHandleObject objp) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  if (objp) {
    JS::ExposeObjectToActiveJS(objp);
  }
  return cx->compartment()->wrap(cx, objp);
}

JS_PUBLIC_API bool JS_WrapValue(JSContext* cx, MutableHandleValue vp) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  JS::ExposeValueToActiveJS(vp);
  return cx->compartment()->wrap(cx, vp);
}

static void ReleaseAssertObjectHasNoWrappers(JSContext* cx,
                                             HandleObject target) {
  for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
    if (c->lookupWrapper(target)) {
      MOZ_CRASH("wrapper found for target object");
    }
  }
}

/*
 * [SMDOC] Brain transplants.
 *
 * Not for beginners or the squeamish.
 *
 * Sometimes a web spec requires us to transplant an object from one
 * compartment to another, like when a DOM node is inserted into a document in
 * another window and thus gets "adopted". We cannot literally change the
 * `.compartment()` of a `JSObject`; that would break the compartment
 * invariants. However, as usual, we have a workaround using wrappers.
 *
 * Of all the wrapper-based workarounds we do, it's safe to say this is the
 * most spectacular and questionable.
 *
 * `JS_TransplantObject(cx, origobj, target)` changes `origobj` into a
 * simulacrum of `target`, using highly esoteric means. To JS code, the effect
 * is as if `origobj` magically "became" `target`, but most often what actually
 * happens is that `origobj` gets turned into a cross-compartment wrapper for
 * `target`. The old behavior and contents of `origobj` are overwritten or
 * discarded.
 *
 * Thus, to "transplant" an object from one compartment to another:
 *
 * 1.  Let `origobj` be the object that you want to move. First, create a
 *     clone of it, `target`, in the destination compartment.
 *
 *     In our DOM adoption example, `target` will be a Node of the same type as
 *     `origobj`, same content, but in the adopting document.  We're not done
 *     yet: the spec for DOM adoption requires that `origobj.ownerDocument`
 *     actually change. All we've done so far is make a copy.
 *
 * 2.  Call `JS_TransplantObject(cx, origobj, target)`. This typically turns
 *     `origobj` into a wrapper for `target`, so that any JS code that has a
 *     reference to `origobj` will observe it to have the behavior of `target`
 *     going forward. In addition, all existing wrappers for `origobj` are
 *     changed into wrappers for `target`, extending the illusion to those
 *     compartments as well.
 *
 * During navigation, we use the above technique to transplant the WindowProxy
 * into the new Window's compartment.
 *
 * A few rules:
 *
 * -   `origobj` and `target` must be two distinct objects of the same
 *     `JSClass`.  Some classes may not support transplantation; WindowProxy
 *     objects and DOM nodes are OK.
 *
 * -   `target` should be created specifically to be passed to this function.
 *     There must be no existing cross-compartment wrappers for it; ideally
 *     there shouldn't be any pointers to it at all, except the one passed in.
 *
 * -   `target` shouldn't be used afterwards. Instead, `JS_TransplantObject`
 *     returns a pointer to the transplanted object, which might be `target`
 *     but might be some other object in the same compartment. Use that.
 *
 * The reason for this last rule is that JS_TransplantObject does very strange
 * things in some cases, like swapping `target`'s brain with that of another
 * object. Leaving `target` behaving like its former self is not a goal.
 *
 * We don't have a good way to recover from failure in this function, so
 * we intentionally crash instead.
 */


static void CheckTransplantObject(JSObject* obj) {
#ifdef DEBUG
  MOZ_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
  JS::AssertCellIsNotGray(obj);
#endif
}

JS_PUBLIC_API JSObject* JS_TransplantObject(JSContext* cx, HandleObject origobj,
                                            HandleObject target) {
  AssertHeapIsIdle();
  MOZ_ASSERT(origobj != target);
  CheckTransplantObject(origobj);
  CheckTransplantObject(target);
  ReleaseAssertObjectHasNoWrappers(cx, target);

  RootedObject newIdentity(cx);

  // Don't allow a compacting GC to observe any intermediate state.
  AutoDisableCompactingGC nocgc(cx);

  AutoDisableProxyCheck adpc;

  AutoEnterOOMUnsafeRegion oomUnsafe;

  JS::Compartment* destination = target->compartment();

  if (origobj->compartment() == destination) {
    // If the original object is in the same compartment as the
    // destination, then we know that we won't find a wrapper in the
    // destination's cross compartment map and that the same
    // object will continue to work.
    AutoRealm ar(cx, origobj);
    JSObject::swap(cx, origobj, target, oomUnsafe);
    newIdentity = origobj;
  } else if (ObjectWrapperMap::Ptr p = destination->lookupWrapper(origobj)) {
    // There might already be a wrapper for the original object in
    // the new compartment. If there is, we use its identity and swap
    // in the contents of |target|.
    newIdentity = p->value().get();

    // When we remove origv from the wrapper map, its wrapper, newIdentity,
    // must immediately cease to be a cross-compartment wrapper. Nuke it.
    destination->removeWrapper(p);
    NukeCrossCompartmentWrapper(cx, newIdentity);

    AutoRealm ar(cx, newIdentity);
    JSObject::swap(cx, newIdentity, target, oomUnsafe);
  } else {
    // Otherwise, we use |target| for the new identity object.
    newIdentity = target;
  }

  // Now, iterate through other scopes looking for references to the old
  // object, and update the relevant cross-compartment wrappers. We do this
  // even if origobj is in the same compartment as target and thus
  // `newIdentity == origobj`, because this process also clears out any
  // cached wrapper state.
  if (!RemapAllWrappersForObject(cx, origobj, newIdentity)) {
    oomUnsafe.crash("JS_TransplantObject");
  }

  // Lastly, update the original object to point to the new one.
  if (origobj->compartment() != destination) {
    RootedObject newIdentityWrapper(cx, newIdentity);
    AutoRealm ar(cx, origobj);
    if (!JS_WrapObject(cx, &newIdentityWrapper)) {
      MOZ_RELEASE_ASSERT(cx->isThrowingOutOfMemory() ||
                         cx->isThrowingOverRecursed());
      oomUnsafe.crash("JS_TransplantObject");
    }
    MOZ_ASSERT(Wrapper::wrappedObject(newIdentityWrapper) == newIdentity);
    JSObject::swap(cx, origobj, newIdentityWrapper, oomUnsafe);
    if (origobj->compartment()->lookupWrapper(newIdentity)) {
      MOZ_ASSERT(origobj->is<CrossCompartmentWrapperObject>());
      if (!origobj->compartment()->putWrapper(cx, newIdentity, origobj)) {
        oomUnsafe.crash("JS_TransplantObject");
      }
    }
  }

  // The new identity object might be one of several things. Return it to avoid
  // ambiguity.
  JS::AssertCellIsNotGray(newIdentity);
  return newIdentity;
}

JS_PUBLIC_API void js::RemapRemoteWindowProxies(
    JSContext* cx, CompartmentTransplantCallback* callback,
    MutableHandleObject target) {
  AssertHeapIsIdle();
  CheckTransplantObject(target);
  ReleaseAssertObjectHasNoWrappers(cx, target);

  // |target| can't be a remote proxy, because we expect it to get a CCW when
  // wrapped across compartments.
  MOZ_ASSERT(!js::IsDOMRemoteProxyObject(target));

  // Don't allow a compacting GC to observe any intermediate state.
  AutoDisableCompactingGC nocgc(cx);

  AutoDisableProxyCheck adpc;

  AutoEnterOOMUnsafeRegion oomUnsafe;

  AutoCheckRecursionLimit recursion(cx);
  if (!recursion.checkSystem(cx)) {
    oomUnsafe.crash("js::RemapRemoteWindowProxies");
  }

  RootedObject targetCompartmentProxy(cx);
  JS::RootedVector<JSObject*> otherProxies(cx);

  // Use the callback to find remote proxies in all compartments that match
  // whatever criteria callback uses.
  for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
    RootedObject remoteProxy(cx, callback->getObjectToTransplant(c));
    if (!remoteProxy) {
      continue;
    }
    // The object the callback returns should be a DOM remote proxy object in
    // the compartment c. We rely on it being a DOM remote proxy because that
    // means that it won't have any cross-compartment wrappers.
    MOZ_ASSERT(js::IsDOMRemoteProxyObject(remoteProxy));
    MOZ_ASSERT(remoteProxy->compartment() == c);
    CheckTransplantObject(remoteProxy);

    // Immediately turn the DOM remote proxy object into a dead proxy object
    // so we don't have to worry about anything weird going on with it.
    js::NukeNonCCWProxy(cx, remoteProxy);

    if (remoteProxy->compartment() == target->compartment()) {
      targetCompartmentProxy = remoteProxy;
    } else if (!otherProxies.append(remoteProxy)) {
      oomUnsafe.crash("js::RemapRemoteWindowProxies");
    }
  }

  // If there was a remote proxy in |target|'s compartment, we need to use it
  // instead of |target|, in case it had any references, so swap it. Do this
  // before any other compartment so that the target object will be set up
  // correctly before we start wrapping it into other compartments.
  if (targetCompartmentProxy) {
    AutoRealm ar(cx, targetCompartmentProxy);
    JSObject::swap(cx, targetCompartmentProxy, target, oomUnsafe);
    target.set(targetCompartmentProxy);
  }

  for (JSObject*& obj : otherProxies) {
    RootedObject deadWrapper(cx, obj);
    js::RemapDeadWrapper(cx, deadWrapper, target);
  }
}

/*
 * Recompute all cross-compartment wrappers for an object, resetting state.
 * Gecko uses this to clear Xray wrappers when doing a navigation that reuses
 * the inner window and global object.
 */

JS_PUBLIC_API bool JS_RefreshCrossCompartmentWrappers(JSContext* cx,
                                                      HandleObject obj) {
  return RemapAllWrappersForObject(cx, obj, obj);
}

struct JSStdName {
  size_t atomOffset; /* offset of atom pointer in JSAtomState */
  JSProtoKey key;
  bool isDummy() const { return key == JSProto_Null; }
  bool isSentinel() const { return key == JSProto_LIMIT; }
};

static const JSStdName* LookupStdName(const JSAtomState& names, JSAtom* name,
                                      const JSStdName* table) {
  for (unsigned i = 0; !table[i].isSentinel(); i++) {
    if (table[i].isDummy()) {
      continue;
    }
    JSAtom* atom = AtomStateOffsetToName(names, table[i].atomOffset);
    MOZ_ASSERT(atom);
    if (name == atom) {
      return &table[i];
    }
  }

  return nullptr;
}

/*
 * Table of standard classes, indexed by JSProtoKey. For entries where the
 * JSProtoKey does not correspond to a class with a meaningful constructor, we
 * insert a null entry into the table.
 */

#define STD_NAME_ENTRY(name, clasp) {NAME_OFFSET(name), JSProto_##name},
#define STD_DUMMY_ENTRY(name, dummy) {0, JSProto_Null},
static const JSStdName standard_class_names[] = {
    JS_FOR_PROTOTYPES(STD_NAME_ENTRY, STD_DUMMY_ENTRY){0, JSProto_LIMIT}};

/*
 * Table of top-level function and constant names and the JSProtoKey of the
 * standard class that initializes them.
 */

static const JSStdName builtin_property_names[] = {
    {NAME_OFFSET(eval), JSProto_Object},

    /* Global properties and functions defined by the Number class. */
    {NAME_OFFSET(NaN), JSProto_Number},
    {NAME_OFFSET(Infinity), JSProto_Number},
    {NAME_OFFSET(isNaN), JSProto_Number},
    {NAME_OFFSET(isFinite), JSProto_Number},
    {NAME_OFFSET(parseFloat), JSProto_Number},
    {NAME_OFFSET(parseInt), JSProto_Number},

    /* String global functions. */
    {NAME_OFFSET(escape), JSProto_String},
    {NAME_OFFSET(unescape), JSProto_String},
    {NAME_OFFSET(decodeURI), JSProto_String},
    {NAME_OFFSET(encodeURI), JSProto_String},
    {NAME_OFFSET(decodeURIComponent), JSProto_String},
    {NAME_OFFSET(encodeURIComponent), JSProto_String},
    {NAME_OFFSET(uneval), JSProto_String},

    {0, JSProto_LIMIT}};

static bool SkipUneval(jsid id, JSContext* cx) {
  return !cx->realm()->creationOptions().getToSourceEnabled() &&
         id == NameToId(cx->names().uneval);
}

static bool SkipSharedArrayBufferConstructor(JSProtoKey key,
                                             GlobalObject* global) {
  if (key != JSProto_SharedArrayBuffer) {
    return false;
  }

  const JS::RealmCreationOptions& options = global->realm()->creationOptions();
  MOZ_ASSERT(options.getSharedMemoryAndAtomicsEnabled(),
             "shouldn't contemplate defining SharedArrayBuffer if shared "
             "memory is disabled");

  // On the web, it isn't presently possible to expose the global
  // "SharedArrayBuffer" property unless the page is cross-site-isolated.  Only
  // define this constructor if an option on the realm indicates that it should
  // be defined.
  return !options.defineSharedArrayBufferConstructor();
}

JS_PUBLIC_API bool JS_ResolveStandardClass(JSContext* cx, HandleObject obj,
                                           HandleId id, bool* resolved) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->check(obj, id);

  Handle<GlobalObject*> global = obj.as<GlobalObject>();
  *resolved = false;

  if (!id.isAtom()) {
    return true;
  }

  /* Check whether we're resolving 'undefined', and define it if so. */
  JSAtom* idAtom = id.toAtom();
  if (idAtom == cx->names().undefined) {
    *resolved = true;
    return js::DefineDataProperty(
        cx, global, id, UndefinedHandleValue,
        JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING);
  }

  // Resolve a "globalThis" self-referential property if necessary.
  if (idAtom == cx->names().globalThis) {
    return GlobalObject::maybeResolveGlobalThis(cx, global, resolved);
  }

  // Try for class constructors/prototypes named by well-known atoms.
  const JSStdName* stdnm =
      LookupStdName(cx->names(), idAtom, standard_class_names);
  if (!stdnm) {
    // Try less frequently used top-level functions and constants.
    stdnm = LookupStdName(cx->names(), idAtom, builtin_property_names);
    if (!stdnm) {
      return true;
    }
  }

  JSProtoKey key = stdnm->key;
  if (key == JSProto_Null || GlobalObject::skipDeselectedConstructor(cx, key) ||
      SkipUneval(id, cx)) {
    return true;
  }

  // If this class is anonymous (or it's "SharedArrayBuffer" but that global
  // constructor isn't supposed to be defined), then it doesn't exist as a
  // global property, so we won't resolve anything.
  const JSClass* clasp = ProtoKeyToClass(key);
  if (clasp && !clasp->specShouldDefineConstructor()) {
    return true;
  }
  if (SkipSharedArrayBufferConstructor(key, global)) {
    return true;
  }

  if (!GlobalObject::ensureConstructor(cx, global, key)) {
    return false;
  }
  *resolved = true;
  return true;
}

JS_PUBLIC_API bool JS_MayResolveStandardClass(const JSAtomState& names, jsid id,
                                              JSObject* maybeObj) {
  MOZ_ASSERT_IF(maybeObj, maybeObj->is<GlobalObject>());

  // The global object's resolve hook is special: JS_ResolveStandardClass
  // initializes the prototype chain lazily. Only attempt to optimize here
  // if we know the prototype chain has been initialized.
  if (!maybeObj || !maybeObj->staticPrototype()) {
    return true;
  }

  if (!id.isAtom()) {
    return false;
  }

  JSAtom* atom = id.toAtom();

  // This will return true even for deselected constructors.  (To do
  // better, we need a JSContext here; it's fine as it is.)

  return atom == names.undefined || atom == names.globalThis ||
         LookupStdName(names, atom, standard_class_names) ||
         LookupStdName(names, atom, builtin_property_names);
}

JS_PUBLIC_API bool JS_EnumerateStandardClasses(JSContext* cx,
                                               HandleObject obj) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->check(obj);
  Handle<GlobalObject*> global = obj.as<GlobalObject>();
  return GlobalObject::initStandardClasses(cx, global);
}

static bool EnumerateStandardClassesInTable(JSContext* cx,
                                            Handle<GlobalObject*> global,
                                            MutableHandleIdVector properties,
                                            const JSStdName* table,
                                            bool includeResolved) {
  for (unsigned i = 0; !table[i].isSentinel(); i++) {
    if (table[i].isDummy()) {
      continue;
    }

    JSProtoKey key = table[i].key;

    // If the standard class has been resolved, the properties have been
    // defined on the global so we don't need to add them here.
    if (!includeResolved && global->isStandardClassResolved(key)) {
      continue;
    }

    if (GlobalObject::skipDeselectedConstructor(cx, key)) {
      continue;
    }

    if (const JSClass* clasp = ProtoKeyToClass(key)) {
      if (!clasp->specShouldDefineConstructor() ||
          SkipSharedArrayBufferConstructor(key, global)) {
        continue;
      }
    }

    jsid id = NameToId(AtomStateOffsetToName(cx->names(), table[i].atomOffset));

    if (SkipUneval(id, cx)) {
      continue;
    }

    if (!properties.append(id)) {
      return false;
    }
  }

  return true;
}

static bool EnumerateStandardClasses(JSContext* cx, JS::HandleObject obj,
                                     JS::MutableHandleIdVector properties,
                                     bool enumerableOnly,
                                     bool includeResolved) {
  if (enumerableOnly) {
    // There are no enumerable standard classes and "undefined" is
    // not enumerable.
    return true;
  }

  Handle<GlobalObject*> global = obj.as<GlobalObject>();

  // It's fine to always append |undefined| here, it's non-configurable and
  // the enumeration code filters duplicates.
  if (!properties.append(NameToId(cx->names().undefined))) {
    return false;
  }

  bool resolved = false;
  if (!GlobalObject::maybeResolveGlobalThis(cx, global, &resolved)) {
    return false;
  }
  if (resolved || includeResolved) {
    if (!properties.append(NameToId(cx->names().globalThis))) {
      return false;
    }
  }

  if (!EnumerateStandardClassesInTable(cx, global, properties,
                                       standard_class_names, includeResolved)) {
    return false;
  }
  if (!EnumerateStandardClassesInTable(
          cx, global, properties, builtin_property_names, includeResolved)) {
    return false;
  }

  return true;
}

JS_PUBLIC_API bool JS_NewEnumerateStandardClasses(
    JSContext* cx, JS::HandleObject obj, JS::MutableHandleIdVector properties,
    bool enumerableOnly) {
  return EnumerateStandardClasses(cx, obj, properties, enumerableOnly, false);
}

JS_PUBLIC_API bool JS_NewEnumerateStandardClassesIncludingResolved(
    JSContext* cx, JS::HandleObject obj, JS::MutableHandleIdVector properties,
    bool enumerableOnly) {
  return EnumerateStandardClasses(cx, obj, properties, enumerableOnly, true);
}

JS_PUBLIC_API bool JS_GetClassObject(JSContext* cx, JSProtoKey key,
                                     MutableHandleObject objp) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  JSObject* obj = GlobalObject::getOrCreateConstructor(cx, key);
  if (!obj) {
    return false;
  }
  objp.set(obj);
  return true;
}

JS_PUBLIC_API bool JS_GetClassPrototype(JSContext* cx, JSProtoKey key,
                                        MutableHandleObject objp) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);

  // Bound functions don't have their own prototype object: they reuse the
  // prototype of the target object. This is typically Function.prototype so we
  // use that here.
  if (key == JSProto_BoundFunction) {
    key = JSProto_Function;
  }

  JSObject* proto = GlobalObject::getOrCreatePrototype(cx, key);
  if (!proto) {
    return false;
  }
  objp.set(proto);
  return true;
}

namespace JS {

JS_PUBLIC_API void ProtoKeyToId(JSContext* cx, JSProtoKey key,
                                MutableHandleId idp) {
  idp.set(NameToId(ClassName(key, cx)));
}

/* namespace JS */

JS_PUBLIC_API JSProtoKey JS_IdToProtoKey(JSContext* cx, HandleId id) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->check(id);

  if (!id.isAtom()) {
    return JSProto_Null;
  }

  JSAtom* atom = id.toAtom();
  const JSStdName* stdnm =
      LookupStdName(cx->names(), atom, standard_class_names);
  if (!stdnm) {
    return JSProto_Null;
  }

  if (GlobalObject::skipDeselectedConstructor(cx, stdnm->key)) {
    return JSProto_Null;
  }

  if (SkipSharedArrayBufferConstructor(stdnm->key, cx->global())) {
    MOZ_ASSERT(id == NameToId(cx->names().SharedArrayBuffer));
    return JSProto_Null;
  }

  if (SkipUneval(id, cx)) {
    return JSProto_Null;
  }

  static_assert(std::size(standard_class_names) == JSProto_LIMIT + 1);
  return static_cast<JSProtoKey>(stdnm - standard_class_names);
}

extern JS_PUBLIC_API bool JS_IsGlobalObject(JSObject* obj) {
  return obj->is<GlobalObject>();
}

extern JS_PUBLIC_API JSObject* JS_GlobalLexicalEnvironment(JSObject* obj) {
  return &obj->as<GlobalObject>().lexicalEnvironment();
}

extern JS_PUBLIC_API bool JS_HasExtensibleLexicalEnvironment(JSObject* obj) {
  return obj->is<GlobalObject>() ||
         ObjectRealm::get(obj).getNonSyntacticLexicalEnvironment(obj);
}

extern JS_PUBLIC_API JSObject* JS_ExtensibleLexicalEnvironment(JSObject* obj) {
  return ExtensibleLexicalEnvironmentObject::forVarEnvironment(obj);
}

JS_PUBLIC_API JSObject* JS::CurrentGlobalOrNull(JSContext* cx) {
  AssertHeapIsIdleOrIterating();
  CHECK_THREAD(cx);
  if (!cx->realm()) {
    return nullptr;
  }
  return cx->global();
}

JS_PUBLIC_API JSObject* JS::GetNonCCWObjectGlobal(JSObject* obj) {
  AssertHeapIsIdleOrIterating();
  MOZ_DIAGNOSTIC_ASSERT(!IsCrossCompartmentWrapper(obj));
  return &obj->nonCCWGlobal();
}

JS_PUBLIC_API bool JS::detail::ComputeThis(JSContext* cx, Value* vp,
                                           MutableHandleObject thisObject) {
  AssertHeapIsIdle();
  cx->check(vp[0], vp[1]);

  MutableHandleValue thisv = MutableHandleValue::fromMarkedLocation(&vp[1]);
  JSObject* obj = BoxNonStrictThis(cx, thisv);
  if (!obj) {
    return false;
  }

  thisObject.set(obj);
  return true;
}

static bool gProfileTimelineRecordingEnabled = false;

JS_PUBLIC_API void JS::SetProfileTimelineRecordingEnabled(bool enabled) {
  gProfileTimelineRecordingEnabled = enabled;
}

JS_PUBLIC_API bool JS::IsProfileTimelineRecordingEnabled() {
  return gProfileTimelineRecordingEnabled;
}

JS_PUBLIC_API void* JS_malloc(JSContext* cx, size_t nbytes) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  return static_cast<void*>(cx->maybe_pod_malloc<uint8_t>(nbytes));
}

JS_PUBLIC_API void* JS_realloc(JSContext* cx, void* p, size_t oldBytes,
                               size_t newBytes) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  return static_cast<void*>(cx->maybe_pod_realloc<uint8_t>(
      static_cast<uint8_t*>(p), oldBytes, newBytes));
}

JS_PUBLIC_API void JS_free(JSContext* cx, void* p) { return js_free(p); }

JS_PUBLIC_API void* JS_string_malloc(JSContext* cx, size_t nbytes) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  return static_cast<void*>(
      cx->maybe_pod_arena_malloc<uint8_t>(js::StringBufferArena, nbytes));
}

JS_PUBLIC_API void* JS_string_realloc(JSContext* cx, void* p, size_t oldBytes,
                                      size_t newBytes) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  return static_cast<void*>(cx->maybe_pod_arena_realloc<uint8_t>(
      js::StringBufferArena, static_cast<uint8_t*>(p), oldBytes, newBytes));
}

JS_PUBLIC_API void JS_string_free(JSContext* cx, void* p) { return js_free(p); }

JS_PUBLIC_API void JS::AddAssociatedMemory(JSObject* obj, size_t nbytes,
                                           JS::MemoryUse use) {
  MOZ_ASSERT(obj);
  if (!nbytes) {
    return;
  }

  Zone* zone = obj->zone();
  MOZ_ASSERT(!IsInsideNursery(obj));
  zone->addCellMemory(obj, nbytes, js::MemoryUse(use));
  zone->maybeTriggerGCOnMalloc();
}

JS_PUBLIC_API void JS::RemoveAssociatedMemory(JSObject* obj, size_t nbytes,
                                              JS::MemoryUse use) {
  MOZ_ASSERT(obj);
  if (!nbytes) {
    return;
  }

  GCContext* gcx = obj->runtimeFromMainThread()->gcContext();
  gcx->removeCellMemory(obj, nbytes, js::MemoryUse(use));
}

#undef JS_AddRoot

JS_PUBLIC_API bool JS_AddExtraGCRootsTracer(JSContext* cx,
                                            JSTraceDataOp traceOp, void* data) {
  return cx->runtime()->gc.addBlackRootsTracer(traceOp, data);
}

JS_PUBLIC_API void JS_RemoveExtraGCRootsTracer(JSContext* cx,
                                               JSTraceDataOp traceOp,
                                               void* data) {
  return cx->runtime()->gc.removeBlackRootsTracer(traceOp, data);
}

JS_PUBLIC_API JS::GCReason JS::WantEagerMinorGC(JSRuntime* rt) {
  if (rt->gc.nursery().wantEagerCollection()) {
    return JS::GCReason::EAGER_NURSERY_COLLECTION;
  }
  return JS::GCReason::NO_REASON;
}

JS_PUBLIC_API JS::GCReason JS::WantEagerMajorGC(JSRuntime* rt) {
  return rt->gc.wantMajorGC(true);
}

JS_PUBLIC_API void JS::MaybeRunNurseryCollection(JSRuntime* rt,
                                                 JS::GCReason reason) {
  gc::GCRuntime& gc = rt->gc;
  if (gc.nursery().wantEagerCollection()) {
    gc.minorGC(reason);
  }
}

JS_PUBLIC_API void JS::RunNurseryCollection(
    JSRuntime* rt, JS::GCReason reason,
    mozilla::TimeDuration aSinceLastMinorGC) {
  gc::GCRuntime& gc = rt->gc;
  if (!gc.nursery().lastCollectionEndTime() ||
      (mozilla::TimeStamp::Now() - gc.nursery().lastCollectionEndTime() >
       aSinceLastMinorGC)) {
    gc.minorGC(reason);
  }
}

JS_PUBLIC_API void JS_GC(JSContext* cx, JS::GCReason reason) {
  AssertHeapIsIdle();
  JS::PrepareForFullGC(cx);
  cx->runtime()->gc.gc(JS::GCOptions::Normal, reason);
}

JS_PUBLIC_API void JS_MaybeGC(JSContext* cx) {
  AssertHeapIsIdle();
  cx->runtime()->gc.maybeGC();
}

JS_PUBLIC_API void JS_SetGCCallback(JSContext* cx, JSGCCallback cb,
                                    void* data) {
  AssertHeapIsIdle();
  cx->runtime()->gc.setGCCallback(cb, data);
}

JS_PUBLIC_API void JS_SetObjectsTenuredCallback(JSContext* cx,
                                                JSObjectsTenuredCallback cb,
                                                void* data) {
  AssertHeapIsIdle();
  cx->runtime()->gc.setObjectsTenuredCallback(cb, data);
}

JS_PUBLIC_API bool JS_AddFinalizeCallback(JSContext* cx, JSFinalizeCallback cb,
                                          void* data) {
  AssertHeapIsIdle();
  return cx->runtime()->gc.addFinalizeCallback(cb, data);
}

JS_PUBLIC_API void JS_RemoveFinalizeCallback(JSContext* cx,
                                             JSFinalizeCallback cb) {
  cx->runtime()->gc.removeFinalizeCallback(cb);
}

JS_PUBLIC_API void JS::SetHostCleanupFinalizationRegistryCallback(
    JSContext* cx, JSHostCleanupFinalizationRegistryCallback cb, void* data) {
  AssertHeapIsIdle();
  cx->runtime()->gc.setHostCleanupFinalizationRegistryCallback(cb, data);
}

JS_PUBLIC_API void JS::ClearKeptObjects(JSContext* cx) {
  gc::GCRuntime* gc = &cx->runtime()->gc;

  for (ZonesIter zone(gc, ZoneSelector::WithAtoms); !zone.done(); zone.next()) {
    zone->clearKeptObjects();
  }
}

JS_PUBLIC_API bool JS::AtomsZoneIsCollecting(JSRuntime* runtime) {
  return runtime->activeGCInAtomsZone();
}

JS_PUBLIC_API bool JS::IsAtomsZone(JS::Zone* zone) {
  return zone->isAtomsZone();
}

JS_PUBLIC_API bool JS_AddWeakPointerZonesCallback(JSContext* cx,
                                                  JSWeakPointerZonesCallback cb,
                                                  void* data) {
  AssertHeapIsIdle();
  return cx->runtime()->gc.addWeakPointerZonesCallback(cb, data);
}

JS_PUBLIC_API void JS_RemoveWeakPointerZonesCallback(
    JSContext* cx, JSWeakPointerZonesCallback cb) {
  cx->runtime()->gc.removeWeakPointerZonesCallback(cb);
}

JS_PUBLIC_API bool JS_AddWeakPointerCompartmentCallback(
    JSContext* cx, JSWeakPointerCompartmentCallback cb, void* data) {
  AssertHeapIsIdle();
  return cx->runtime()->gc.addWeakPointerCompartmentCallback(cb, data);
}

JS_PUBLIC_API void JS_RemoveWeakPointerCompartmentCallback(
    JSContext* cx, JSWeakPointerCompartmentCallback cb) {
  cx->runtime()->gc.removeWeakPointerCompartmentCallback(cb);
}

JS_PUBLIC_API bool JS_UpdateWeakPointerAfterGC(JSTracer* trc,
                                               JS::Heap<JSObject*>* objp) {
  return TraceWeakEdge(trc, objp);
}

JS_PUBLIC_API bool JS_UpdateWeakPointerAfterGCUnbarriered(JSTracer* trc,
                                                          JSObject** objp) {
  return TraceManuallyBarrieredWeakEdge(trc, objp, "External weak pointer");
}

JS_PUBLIC_API void JS_SetGCParameter(JSContext* cx, JSGCParamKey key,
                                     uint32_t value) {
  // Bug 1742118: JS_SetGCParameter has no way to return an error
  // The GC ignores invalid values internally but this is not reported to the
  // caller.
  (void)cx->runtime()->gc.setParameter(cx, key, value);
}

JS_PUBLIC_API void JS_ResetGCParameter(JSContext* cx, JSGCParamKey key) {
  cx->runtime()->gc.resetParameter(cx, key);
}

JS_PUBLIC_API uint32_t JS_GetGCParameter(JSContext* cx, JSGCParamKey key) {
  return cx->runtime()->gc.getParameter(key);
}

JS_PUBLIC_API void JS_SetGCParametersBasedOnAvailableMemory(
    JSContext* cx, uint32_t availMemMB) {
  struct JSGCConfig {
    JSGCParamKey key;
    uint32_t value;
  };

  static const JSGCConfig minimal[] = {
      {JSGC_SLICE_TIME_BUDGET_MS, 5},
      {JSGC_HIGH_FREQUENCY_TIME_LIMIT, 1500},
      {JSGC_LARGE_HEAP_SIZE_MIN, 250},
      {JSGC_SMALL_HEAP_SIZE_MAX, 50},
      {JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH, 300},
      {JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH, 120},
      {JSGC_LOW_FREQUENCY_HEAP_GROWTH, 120},
      {JSGC_ALLOCATION_THRESHOLD, 15},
      {JSGC_MALLOC_THRESHOLD_BASE, 20},
      {JSGC_SMALL_HEAP_INCREMENTAL_LIMIT, 200},
      {JSGC_LARGE_HEAP_INCREMENTAL_LIMIT, 110},
      {JSGC_URGENT_THRESHOLD_MB, 8}};

  static const JSGCConfig nominal[] = {
      {JSGC_SLICE_TIME_BUDGET_MS, 5},
      {JSGC_HIGH_FREQUENCY_TIME_LIMIT, 1000},
      {JSGC_LARGE_HEAP_SIZE_MIN, 500},
      {JSGC_SMALL_HEAP_SIZE_MAX, 100},
      {JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH, 300},
      {JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH, 150},
      {JSGC_LOW_FREQUENCY_HEAP_GROWTH, 150},
      {JSGC_ALLOCATION_THRESHOLD, 27},
      {JSGC_MALLOC_THRESHOLD_BASE, 38},
      {JSGC_SMALL_HEAP_INCREMENTAL_LIMIT, 150},
      {JSGC_LARGE_HEAP_INCREMENTAL_LIMIT, 110},
      {JSGC_URGENT_THRESHOLD_MB, 16}};

  const auto& configSet = availMemMB > 512 ? nominal : minimal;
  for (const auto& config : configSet) {
    JS_SetGCParameter(cx, config.key, config.value);
  }
}

JS_PUBLIC_API JSString* JS_NewExternalStringLatin1(
    JSContext* cx, const Latin1Char* chars, size_t length,
    const JSExternalStringCallbacks* callbacks) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  return JSExternalString::new_(cx, chars, length, callbacks);
}

JS_PUBLIC_API JSString* JS_NewExternalUCString(
    JSContext* cx, const char16_t* chars, size_t length,
    const JSExternalStringCallbacks* callbacks) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  return JSExternalString::new_(cx, chars, length, callbacks);
}

JS_PUBLIC_API JSString* JS_NewMaybeExternalStringLatin1(
    JSContext* cx, const JS::Latin1Char* chars, size_t length,
    const JSExternalStringCallbacks* callbacks, bool* allocatedExternal) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  return NewMaybeExternalString(cx, chars, length, callbacks,
                                allocatedExternal);
}

JS_PUBLIC_API JSString* JS_NewMaybeExternalStringUTF8(
    JSContext* cx, const JS::UTF8Chars& utf8,
    const JSExternalStringCallbacks* callbacks, bool* allocatedExternal) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);

  JS::SmallestEncoding encoding = JS::FindSmallestEncoding(utf8);
  if (encoding == JS::SmallestEncoding::ASCII) {
    // ASCII case can use the external buffer as Latin1 buffer.
    return NewMaybeExternalString(
        cx, reinterpret_cast<JS::Latin1Char*>(utf8.begin().get()),
        utf8.length(), callbacks, allocatedExternal);
  }

  // Non-ASCII case cannot use the external buffer.
  *allocatedExternal = false;
  return NewStringCopyUTF8N(cx, utf8, encoding);
}

JS_PUBLIC_API JSString* JS_NewMaybeExternalUCString(
    JSContext* cx, const char16_t* chars, size_t length,
    const JSExternalStringCallbacks* callbacks, bool* allocatedExternal) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  return NewMaybeExternalString(cx, chars, length, callbacks,
                                allocatedExternal);
}

extern JS_PUBLIC_API const JSExternalStringCallbacks*
JS_GetExternalStringCallbacks(JSString* str) {
  return str->asExternal().callbacks();
}

static void SetNativeStackSize(JSContext* cx, JS::StackKind kind,
                               JS::NativeStackSize stackSize) {
#ifdef __wasi__
  cx->nativeStackLimit[kind] = JS::WASINativeStackLimit;
#else   // __wasi__
  if (stackSize == 0) {
    cx->nativeStackLimit[kind] = JS::NativeStackLimitMax;
  } else {
    cx->nativeStackLimit[kind] =
        JS::GetNativeStackLimit(cx->nativeStackBase(), stackSize - 1);
  }
#endif  // !__wasi__
}

JS_PUBLIC_API void JS_SetNativeStackQuota(
    JSContext* cx, JS::NativeStackSize systemCodeStackSize,
    JS::NativeStackSize trustedScriptStackSize,
    JS::NativeStackSize untrustedScriptStackSize) {
  MOZ_ASSERT(!cx->activation());

  if (!trustedScriptStackSize) {
    trustedScriptStackSize = systemCodeStackSize;
  } else {
    MOZ_ASSERT(trustedScriptStackSize < systemCodeStackSize);
  }

  if (!untrustedScriptStackSize) {
    untrustedScriptStackSize = trustedScriptStackSize;
  } else {
    MOZ_ASSERT(untrustedScriptStackSize < trustedScriptStackSize);
  }

  SetNativeStackSize(cx, JS::StackForSystemCode, systemCodeStackSize);
  SetNativeStackSize(cx, JS::StackForTrustedScript, trustedScriptStackSize);
  SetNativeStackSize(cx, JS::StackForUntrustedScript, untrustedScriptStackSize);

  cx->initJitStackLimit();
}

/************************************************************************/

JS_PUBLIC_API bool JS_ValueToId(JSContext* cx, HandleValue value,
                                MutableHandleId idp) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->check(value);
  return ToPropertyKey(cx, value, idp);
}

JS_PUBLIC_API bool JS_StringToId(JSContext* cx, HandleString string,
                                 MutableHandleId idp) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->check(string);
  RootedValue value(cx, StringValue(string));
  return PrimitiveValueToId<CanGC>(cx, value, idp);
}

JS_PUBLIC_API bool JS_IdToValue(JSContext* cx, jsid id, MutableHandleValue vp) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->check(id);
  vp.set(IdToValue(id));
  cx->check(vp);
  return true;
}

JS_PUBLIC_API bool JS::ToPrimitive(JSContext* cx, HandleObject obj, JSType hint,
                                   MutableHandleValue vp) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->check(obj);
  MOZ_ASSERT(obj != nullptr);
  MOZ_ASSERT(hint == JSTYPE_UNDEFINED || hint == JSTYPE_STRING ||
             hint == JSTYPE_NUMBER);
  vp.setObject(*obj);
  return ToPrimitiveSlow(cx, hint, vp);
}

JS_PUBLIC_API bool JS::GetFirstArgumentAsTypeHint(JSContext* cx,
                                                  const CallArgs& args,
                                                  JSType* result) {
  if (!args.get(0).isString()) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_NOT_EXPECTED_TYPE, "Symbol.toPrimitive",
                              "\"string\", \"number\", or \"default\"",
                              InformalValueTypeName(args.get(0)));
    return false;
  }

  RootedString str(cx, args.get(0).toString());
  bool match;

  if (!EqualStrings(cx, str, cx->names().default_, &match)) {
    return false;
  }
  if (match) {
    *result = JSTYPE_UNDEFINED;
    return true;
  }

  if (!EqualStrings(cx, str, cx->names().string, &match)) {
    return false;
  }
  if (match) {
    *result = JSTYPE_STRING;
    return true;
  }

  if (!EqualStrings(cx, str, cx->names().number, &match)) {
    return false;
  }
  if (match) {
    *result = JSTYPE_NUMBER;
    return true;
  }

  UniqueChars bytes;
  const char* source = ValueToSourceForError(cx, args.get(0), bytes);
  MOZ_ASSERT(source);

  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                           JSMSG_NOT_EXPECTED_TYPE, "Symbol.toPrimitive",
                           "\"string\", \"number\", or \"default\"", source);
  return false;
}

JS_PUBLIC_API JSObject* JS_InitClass(
    JSContext* cx, HandleObject obj, const JSClass* protoClass,
    HandleObject protoProto, const char* name, JSNative constructor,
    unsigned nargs, const JSPropertySpec* ps, const JSFunctionSpec* fs,
    const JSPropertySpec* static_ps, const JSFunctionSpec* static_fs) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->check(obj, protoProto);
  return InitClass(cx, obj, protoClass, protoProto, name, constructor, nargs,
                   ps, fs, static_ps, static_fs);
}

JS_PUBLIC_API bool JS_LinkConstructorAndPrototype(JSContext* cx,
                                                  HandleObject ctor,
                                                  HandleObject proto) {
  return LinkConstructorAndPrototype(cx, ctor, proto);
}

JS_PUBLIC_API bool JS_InstanceOf(JSContext* cx, HandleObject obj,
                                 const JSClass* clasp, CallArgs* args) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
#ifdef DEBUG
  if (args) {
    cx->check(obj);
    cx->check(args->thisv(), args->calleev());
  }
#endif
  if (!obj || obj->getClass() != clasp) {
    if (args) {
      ReportIncompatibleMethod(cx, *args, clasp);
    }
    return false;
  }
  return true;
}

JS_PUBLIC_API bool JS_HasInstance(JSContext* cx, HandleObject obj,
                                  HandleValue value, bool* bp) {
  AssertHeapIsIdle();
  cx->check(obj, value);
  return InstanceofOperator(cx, obj, value, bp);
}

JS_PUBLIC_API JSObject* JS_GetConstructor(JSContext* cx, HandleObject proto) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->check(proto);

  RootedValue cval(cx);
  if (!GetProperty(cx, proto, proto, cx->names().constructor, &cval)) {
    return nullptr;
  }
  if (!IsFunctionObject(cval)) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_NO_CONSTRUCTOR, proto->getClass()->name);
    return nullptr;
  }
  return &cval.toObject();
}

JS::RealmCreationOptions&
JS::RealmCreationOptions::setNewCompartmentInSystemZone() {
  compSpec_ = CompartmentSpecifier::NewCompartmentInSystemZone;
  comp_ = nullptr;
  return *this;
}

JS::RealmCreationOptions&
JS::RealmCreationOptions::setNewCompartmentInExistingZone(JSObject* obj) {
  compSpec_ = CompartmentSpecifier::NewCompartmentInExistingZone;
  zone_ = obj->zone();
  return *this;
}

JS::RealmCreationOptions& JS::RealmCreationOptions::setExistingCompartment(
    JSObject* obj) {
  compSpec_ = CompartmentSpecifier::ExistingCompartment;
  comp_ = obj->compartment();
  return *this;
}

JS::RealmCreationOptions& JS::RealmCreationOptions::setExistingCompartment(
    JS::Compartment* compartment) {
  compSpec_ = CompartmentSpecifier::ExistingCompartment;
  comp_ = compartment;
  return *this;
}

JS::RealmCreationOptions& JS::RealmCreationOptions::setNewCompartmentAndZone() {
  compSpec_ = CompartmentSpecifier::NewCompartmentAndZone;
  comp_ = nullptr;
  return *this;
}

const JS::RealmCreationOptions& JS::RealmCreationOptionsRef(Realm* realm) {
  return realm->creationOptions();
}

const JS::RealmCreationOptions& JS::RealmCreationOptionsRef(JSContext* cx) {
  return cx->realm()->creationOptions();
}

bool JS::RealmCreationOptions::getSharedMemoryAndAtomicsEnabled() const {
  return sharedMemoryAndAtomics_;
}

JS::RealmCreationOptions&
JS::RealmCreationOptions::setSharedMemoryAndAtomicsEnabled(bool flag) {
  sharedMemoryAndAtomics_ = flag;
  return *this;
}

bool JS::RealmCreationOptions::getCoopAndCoepEnabled() const {
  return coopAndCoep_;
}

JS::RealmCreationOptions& JS::RealmCreationOptions::setCoopAndCoepEnabled(
    bool flag) {
  coopAndCoep_ = flag;
  return *this;
}

JS::RealmCreationOptions& JS::RealmCreationOptions::setLocaleCopyZ(
    const char* locale) {
  const size_t size = strlen(locale) + 1;

  AutoEnterOOMUnsafeRegion oomUnsafe;
  char* memoryPtr = js_pod_malloc<char>(sizeof(LocaleString) + size);
  if (!memoryPtr) {
    oomUnsafe.crash("RealmCreationOptions::setLocaleCopyZ");
  }

  char* localePtr = memoryPtr + sizeof(LocaleString);
  memcpy(localePtr, locale, size);

  locale_ = new (memoryPtr) LocaleString(localePtr);

  return *this;
}

const JS::RealmBehaviors& JS::RealmBehaviorsRef(JS::Realm* realm) {
  return realm->behaviors();
}

const JS::RealmBehaviors& JS::RealmBehaviorsRef(JSContext* cx) {
  return cx->realm()->behaviors();
}

void JS::SetRealmNonLive(Realm* realm) { realm->setNonLive(); }

void JS::SetRealmReduceTimerPrecisionCallerType(Realm* realm,
                                                JS::RTPCallerTypeToken type) {
  realm->setReduceTimerPrecisionCallerType(type);
}

JS_PUBLIC_API JSObject* JS_NewGlobalObject(JSContext* cx, const JSClass* clasp,
                                           JSPrincipals* principals,
                                           JS::OnNewGlobalHookOption hookOption,
                                           const JS::RealmOptions& options) {
  MOZ_RELEASE_ASSERT(
      cx->runtime()->hasInitializedSelfHosting(),
      "Must call JS::InitSelfHostedCode() before creating a global");

  AssertHeapIsIdle();
  CHECK_THREAD(cx);

  return GlobalObject::new_(cx, clasp, principals, hookOption, options);
}

JS_PUBLIC_API void JS_GlobalObjectTraceHook(JSTracer* trc, JSObject* global) {
  GlobalObject* globalObj = &global->as<GlobalObject>();
  Realm* globalRealm = globalObj->realm();

  // If we GC when creating the global, we may not have set that global's
  // realm's global pointer yet. In this case, the realm will not yet contain
  // anything that needs to be traced.
  if (globalRealm->unsafeUnbarrieredMaybeGlobal() != globalObj) {
    return;
  }

  // Trace the realm for any GC things that should only stick around if we
  // know the global is live.
  globalRealm->traceGlobalData(trc);

  globalObj->traceData(trc, globalObj);

  if (JSTraceOp trace = globalRealm->creationOptions().getTrace()) {
    trace(trc, global);
  }
}

const JSClassOps JS::DefaultGlobalClassOps = {
    nullptr,                         // addProperty
    nullptr,                         // delProperty
    nullptr,                         // enumerate
    JS_NewEnumerateStandardClasses,  // newEnumerate
    JS_ResolveStandardClass,         // resolve
    JS_MayResolveStandardClass,      // mayResolve
    nullptr,                         // finalize
    nullptr,                         // call
    nullptr,                         // construct
    JS_GlobalObjectTraceHook,        // trace
};

JS_PUBLIC_API void JS_FireOnNewGlobalObject(JSContext* cx,
                                            JS::HandleObject global) {
  // This hook is infallible, because we don't really want arbitrary script
  // to be able to throw errors during delicate global creation routines.
  // This infallibility will eat OOM and slow script, but if that happens
  // we'll likely run up into them again soon in a fallible context.
  cx->check(global);

  Rooted<js::GlobalObject*> globalObject(cx, &global->as<GlobalObject>());
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  if (JS::GetReduceMicrosecondTimePrecisionCallback()) {
    MOZ_DIAGNOSTIC_ASSERT(globalObject->realm()
                              ->behaviors()
                              .reduceTimerPrecisionCallerType()
                              .isSome(),
                          "Trying to create a global without setting an "
                          "explicit RTPCallerType!");
  }
#endif
  DebugAPI::onNewGlobalObject(cx, globalObject);
  cx->runtime()->ensureRealmIsRecordingAllocations(globalObject);
}

JS_PUBLIC_API JSObject* JS_NewObject(JSContext* cx, const JSClass* clasp) {
  MOZ_ASSERT(!cx->zone()->isAtomsZone());
  AssertHeapIsIdle();
  CHECK_THREAD(cx);

  if (!clasp) {
    // Default class is Object.
    return NewPlainObject(cx);
  }

  MOZ_ASSERT(!clasp->isJSFunction());
  MOZ_ASSERT(clasp != &PlainObject::class_);
  MOZ_ASSERT(clasp != &ArrayObject::class_);
  MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));

  return NewBuiltinClassInstance(cx, clasp);
}

JS_PUBLIC_API JSObject* JS_NewObjectWithGivenProto(JSContext* cx,
                                                   const JSClass* clasp,
                                                   HandleObject proto) {
  MOZ_ASSERT(!cx->zone()->isAtomsZone());
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->check(proto);

  if (!clasp) {
    // Default class is Object.
    return NewPlainObjectWithProto(cx, proto);
  }

  MOZ_ASSERT(!clasp->isJSFunction());
  MOZ_ASSERT(clasp != &PlainObject::class_);
  MOZ_ASSERT(clasp != &ArrayObject::class_);
  MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));

  return NewObjectWithGivenProto(cx, clasp, proto);
}

JS_PUBLIC_API JSObject* JS_NewObjectWithGivenProtoAndUseAllocSite(
    JSContext* cx, const JSClass* clasp, HandleObject proto) {
  MOZ_ASSERT(!cx->zone()->isAtomsZone());
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->check(proto);

  MOZ_ASSERT(clasp);
  MOZ_ASSERT(!clasp->isJSFunction());
  MOZ_ASSERT(clasp != &PlainObject::class_);
  MOZ_ASSERT(clasp != &ArrayObject::class_);
  MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));

  return NewObjectWithGivenProtoAndAllocSite(cx, clasp, proto,
                                             cx->realm()->localAllocSite);
}

JS_PUBLIC_API JSObject* JS_NewPlainObject(JSContext* cx) {
  MOZ_ASSERT(!cx->zone()->isAtomsZone());
  AssertHeapIsIdle();
  CHECK_THREAD(cx);

  return NewPlainObject(cx);
}

JS_PUBLIC_API JSObject* JS_NewObjectForConstructor(JSContext* cx,
                                                   const JSClass* clasp,
                                                   const CallArgs& args) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);

  MOZ_ASSERT(!clasp->isJSFunction());
  MOZ_ASSERT(clasp != &PlainObject::class_);
  MOZ_ASSERT(clasp != &ArrayObject::class_);
  MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));

  if (!ThrowIfNotConstructing(cx, args, clasp->name)) {
    return nullptr;
  }

  RootedObject newTarget(cx, &args.newTarget().toObject());
  cx->check(newTarget);

  RootedObject proto(cx);
  if (!GetPrototypeFromConstructor(cx, newTarget,
                                   JSCLASS_CACHED_PROTO_KEY(clasp), &proto)) {
    return nullptr;
  }

  return NewObjectWithClassProto(cx, clasp, proto);
}

JS_PUBLIC_API bool JS_IsNative(JSObject* obj) {
  return obj->is<NativeObject>();
}

JS_PUBLIC_API void JS::AssertObjectBelongsToCurrentThread(JSObject* obj) {
  JSRuntime* rt = obj->compartment()->runtimeFromAnyThread();
  MOZ_RELEASE_ASSERT(CurrentThreadCanAccessRuntime(rt));
}

JS_PUBLIC_API void JS::SetFilenameValidationCallback(
    JS::FilenameValidationCallback cb) {
  js::gFilenameValidationCallback = cb;
}

JS_PUBLIC_API void JS::SetHostEnsureCanAddPrivateElementHook(
    JSContext* cx, JS::EnsureCanAddPrivateElementOp op) {
  cx->runtime()->canAddPrivateElement = op;
}

JS_PUBLIC_API bool JS::SetBrittleMode(JSContext* cx, bool setting) {
  bool wasBrittle = cx->brittleMode;
  cx->brittleMode = setting;
  return wasBrittle;
}

/*** Standard internal methods **********************************************/

JS_PUBLIC_API bool JS_GetPrototype(JSContext* cx, HandleObject obj,
                                   MutableHandleObject result) {
  cx->check(obj);
  return GetPrototype(cx, obj, result);
}

JS_PUBLIC_API bool JS_SetPrototype(JSContext* cx, HandleObject obj,
                                   HandleObject proto) {
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  cx->check(obj, proto);

  return SetPrototype(cx, obj, proto);
}

JS_PUBLIC_API bool JS_GetPrototypeIfOrdinary(JSContext* cx, HandleObject obj,
                                             bool* isOrdinary,
                                             MutableHandleObject result) {
  cx->check(obj);
  return GetPrototypeIfOrdinary(cx, obj, isOrdinary, result);
}

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

--> maximum size reached

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

98%


¤ Dauer der Verarbeitung: 0.27 Sekunden  (vorverarbeitet)  ¤

*© 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 ist 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