/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * 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/. */

#ifndef jsfriendapi_h
#define jsfriendapi_h

#include "mozilla/Atomics.h"
#include "mozilla/Casting.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/Maybe.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/UniquePtr.h"

#include "jsapi.h"  // For JSAutoByteString.  See bug 1033916.
#include "jspubtd.h"

#include "js/CallArgs.h"
#include "js/CallNonGenericMethod.h"
#include "js/Class.h"
#include "js/HeapAPI.h"
#include "js/TypeDecls.h"
#include "js/Utility.h"

#ifndef JS_STACK_GROWTH_DIRECTION
#ifdef __hppa
#define JS_STACK_GROWTH_DIRECTION (1)
#else
#define JS_STACK_GROWTH_DIRECTION (-1)
#endif
#endif

#if JS_STACK_GROWTH_DIRECTION > 0
#define JS_CHECK_STACK_SIZE(limit, sp) (MOZ_LIKELY((uintptr_t)(sp) < (limit)))
#else
#define JS_CHECK_STACK_SIZE(limit, sp) (MOZ_LIKELY((uintptr_t)(sp) > (limit)))
#endif

struct JSErrorFormatString;
class JSLinearString;
struct JSJitInfo;
class JSErrorReport;

namespace JS {
template <class T>
class Heap;
} /* namespace JS */

namespace js {
class JS_FRIEND_API BaseProxyHandler;
class InterpreterFrame;
} /* namespace js */

extern JS_FRIEND_API void JS_SetGrayGCRootsTracer(JSContext* cx,
                                                  JSTraceDataOp traceOp,
                                                  void* data);

extern JS_FRIEND_API JSObject* JS_FindCompilationScope(JSContext* cx,
                                                       JS::HandleObject obj);

extern JS_FRIEND_API JSFunction* JS_GetObjectFunction(JSObject* obj);

extern JS_FRIEND_API bool JS_SplicePrototype(JSContext* cx,
                                             JS::HandleObject obj,
                                             JS::HandleObject proto);

extern JS_FRIEND_API JSObject* JS_NewObjectWithUniqueType(
    JSContext* cx, const JSClass* clasp, JS::HandleObject proto);

/**
 * Allocate an object in exactly the same way as JS_NewObjectWithGivenProto, but
 * without invoking the metadata callback on it.  This allows creation of
 * internal bookkeeping objects that are guaranteed to not have metadata
 * attached to them.
 */
extern JS_FRIEND_API JSObject* JS_NewObjectWithoutMetadata(
    JSContext* cx, const JSClass* clasp, JS::Handle<JSObject*> proto);

extern JS_FRIEND_API uint32_t JS_ObjectCountDynamicSlots(JS::HandleObject obj);

extern JS_FRIEND_API bool JS_NondeterministicGetWeakMapKeys(
    JSContext* cx, JS::HandleObject obj, JS::MutableHandleObject ret);

extern JS_FRIEND_API bool JS_NondeterministicGetWeakSetKeys(
    JSContext* cx, JS::HandleObject obj, JS::MutableHandleObject ret);

// Raw JSScript* because this needs to be callable from a signal handler.
extern JS_FRIEND_API unsigned JS_PCToLineNumber(JSScript* script,
                                                jsbytecode* pc,
                                                unsigned* columnp = nullptr);

/**
 * Determine whether the given object is backed by a DeadObjectProxy.
 *
 * Such objects hold no other objects (they have no outgoing reference edges)
 * and will throw if you touch them (e.g. by reading/writing a property).
 */
extern JS_FRIEND_API bool JS_IsDeadWrapper(JSObject* obj);

/**
 * Creates a new dead wrapper object in the given scope. To be used when
 * attempting to wrap objects from scopes which are already dead.
 *
 * If origObject is passed, it must be an proxy object, and will be
 * used to determine the characteristics of the new dead wrapper.
 */
extern JS_FRIEND_API JSObject* JS_NewDeadWrapper(
    JSContext* cx, JSObject* origObject = nullptr);

/**
 * Determine whether the given object is a ScriptSourceObject.
 */
extern JS_FRIEND_API bool JS_IsScriptSourceObject(JSObject* obj);

/*
 * Used by the cycle collector to trace through a shape or object group and
 * all cycle-participating data it reaches, using bounded stack space.
 */
extern JS_FRIEND_API void JS_TraceShapeCycleCollectorChildren(
    JS::CallbackTracer* trc, JS::GCCellPtr shape);
extern JS_FRIEND_API void JS_TraceObjectGroupCycleCollectorChildren(
    JS::CallbackTracer* trc, JS::GCCellPtr group);

/*
 * Telemetry reasons passed to the accumulate telemetry callback.
 *
 * It's OK for these enum values to change as they will be mapped to a fixed
 * member of the mozilla::Telemetry::HistogramID enum by the callback.
 */
enum {
  JS_TELEMETRY_GC_REASON,
  JS_TELEMETRY_GC_IS_ZONE_GC,
  JS_TELEMETRY_GC_MS,
  JS_TELEMETRY_GC_BUDGET_MS,
  JS_TELEMETRY_GC_BUDGET_OVERRUN,
  JS_TELEMETRY_GC_ANIMATION_MS,
  JS_TELEMETRY_GC_MAX_PAUSE_MS_2,
  JS_TELEMETRY_GC_MARK_MS,
  JS_TELEMETRY_GC_SWEEP_MS,
  JS_TELEMETRY_GC_COMPACT_MS,
  JS_TELEMETRY_GC_MARK_ROOTS_MS,
  JS_TELEMETRY_GC_MARK_GRAY_MS,
  JS_TELEMETRY_GC_SLICE_MS,
  JS_TELEMETRY_GC_SLOW_PHASE,
  JS_TELEMETRY_GC_SLOW_TASK,
  JS_TELEMETRY_GC_MMU_50,
  JS_TELEMETRY_GC_RESET,
  JS_TELEMETRY_GC_RESET_REASON,
  JS_TELEMETRY_GC_INCREMENTAL_DISABLED,
  JS_TELEMETRY_GC_NON_INCREMENTAL,
  JS_TELEMETRY_GC_NON_INCREMENTAL_REASON,
  JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS,
  JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS,
  JS_TELEMETRY_GC_MINOR_REASON,
  JS_TELEMETRY_GC_MINOR_REASON_LONG,
  JS_TELEMETRY_GC_MINOR_US,
  JS_TELEMETRY_GC_NURSERY_BYTES,
  JS_TELEMETRY_GC_PRETENURE_COUNT,
  JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT,
  JS_TELEMETRY_ADDON_EXCEPTIONS,
  JS_TELEMETRY_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS,
  JS_TELEMETRY_WEB_PARSER_COMPILE_LAZY_AFTER_MS,
  JS_TELEMETRY_END
};

typedef void (*JSAccumulateTelemetryDataCallback)(int id, uint32_t sample,
                                                  const char* key);

extern JS_FRIEND_API void JS_SetAccumulateTelemetryCallback(
    JSContext* cx, JSAccumulateTelemetryDataCallback callback);

/*
 * Use counter names passed to the accumulate use counter callback.
 *
 * It's OK to for these enum values to change as they will be mapped to a
 * fixed member of the mozilla::UseCounter enum by the callback.
 */

enum class JSUseCounter { ASMJS, WASM };

typedef void (*JSSetUseCounterCallback)(JSObject* obj, JSUseCounter counter);

extern JS_FRIEND_API void JS_SetSetUseCounterCallback(
    JSContext* cx, JSSetUseCounterCallback callback);

extern JS_FRIEND_API bool JS_GetIsSecureContext(JSCompartment* compartment);

extern JS_FRIEND_API JSPrincipals* JS_GetCompartmentPrincipals(
    JSCompartment* compartment);

extern JS_FRIEND_API void JS_SetCompartmentPrincipals(
    JSCompartment* compartment, JSPrincipals* principals);

extern JS_FRIEND_API JSPrincipals* JS_GetScriptPrincipals(JSScript* script);

namespace js {
extern JS_FRIEND_API JSCompartment* GetScriptCompartment(JSScript* script);
} /* namespace js */

extern JS_FRIEND_API bool JS_ScriptHasMutedErrors(JSScript* script);

extern JS_FRIEND_API JSObject* JS_CloneObject(JSContext* cx,
                                              JS::HandleObject obj,
                                              JS::HandleObject proto);

/**
 * Copy the own properties of src to dst in a fast way.  src and dst must both
 * be native and must be in the compartment of cx.  They must have the same
 * class, the same parent, and the same prototype.  Class reserved slots will
 * NOT be copied.
 *
 * dst must not have any properties on it before this function is called.
 *
 * src must have been allocated via JS_NewObjectWithoutMetadata so that we can
 * be sure it has no metadata that needs copying to dst.  This also means that
 * dst needs to have the compartment global as its parent.  This function will
 * preserve the existing metadata on dst, if any.
 */
extern JS_FRIEND_API bool JS_InitializePropertiesFromCompatibleNativeObject(
    JSContext* cx, JS::HandleObject dst, JS::HandleObject src);

extern JS_FRIEND_API JSString* JS_BasicObjectToString(JSContext* cx,
                                                      JS::HandleObject obj);

namespace js {

JS_FRIEND_API bool GetBuiltinClass(JSContext* cx, JS::HandleObject obj,
                                   ESClass* cls);

JS_FRIEND_API const char* ObjectClassName(JSContext* cx, JS::HandleObject obj);

JS_FRIEND_API void ReportOverRecursed(JSContext* maybecx);

JS_FRIEND_API bool AddRawValueRoot(JSContext* cx, JS::Value* vp,
                                   const char* name);

JS_FRIEND_API void RemoveRawValueRoot(JSContext* cx, JS::Value* vp);

JS_FRIEND_API JSAtom* GetPropertyNameFromPC(JSScript* script, jsbytecode* pc);

#ifdef JS_DEBUG

/*
 * Routines to print out values during debugging. These are FRIEND_API to help
 * the debugger find them and to support temporarily hacking js::Dump* calls
 * into other code. Note that there are overloads that do not require the FILE*
 * parameter, which will default to stderr.
 */

extern JS_FRIEND_API void DumpString(JSString* str, FILE* fp);

extern JS_FRIEND_API void DumpAtom(JSAtom* atom, FILE* fp);

extern JS_FRIEND_API void DumpObject(JSObject* obj, FILE* fp);

extern JS_FRIEND_API void DumpChars(const char16_t* s, size_t n, FILE* fp);

extern JS_FRIEND_API void DumpValue(const JS::Value& val, FILE* fp);

extern JS_FRIEND_API void DumpId(jsid id, FILE* fp);

extern JS_FRIEND_API void DumpInterpreterFrame(
    JSContext* cx, FILE* fp, InterpreterFrame* start = nullptr);

extern JS_FRIEND_API bool DumpPC(JSContext* cx, FILE* fp);

extern JS_FRIEND_API bool DumpScript(JSContext* cx, JSScript* scriptArg,
                                     FILE* fp);

// Versions for use directly in a debugger (default parameters are not handled
// well in gdb; built-in handles like stderr are not handled well in lldb.)
extern JS_FRIEND_API void DumpString(JSString* str);
extern JS_FRIEND_API void DumpAtom(JSAtom* atom);
extern JS_FRIEND_API void DumpObject(JSObject* obj);
extern JS_FRIEND_API void DumpChars(const char16_t* s, size_t n);
extern JS_FRIEND_API void DumpValue(const JS::Value& val);
extern JS_FRIEND_API void DumpId(jsid id);
extern JS_FRIEND_API void DumpInterpreterFrame(
    JSContext* cx, InterpreterFrame* start = nullptr);
extern JS_FRIEND_API bool DumpPC(JSContext* cx);
extern JS_FRIEND_API bool DumpScript(JSContext* cx, JSScript* scriptArg);

#endif

extern JS_FRIEND_API void DumpBacktrace(JSContext* cx, FILE* fp);

extern JS_FRIEND_API void DumpBacktrace(JSContext* cx);

}  // namespace js

namespace JS {

/** Exposed for DumpJSStack */
extern JS_FRIEND_API JS::UniqueChars FormatStackDump(JSContext* cx,
                                                     JS::UniqueChars&& buf,
                                                     bool showArgs,
                                                     bool showLocals,
                                                     bool showThisProps);

/**
 * Set all of the uninitialized lexicals on an object to undefined. Return
 * true if any lexicals were initialized and false otherwise.
 * */
extern JS_FRIEND_API bool ForceLexicalInitialization(JSContext* cx,
                                                     HandleObject obj);

/**
 * Whether we are poisoning unused/released data for error detection. Governed
 * by the JS_GC_POISONING #ifdef as well as the $JSGC_DISABLE_POISONING
 * environment variable.
 */
extern JS_FRIEND_API int IsGCPoisoning();

}  // namespace JS

/**
 * Copies all own properties from |obj| to |target|. |obj| must be a "native"
 * object (that is to say, normal-ish - not an Array or a Proxy).
 *
 * This function immediately enters a compartment, and does not impose any
 * restrictions on the compartment of |cx|.
 */
extern JS_FRIEND_API bool JS_CopyPropertiesFrom(JSContext* cx,
                                                JS::HandleObject target,
                                                JS::HandleObject obj);

/*
 * Single-property version of the above. This function asserts that an |own|
 * property of the given name exists on |obj|.
 *
 * On entry, |cx| must be same-compartment with |obj|.
 *
 * The copyBehavior argument controls what happens with
 * non-configurable properties.
 */
typedef enum {
  MakeNonConfigurableIntoConfigurable,
  CopyNonConfigurableAsIs
} PropertyCopyBehavior;

extern JS_FRIEND_API bool JS_CopyPropertyFrom(
    JSContext* cx, JS::HandleId id, JS::HandleObject target,
    JS::HandleObject obj,
    PropertyCopyBehavior copyBehavior = CopyNonConfigurableAsIs);

extern JS_FRIEND_API bool JS_WrapPropertyDescriptor(
    JSContext* cx, JS::MutableHandle<JS::PropertyDescriptor> desc);

struct JSFunctionSpecWithHelp {
  const char* name;
  JSNative call;
  uint16_t nargs;
  uint16_t flags;
  const JSJitInfo* jitInfo;
  const char* usage;
  const char* help;
};

#define JS_FN_HELP(name, call, nargs, flags, usage, help) \
  { name, call, nargs, (flags) | JSPROP_ENUMERATE, nullptr, usage, help }
#define JS_INLINABLE_FN_HELP(name, call, nargs, flags, native, usage, help)    \
  {                                                                            \
    name, call, nargs, (flags) | JSPROP_ENUMERATE, &js::jit::JitInfo_##native, \
        usage, help                                                            \
  }
#define JS_FS_HELP_END \
  { nullptr, nullptr, 0, 0, nullptr, nullptr }

extern JS_FRIEND_API bool JS_DefineFunctionsWithHelp(
    JSContext* cx, JS::HandleObject obj, const JSFunctionSpecWithHelp* fs);

namespace js {

extern JS_FRIEND_API JSObject* proxy_WeakmapKeyDelegate(JSObject* obj);

/**
 * A class of objects that return source code on demand.
 *
 * When code is compiled with setSourceIsLazy(true), SpiderMonkey doesn't
 * retain the source code (and doesn't do lazy bytecode generation). If we ever
 * need the source code, say, in response to a call to Function.prototype.
 * toSource or Debugger.Source.prototype.text, then we call the 'load' member
 * function of the instance of this class that has hopefully been registered
 * with the runtime, passing the code's URL, and hope that it will be able to
 * find the source.
 */
class SourceHook {
 public:
  virtual ~SourceHook() {}

  /**
   * Set |*src| and |*length| to refer to the source code for |filename|.
   * On success, the caller owns the buffer to which |*src| points, and
   * should use JS_free to free it.
   */
  virtual bool load(JSContext* cx, const char* filename, char16_t** src,
                    size_t* length) = 0;
};

/**
 * Have |cx| use |hook| to retrieve lazily-retrieved source code. See the
 * comments for SourceHook. The context takes ownership of the hook, and
 * will delete it when the context itself is deleted, or when a new hook is
 * set.
 */
extern JS_FRIEND_API void SetSourceHook(JSContext* cx,
                                        mozilla::UniquePtr<SourceHook> hook);

/** Remove |cx|'s source hook, and return it. The caller now owns the hook. */
extern JS_FRIEND_API mozilla::UniquePtr<SourceHook> ForgetSourceHook(
    JSContext* cx);

/**
 * Use the runtime's internal handling of job queues for Promise jobs.
 *
 * Most embeddings, notably web browsers, will have their own task scheduling
 * systems and need to integrate handling of Promise jobs into that, so they
 * will want to manage job queues themselves. For basic embeddings such as the
 * JS shell that don't have an event loop of their own, it's easier to have
 * SpiderMonkey handle job queues internally.
 *
 * Note that the embedding still has to trigger processing of job queues at
 * right time(s), such as after evaluation of a script has run to completion.
 */
extern JS_FRIEND_API bool UseInternalJobQueues(JSContext* cx,
                                               bool cooperative = false);

/**
 * Enqueue |job| on the internal job queue.
 *
 * This is useful in tests for creating situations where a call occurs with no
 * other JavaScript on the stack.
 */
extern JS_FRIEND_API bool EnqueueJob(JSContext* cx, JS::HandleObject job);

/**
 * Instruct the runtime to stop draining the internal job queue.
 *
 * Useful if the embedding is in the process of quitting in reaction to a
 * builtin being called, or if it wants to resume executing jobs later on.
 */
extern JS_FRIEND_API void StopDrainingJobQueue(JSContext* cx);

extern JS_FRIEND_API void RunJobs(JSContext* cx);

extern JS_FRIEND_API JS::Zone* GetCompartmentZone(JSCompartment* comp);

typedef bool (*PreserveWrapperCallback)(JSContext* cx, JSObject* obj);

typedef enum {
  CollectNurseryBeforeDump,
  IgnoreNurseryObjects
} DumpHeapNurseryBehaviour;

/**
 * Dump the complete object graph of heap-allocated things.
 * fp is the file for the dump output.
 */
extern JS_FRIEND_API void DumpHeap(JSContext* cx, FILE* fp,
                                   DumpHeapNurseryBehaviour nurseryBehaviour);

#ifdef JS_OLD_GETTER_SETTER_METHODS
JS_FRIEND_API bool obj_defineGetter(JSContext* cx, unsigned argc,
                                    JS::Value* vp);
JS_FRIEND_API bool obj_defineSetter(JSContext* cx, unsigned argc,
                                    JS::Value* vp);
#endif

extern JS_FRIEND_API bool IsSystemCompartment(JSCompartment* comp);

extern JS_FRIEND_API bool IsSystemZone(JS::Zone* zone);

extern JS_FRIEND_API bool IsAtomsCompartment(JSCompartment* comp);

extern JS_FRIEND_API bool IsAtomsZone(JS::Zone* zone);

struct WeakMapTracer {
  JSRuntime* runtime;

  explicit WeakMapTracer(JSRuntime* rt) : runtime(rt) {}

  // Weak map tracer callback, called once for every binding of every
  // weak map that was live at the time of the last garbage collection.
  //
  // m will be nullptr if the weak map is not contained in a JS Object.
  //
  // The callback should not GC (and will assert in a debug build if it does
  // so.)
  virtual void trace(JSObject* m, JS::GCCellPtr key, JS::GCCellPtr value) = 0;
};

extern JS_FRIEND_API void TraceWeakMaps(WeakMapTracer* trc);

extern JS_FRIEND_API bool AreGCGrayBitsValid(JSRuntime* rt);

extern JS_FRIEND_API bool ZoneGlobalsAreAllGray(JS::Zone* zone);

extern JS_FRIEND_API bool IsObjectZoneSweepingOrCompacting(JSObject* obj);

typedef void (*GCThingCallback)(void* closure, JS::GCCellPtr thing);

extern JS_FRIEND_API void VisitGrayWrapperTargets(JS::Zone* zone,
                                                  GCThingCallback callback,
                                                  void* closure);

extern JS_FRIEND_API JSObject* GetWeakmapKeyDelegate(JSObject* key);

/**
 * Invoke cellCallback on every gray JSObject in the given zone.
 */
extern JS_FRIEND_API void IterateGrayObjects(JS::Zone* zone,
                                             GCThingCallback cellCallback,
                                             void* data);

/**
 * Invoke cellCallback on every gray JSObject in the given zone while cycle
 * collection is in progress.
 */
extern JS_FRIEND_API void IterateGrayObjectsUnderCC(
    JS::Zone* zone, GCThingCallback cellCallback, void* data);

#if defined(JS_GC_ZEAL) || defined(DEBUG)
// Trace the heap and check there are no black to gray edges. These are
// not allowed since the cycle collector could throw away the gray thing and
// leave a dangling pointer.
//
// This doesn't trace weak maps as these are handled separately.
extern JS_FRIEND_API bool CheckGrayMarkingState(JSRuntime* rt);
#endif

#ifdef JS_HAS_CTYPES
extern JS_FRIEND_API size_t
SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf, JSObject* obj);
#endif

extern JS_FRIEND_API JSCompartment* GetAnyCompartmentInZone(JS::Zone* zone);

/*
 * Shadow declarations of JS internal structures, for access by inline access
 * functions below. Do not use these structures in any other way. When adding
 * new fields for access by inline methods, make sure to add static asserts to
 * the original header file to ensure that offsets are consistent.
 */
namespace shadow {

struct ObjectGroup {
  const Class* clasp;
  JSObject* proto;
  JSCompartment* compartment;
};

struct BaseShape {
  const js::Class* clasp_;
  JSObject* parent;
};

class Shape {
 public:
  shadow::BaseShape* base;
  void* _1;
  jsid _2;
  uint32_t slotInfo;

  static const uint32_t FIXED_SLOTS_SHIFT = 27;
};

/**
 * This layout is shared by all native objects. For non-native objects, the
 * group may always be accessed safely, and other members may be as well,
 * depending on the object's specific layout.
 */
struct Object {
  shadow::ObjectGroup* group;
  shadow::Shape* shape;
  JS::Value* slots;
  void* _1;

  static const size_t MAX_FIXED_SLOTS = 16;

  size_t numFixedSlots() const {
    return shape->slotInfo >> Shape::FIXED_SLOTS_SHIFT;
  }
  JS::Value* fixedSlots() const {
    return (JS::Value*)(uintptr_t(this) + sizeof(shadow::Object));
  }

  JS::Value& slotRef(size_t slot) const {
    size_t nfixed = numFixedSlots();
    if (slot < nfixed) return fixedSlots()[slot];
    return slots[slot - nfixed];
  }
};

struct Function {
  Object base;
  uint16_t nargs;
  uint16_t flags;
  /* Used only for natives */
  JSNative native;
  const JSJitInfo* jitinfo;
  void* _1;
};

struct String {
  static const uint32_t NON_ATOM_BIT = JS_BIT(0);
  static const uint32_t LINEAR_BIT = JS_BIT(1);
  static const uint32_t INLINE_CHARS_BIT = JS_BIT(3);
  static const uint32_t LATIN1_CHARS_BIT = JS_BIT(6);
  static const uint32_t EXTERNAL_FLAGS = LINEAR_BIT | NON_ATOM_BIT | JS_BIT(5);
  static const uint32_t TYPE_FLAGS_MASK = JS_BIT(6) - 1;
#if MOZ_LITTLE_ENDIAN || JS_BITS_PER_WORD == 32
  uint32_t flags;
  uint32_t length;
#elif JS_BITS_PER_WORD == 64
  uint32_t length;
  uint32_t flags;
#else
#  error "Unknown endianness or word size"
#endif
  union {
    const JS::Latin1Char* nonInlineCharsLatin1;
    const char16_t* nonInlineCharsTwoByte;
    JS::Latin1Char inlineStorageLatin1[1];
    char16_t inlineStorageTwoByte[1];
  };
  const JSStringFinalizer* externalFinalizer;

  static bool nurseryCellIsString(const js::gc::Cell* cell) {
    MOZ_ASSERT(IsInsideNursery(cell));
    return reinterpret_cast<const String*>(cell)->flags & NON_ATOM_BIT;
  }
};

} /* namespace shadow */

// This is equal to |&JSObject::class_|.  Use it in places where you don't want
// to #include vm/JSObject.h.
extern JS_FRIEND_DATA const js::Class* const ObjectClassPtr;

inline const js::Class* GetObjectClass(const JSObject* obj) {
  return reinterpret_cast<const shadow::Object*>(obj)->group->clasp;
}

inline const JSClass* GetObjectJSClass(JSObject* obj) {
  return js::Jsvalify(GetObjectClass(obj));
}

JS_FRIEND_API const Class* ProtoKeyToClass(JSProtoKey key);

// Returns the key for the class inherited by a given standard class (that
// is to say, the prototype of this standard class's prototype).
//
// You must be sure that this corresponds to a standard class with a cached
// JSProtoKey before calling this function. In general |key| will match the
// cached proto key, except in cases where multiple JSProtoKeys share a
// JSClass.
inline JSProtoKey InheritanceProtoKeyForStandardClass(JSProtoKey key) {
  // [Object] has nothing to inherit from.
  if (key == JSProto_Object) return JSProto_Null;

  // If we're ClassSpec defined return the proto key from that
  if (ProtoKeyToClass(key)->specDefined())
    return ProtoKeyToClass(key)->specInheritanceProtoKey();

  // Otherwise, we inherit [Object].
  return JSProto_Object;
}

JS_FRIEND_API bool IsFunctionObject(JSObject* obj);

static MOZ_ALWAYS_INLINE JSCompartment* GetObjectCompartment(JSObject* obj) {
  return reinterpret_cast<shadow::Object*>(obj)->group->compartment;
}

JS_FRIEND_API JSObject* GetGlobalForObjectCrossCompartment(JSObject* obj);

JS_FRIEND_API JSObject* GetPrototypeNoProxy(JSObject* obj);

JS_FRIEND_API void AssertSameCompartment(JSContext* cx, JSObject* obj);

JS_FRIEND_API void AssertSameCompartment(JSContext* cx, JS::HandleValue v);

#ifdef JS_DEBUG
JS_FRIEND_API void AssertSameCompartment(JSObject* objA, JSObject* objB);
#else
inline void AssertSameCompartment(JSObject* objA, JSObject* objB) {}
#endif

JS_FRIEND_API void NotifyAnimationActivity(JSObject* obj);

JS_FRIEND_API JSFunction* DefineFunctionWithReserved(
    JSContext* cx, JSObject* obj, const char* name, JSNative call,
    unsigned nargs, unsigned attrs);

JS_FRIEND_API JSFunction* NewFunctionWithReserved(JSContext* cx, JSNative call,
                                                  unsigned nargs,
                                                  unsigned flags,
                                                  const char* name);

JS_FRIEND_API JSFunction* NewFunctionByIdWithReserved(JSContext* cx,
                                                      JSNative native,
                                                      unsigned nargs,
                                                      unsigned flags, jsid id);

JS_FRIEND_API const JS::Value& GetFunctionNativeReserved(JSObject* fun,
                                                         size_t which);

JS_FRIEND_API void SetFunctionNativeReserved(JSObject* fun, size_t which,
                                             const JS::Value& val);

JS_FRIEND_API bool FunctionHasNativeReserved(JSObject* fun);

JS_FRIEND_API bool GetObjectProto(JSContext* cx, JS::HandleObject obj,
                                  JS::MutableHandleObject proto);

extern JS_FRIEND_API JSObject* GetStaticPrototype(JSObject* obj);

JS_FRIEND_API bool GetOriginalEval(JSContext* cx, JS::HandleObject scope,
                                   JS::MutableHandleObject eval);

inline void* GetObjectPrivate(JSObject* obj) {
  MOZ_ASSERT(GetObjectClass(obj)->flags & JSCLASS_HAS_PRIVATE);
  const shadow::Object* nobj = reinterpret_cast<const shadow::Object*>(obj);
  void** addr =
      reinterpret_cast<void**>(&nobj->fixedSlots()[nobj->numFixedSlots()]);
  return *addr;
}

/**
 * Get the value stored in an object's reserved slot. This can be used with
 * both native objects and proxies, but if |obj| is known to be a proxy
 * GetProxyReservedSlot is a bit more efficient.
 */
inline const JS::Value& GetReservedSlot(JSObject* obj, size_t slot) {
  MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)));
  return reinterpret_cast<const shadow::Object*>(obj)->slotRef(slot);
}

JS_FRIEND_API void SetReservedSlotWithBarrier(JSObject* obj, size_t slot,
                                              const JS::Value& value);

/**
 * Store a value in an object's reserved slot. This can be used with
 * both native objects and proxies, but if |obj| is known to be a proxy
 * SetProxyReservedSlot is a bit more efficient.
 */
inline void SetReservedSlot(JSObject* obj, size_t slot,
                            const JS::Value& value) {
  MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)));
  shadow::Object* sobj = reinterpret_cast<shadow::Object*>(obj);
  if (sobj->slotRef(slot).isGCThing() || value.isGCThing())
    SetReservedSlotWithBarrier(obj, slot, value);
  else
    sobj->slotRef(slot) = value;
}

JS_FRIEND_API uint32_t GetObjectSlotSpan(JSObject* obj);

inline const JS::Value& GetObjectSlot(JSObject* obj, size_t slot) {
  MOZ_ASSERT(slot < GetObjectSlotSpan(obj));
  return reinterpret_cast<const shadow::Object*>(obj)->slotRef(slot);
}

MOZ_ALWAYS_INLINE size_t GetAtomLength(JSAtom* atom) {
  return reinterpret_cast<shadow::String*>(atom)->length;
}

static const uint32_t MaxStringLength = (1 << 28) - 1;

MOZ_ALWAYS_INLINE size_t GetStringLength(JSString* s) {
  return reinterpret_cast<shadow::String*>(s)->length;
}

MOZ_ALWAYS_INLINE size_t GetFlatStringLength(JSFlatString* s) {
  return reinterpret_cast<shadow::String*>(s)->length;
}

MOZ_ALWAYS_INLINE size_t GetLinearStringLength(JSLinearString* s) {
  return reinterpret_cast<shadow::String*>(s)->length;
}

MOZ_ALWAYS_INLINE bool LinearStringHasLatin1Chars(JSLinearString* s) {
  return reinterpret_cast<shadow::String*>(s)->flags &
         shadow::String::LATIN1_CHARS_BIT;
}

MOZ_ALWAYS_INLINE bool AtomHasLatin1Chars(JSAtom* atom) {
  return reinterpret_cast<shadow::String*>(atom)->flags &
         shadow::String::LATIN1_CHARS_BIT;
}

MOZ_ALWAYS_INLINE bool StringHasLatin1Chars(JSString* s) {
  return reinterpret_cast<shadow::String*>(s)->flags &
         shadow::String::LATIN1_CHARS_BIT;
}

MOZ_ALWAYS_INLINE const JS::Latin1Char* GetLatin1LinearStringChars(
    const JS::AutoRequireNoGC& nogc, JSLinearString* linear) {
  MOZ_ASSERT(LinearStringHasLatin1Chars(linear));

  using shadow::String;
  String* s = reinterpret_cast<String*>(linear);
  if (s->flags & String::INLINE_CHARS_BIT) return s->inlineStorageLatin1;
  return s->nonInlineCharsLatin1;
}

MOZ_ALWAYS_INLINE const char16_t* GetTwoByteLinearStringChars(
    const JS::AutoRequireNoGC& nogc, JSLinearString* linear) {
  MOZ_ASSERT(!LinearStringHasLatin1Chars(linear));

  using shadow::String;
  String* s = reinterpret_cast<String*>(linear);
  if (s->flags & String::INLINE_CHARS_BIT) return s->inlineStorageTwoByte;
  return s->nonInlineCharsTwoByte;
}

MOZ_ALWAYS_INLINE JSLinearString* AtomToLinearString(JSAtom* atom) {
  return reinterpret_cast<JSLinearString*>(atom);
}

MOZ_ALWAYS_INLINE JSFlatString* AtomToFlatString(JSAtom* atom) {
  return reinterpret_cast<JSFlatString*>(atom);
}

MOZ_ALWAYS_INLINE JSLinearString* FlatStringToLinearString(JSFlatString* s) {
  return reinterpret_cast<JSLinearString*>(s);
}

MOZ_ALWAYS_INLINE const JS::Latin1Char* GetLatin1AtomChars(
    const JS::AutoRequireNoGC& nogc, JSAtom* atom) {
  return GetLatin1LinearStringChars(nogc, AtomToLinearString(atom));
}

MOZ_ALWAYS_INLINE const char16_t* GetTwoByteAtomChars(
    const JS::AutoRequireNoGC& nogc, JSAtom* atom) {
  return GetTwoByteLinearStringChars(nogc, AtomToLinearString(atom));
}

MOZ_ALWAYS_INLINE bool IsExternalString(JSString* str,
                                        const JSStringFinalizer** fin,
                                        const char16_t** chars) {
  using shadow::String;
  String* s = reinterpret_cast<String*>(str);

  if ((s->flags & String::TYPE_FLAGS_MASK) != String::EXTERNAL_FLAGS)
    return false;

  MOZ_ASSERT(JS_IsExternalString(str));
  *fin = s->externalFinalizer;
  *chars = s->nonInlineCharsTwoByte;
  return true;
}

JS_FRIEND_API JSLinearString* StringToLinearStringSlow(JSContext* cx,
                                                       JSString* str);

MOZ_ALWAYS_INLINE JSLinearString* StringToLinearString(JSContext* cx,
                                                       JSString* str) {
  using shadow::String;
  String* s = reinterpret_cast<String*>(str);
  if (MOZ_UNLIKELY(!(s->flags & String::LINEAR_BIT)))
    return StringToLinearStringSlow(cx, str);
  return reinterpret_cast<JSLinearString*>(str);
}

template <typename CharType>
MOZ_ALWAYS_INLINE void CopyLinearStringChars(CharType* dest, JSLinearString* s,
                                             size_t len, size_t start = 0);

MOZ_ALWAYS_INLINE void CopyLinearStringChars(char16_t* dest, JSLinearString* s,
                                             size_t len, size_t start = 0) {
  MOZ_ASSERT(start + len <= GetLinearStringLength(s));
  JS::AutoCheckCannotGC nogc;
  if (LinearStringHasLatin1Chars(s)) {
    const JS::Latin1Char* src = GetLatin1LinearStringChars(nogc, s);
    for (size_t i = 0; i < len; i++) dest[i] = src[start + i];
  } else {
    const char16_t* src = GetTwoByteLinearStringChars(nogc, s);
    mozilla::PodCopy(dest, src + start, len);
  }
}

MOZ_ALWAYS_INLINE void CopyLinearStringChars(char* dest, JSLinearString* s,
                                             size_t len, size_t start = 0) {
  MOZ_ASSERT(start + len <= GetLinearStringLength(s));
  JS::AutoCheckCannotGC nogc;
  if (LinearStringHasLatin1Chars(s)) {
    const JS::Latin1Char* src = GetLatin1LinearStringChars(nogc, s);
    for (size_t i = 0; i < len; i++) dest[i] = char(src[start + i]);
  } else {
    const char16_t* src = GetTwoByteLinearStringChars(nogc, s);
    for (size_t i = 0; i < len; i++) dest[i] = char(src[start + i]);
  }
}

template <typename CharType>
inline bool CopyStringChars(JSContext* cx, CharType* dest, JSString* s,
                            size_t len, size_t start = 0) {
  JSLinearString* linear = StringToLinearString(cx, s);
  if (!linear) return false;

  CopyLinearStringChars(dest, linear, len, start);
  return true;
}

inline void CopyFlatStringChars(char16_t* dest, JSFlatString* s, size_t len) {
  CopyLinearStringChars(dest, FlatStringToLinearString(s), len);
}

/**
 * Add some or all property keys of obj to the id vector *props.
 *
 * The flags parameter controls which property keys are added. Pass a
 * combination of the following bits:
 *
 *     JSITER_OWNONLY - Don't also search the prototype chain; only consider
 *       obj's own properties.
 *
 *     JSITER_HIDDEN - Include nonenumerable properties.
 *
 *     JSITER_SYMBOLS - Include property keys that are symbols. The default
 *       behavior is to filter out symbols.
 *
 *     JSITER_SYMBOLSONLY - Exclude non-symbol property keys.
 *
 * This is the closest C++ API we have to `Reflect.ownKeys(obj)`, or
 * equivalently, the ES6 [[OwnPropertyKeys]] internal method. Pass
 * `JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS` as flags to get
 * results that match the output of Reflect.ownKeys.
 */
JS_FRIEND_API bool GetPropertyKeys(JSContext* cx, JS::HandleObject obj,
                                   unsigned flags, JS::AutoIdVector* props);

JS_FRIEND_API bool AppendUnique(JSContext* cx, JS::AutoIdVector& base,
                                JS::AutoIdVector& others);

JS_FRIEND_API bool StringIsArrayIndex(JSLinearString* str, uint32_t* indexp);

JS_FRIEND_API void SetPreserveWrapperCallback(JSContext* cx,
                                              PreserveWrapperCallback callback);

JS_FRIEND_API bool IsObjectInContextCompartment(JSObject* obj,
                                                const JSContext* cx);

/*
 * NB: keep these in sync with the copy in builtin/SelfHostingDefines.h.
 */
/* 0x1 is no longer used */
/* 0x2 is no longer used */
/* 0x4 is no longer used */
#define JSITER_OWNONLY 0x8      /* iterate over obj's own properties only */
#define JSITER_HIDDEN 0x10      /* also enumerate non-enumerable properties */
#define JSITER_SYMBOLS 0x20     /* also include symbol property keys */
#define JSITER_SYMBOLSONLY 0x40 /* exclude string property keys */
#define JSITER_FORAWAITOF 0x80  /* for-await-of */

JS_FRIEND_API bool RunningWithTrustedPrincipals(JSContext* cx);

MOZ_ALWAYS_INLINE uintptr_t GetNativeStackLimit(JSContext* cx,
                                                JS::StackKind kind,
                                                int extraAllowance = 0) {
  uintptr_t limit = JS::RootingContext::get(cx)->nativeStackLimit[kind];
#if JS_STACK_GROWTH_DIRECTION > 0
  limit += extraAllowance;
#else
  limit -= extraAllowance;
#endif
  return limit;
}

MOZ_ALWAYS_INLINE uintptr_t GetNativeStackLimit(JSContext* cx,
                                                int extraAllowance = 0) {
  JS::StackKind kind = RunningWithTrustedPrincipals(cx)
                           ? JS::StackForTrustedScript
                           : JS::StackForUntrustedScript;
  return GetNativeStackLimit(cx, kind, extraAllowance);
}

/*
 * These functions return |false| if we are close to using up the C++ stack.
 * They also report an overrecursion error, except for the DontReport variants.
 * The CheckSystemRecursionLimit variant gives us a little extra space so we
 * can ensure that crucial code is able to run. CheckRecursionLimitConservative
 * allows less space than any other check, including a safety buffer (as in, it
 * uses the untrusted limit and subtracts a little more from it).
 */

MOZ_ALWAYS_INLINE bool CheckRecursionLimit(JSContext* cx, uintptr_t limit) {
  int stackDummy;

  JS_STACK_OOM_POSSIBLY_FAIL_REPORT();

  if (!JS_CHECK_STACK_SIZE(limit, &stackDummy)) {
    ReportOverRecursed(cx);
    return false;
  }
  return true;
}

MOZ_ALWAYS_INLINE bool CheckRecursionLimitDontReport(uintptr_t limit) {
  int stackDummy;

  JS_STACK_OOM_POSSIBLY_FAIL();

  return JS_CHECK_STACK_SIZE(limit, &stackDummy);
}

MOZ_ALWAYS_INLINE bool CheckRecursionLimit(JSContext* cx) {
  JS_STACK_OOM_POSSIBLY_FAIL_REPORT();

  // GetNativeStackLimit(cx) is pretty slow because it has to do an uninlined
  // call to RunningWithTrustedPrincipals to determine which stack limit to
  // use. To work around this, check the untrusted limit first to avoid the
  // overhead in most cases.
  uintptr_t untrustedLimit =
      GetNativeStackLimit(cx, JS::StackForUntrustedScript);
  if (MOZ_LIKELY(CheckRecursionLimitDontReport(untrustedLimit))) return true;
  return CheckRecursionLimit(cx, GetNativeStackLimit(cx));
}

MOZ_ALWAYS_INLINE bool CheckRecursionLimitDontReport(JSContext* cx) {
  return CheckRecursionLimitDontReport(GetNativeStackLimit(cx));
}

MOZ_ALWAYS_INLINE bool CheckRecursionLimitWithStackPointerDontReport(
    JSContext* cx, void* sp) {
  JS_STACK_OOM_POSSIBLY_FAIL();

  return JS_CHECK_STACK_SIZE(GetNativeStackLimit(cx), sp);
}

MOZ_ALWAYS_INLINE bool CheckRecursionLimitWithStackPointer(JSContext* cx,
                                                           void* sp) {
  JS_STACK_OOM_POSSIBLY_FAIL_REPORT();

  if (!JS_CHECK_STACK_SIZE(GetNativeStackLimit(cx), sp)) {
    ReportOverRecursed(cx);
    return false;
  }
  return true;
}

MOZ_ALWAYS_INLINE bool CheckSystemRecursionLimit(JSContext* cx) {
  return CheckRecursionLimit(cx,
                             GetNativeStackLimit(cx, JS::StackForSystemCode));
}

MOZ_ALWAYS_INLINE bool CheckRecursionLimitConservative(JSContext* cx) {
  return CheckRecursionLimit(
      cx, GetNativeStackLimit(cx, JS::StackForUntrustedScript,
                              -1024 * int(sizeof(size_t))));
}

MOZ_ALWAYS_INLINE bool CheckRecursionLimitConservativeDontReport(
    JSContext* cx) {
  return CheckRecursionLimitDontReport(GetNativeStackLimit(
      cx, JS::StackForUntrustedScript, -1024 * int(sizeof(size_t))));
}

JS_FRIEND_API void StartPCCountProfiling(JSContext* cx);

JS_FRIEND_API void StopPCCountProfiling(JSContext* cx);

JS_FRIEND_API void PurgePCCounts(JSContext* cx);

JS_FRIEND_API size_t GetPCCountScriptCount(JSContext* cx);

JS_FRIEND_API JSString* GetPCCountScriptSummary(JSContext* cx, size_t script);

JS_FRIEND_API JSString* GetPCCountScriptContents(JSContext* cx, size_t script);

/**
 * Generate lcov trace file content for the current compartment, and allocate a
 * new buffer and return the content in it, the size of the newly allocated
 * content within the buffer would be set to the length out-param.
 *
 * In case of out-of-memory, this function returns nullptr and does not set any
 * value to the length out-param.
 */
JS_FRIEND_API char* GetCodeCoverageSummary(JSContext* cx, size_t* length);

typedef void (*ActivityCallback)(void* arg, bool active);

/**
 * Sets a callback that is run whenever the runtime goes idle - the
 * last active request ceases - and begins activity - when it was
 * idle and a request begins.
 */
JS_FRIEND_API void SetActivityCallback(JSContext* cx, ActivityCallback cb,
                                       void* arg);

typedef bool (*DOMInstanceClassHasProtoAtDepth)(const Class* instanceClass,
                                                uint32_t protoID,
                                                uint32_t depth);
struct JSDOMCallbacks {
  DOMInstanceClassHasProtoAtDepth instanceClassMatchesProto;
};
typedef struct JSDOMCallbacks DOMCallbacks;

extern JS_FRIEND_API void SetDOMCallbacks(JSContext* cx,
                                          const DOMCallbacks* callbacks);

extern JS_FRIEND_API const DOMCallbacks* GetDOMCallbacks(JSContext* cx);

extern JS_FRIEND_API JSObject* GetTestingFunctions(JSContext* cx);

/**
 * Helper to convert FreeOp to JSFreeOp when the definition of FreeOp is not
 * available and the compiler does not know that FreeOp inherits from
 * JSFreeOp.
 */
inline JSFreeOp* CastToJSFreeOp(FreeOp* fop) {
  return reinterpret_cast<JSFreeOp*>(fop);
}

/* Implemented in jsexn.cpp. */

/**
 * Get an error type name from a JSExnType constant.
 * Returns nullptr for invalid arguments and JSEXN_INTERNALERR
 */
extern JS_FRIEND_API JSFlatString* GetErrorTypeName(JSContext* cx,
                                                    int16_t exnType);

#ifdef JS_DEBUG
extern JS_FRIEND_API unsigned GetEnterCompartmentDepth(JSContext* cx);
#endif

extern JS_FRIEND_API RegExpShared* RegExpToSharedNonInline(
    JSContext* cx, JS::HandleObject regexp);

/* Implemented in CrossCompartmentWrapper.cpp. */
typedef enum NukeReferencesToWindow {
  NukeWindowReferences,
  DontNukeWindowReferences
} NukeReferencesToWindow;

typedef enum NukeReferencesFromTarget {
  NukeAllReferences,
  NukeIncomingReferences,
} NukeReferencesFromTarget;

/*
 * These filters are designed to be ephemeral stack classes, and thus don't
 * do any rooting or holding of their members.
 */
struct CompartmentFilter {
  virtual bool match(JSCompartment* c) const = 0;
};

struct AllCompartments : public CompartmentFilter {
  virtual bool match(JSCompartment* c) const override { return true; }
};

struct ContentCompartmentsOnly : public CompartmentFilter {
  virtual bool match(JSCompartment* c) const override {
    return !IsSystemCompartment(c);
  }
};

struct ChromeCompartmentsOnly : public CompartmentFilter {
  virtual bool match(JSCompartment* c) const override {
    return IsSystemCompartment(c);
  }
};

struct SingleCompartment : public CompartmentFilter {
  JSCompartment* ours;
  explicit SingleCompartment(JSCompartment* c) : ours(c) {}
  virtual bool match(JSCompartment* c) const override { return c == ours; }
};

struct CompartmentsWithPrincipals : public CompartmentFilter {
  JSPrincipals* principals;
  explicit CompartmentsWithPrincipals(JSPrincipals* p) : principals(p) {}
  virtual bool match(JSCompartment* c) const override {
    return JS_GetCompartmentPrincipals(c) == principals;
  }
};

extern JS_FRIEND_API bool NukeCrossCompartmentWrappers(
    JSContext* cx, const CompartmentFilter& sourceFilter, JSCompartment* target,
    NukeReferencesToWindow nukeReferencesToWindow,
    NukeReferencesFromTarget nukeReferencesFromTarget);

/* Specify information about DOMProxy proxies in the DOM, for use by ICs. */

/*
 * The DOMProxyShadowsCheck function will be called to check if the property for
 * id should be gotten from the prototype, or if there is an own property that
 * shadows it.
 * * If ShadowsViaDirectExpando is returned, then the slot at
 *   listBaseExpandoSlot contains an expando object which has the property in
 *   question.
 * * If ShadowsViaIndirectExpando is returned, then the slot at
 *   listBaseExpandoSlot contains a private pointer to an ExpandoAndGeneration
 *   and the expando object in the ExpandoAndGeneration has the property in
 *   question.
 * * If DoesntShadow is returned then the slot at listBaseExpandoSlot should
 *   either be undefined or point to an expando object that would contain the
 *   own property.
 * * If DoesntShadowUnique is returned then the slot at listBaseExpandoSlot
 *   should contain a private pointer to a ExpandoAndGeneration, which contains
 *   a JS::Value that should either be undefined or point to an expando object,
 *   and a uint64 value. If that value changes then the IC for getting a
 *   property will be invalidated.
 * * If Shadows is returned, that means the property is an own property of the
 *   proxy but doesn't live on the expando object.
 */

struct ExpandoAndGeneration {
  ExpandoAndGeneration() : expando(JS::UndefinedValue()), generation(0) {}

  void OwnerUnlinked() { ++generation; }

  static size_t offsetOfExpando() {
    return offsetof(ExpandoAndGeneration, expando);
  }

  static size_t offsetOfGeneration() {
    return offsetof(ExpandoAndGeneration, generation);
  }

  JS::Heap<JS::Value> expando;
  uint64_t generation;
};

typedef enum DOMProxyShadowsResult {
  ShadowCheckFailed,
  Shadows,
  DoesntShadow,
  DoesntShadowUnique,
  ShadowsViaDirectExpando,
  ShadowsViaIndirectExpando
} DOMProxyShadowsResult;
typedef DOMProxyShadowsResult (*DOMProxyShadowsCheck)(JSContext* cx,
                                                      JS::HandleObject object,
                                                      JS::HandleId id);
JS_FRIEND_API void SetDOMProxyInformation(
    const void* domProxyHandlerFamily,
    DOMProxyShadowsCheck domProxyShadowsCheck);

const void* GetDOMProxyHandlerFamily();
DOMProxyShadowsCheck GetDOMProxyShadowsCheck();
inline bool DOMProxyIsShadowing(DOMProxyShadowsResult result) {
  return result == Shadows || result == ShadowsViaDirectExpando ||
         result == ShadowsViaIndirectExpando;
}

// Callbacks and other information for use by the JITs when optimizing accesses
// on xray wrappers.
struct XrayJitInfo {
  // Test whether a proxy handler is a cross compartment xray with no
  // security checks.
  bool (*isCrossCompartmentXray)(const BaseProxyHandler* handler);

  // Test whether xrays with a global object's compartment have expandos of
  // their own, instead of sharing them with Xrays from other compartments.
  bool (*globalHasExclusiveExpandos)(JSObject* obj);

  // Proxy reserved slot used by xrays in sandboxes to store their holder
  // object.
  size_t xrayHolderSlot;

  // Reserved slot used by xray holders to store the xray's expando object.
  size_t holderExpandoSlot;

  // Reserved slot used by xray expandos to store a custom prototype.
  size_t expandoProtoSlot;
};

JS_FRIEND_API void SetXrayJitInfo(XrayJitInfo* info);

XrayJitInfo* GetXrayJitInfo();

/* Implemented in jsdate.cpp. */

/** Detect whether the internal date value is NaN. */
extern JS_FRIEND_API bool DateIsValid(JSContext* cx, JS::HandleObject obj,
                                      bool* isValid);

extern JS_FRIEND_API bool DateGetMsecSinceEpoch(JSContext* cx,
                                                JS::HandleObject obj,
                                                double* msecSinceEpoch);

} /* namespace js */

typedef enum JSErrNum {
#define MSG_DEF(name, count, exception, format) name,
#include "js.msg"
#undef MSG_DEF
  JSErr_Limit
} JSErrNum;

namespace js {

/* Implemented in vm/JSContext.cpp. */

extern JS_FRIEND_API const JSErrorFormatString* GetErrorMessage(
    void* userRef, const unsigned errorNumber);

// AutoStableStringChars is here so we can use it in ErrorReport.  It
// should get moved out of here if we can manage it.  See bug 1040316.

/**
 * This class provides safe access to a string's chars across a GC. Once
 * we allocate strings and chars in the nursery (bug 903519), this class
 * will have to make a copy of the string's chars if they are allocated
 * in the nursery, so it's best to avoid using this class unless you really
 * need it. It's usually more efficient to use the latin1Chars/twoByteChars
 * JSString methods and often the code can be rewritten so that only indexes
 * instead of char pointers are used in parts of the code that can GC.
 */
class MOZ_STACK_CLASS JS_FRIEND_API AutoStableStringChars {
  /*
   * When copying string char, use this many bytes of inline storage.  This is
   * chosen to allow the inline string types to be copied without allocating.
   * This is asserted in AutoStableStringChars::allocOwnChars.
   */
  static const size_t InlineCapacity = 24;

  /* Ensure the string is kept alive while we're using its chars. */
  JS::RootedString s_;
  union {
    const char16_t* twoByteChars_;
    const JS::Latin1Char* latin1Chars_;
  };
  mozilla::Maybe<Vector<uint8_t, InlineCapacity>> ownChars_;
  enum State { Uninitialized, Latin1, TwoByte };
  State state_;

 public:
  explicit AutoStableStringChars(JSContext* cx)
      : s_(cx), state_(Uninitialized) {}

  MOZ_MUST_USE
  bool init(JSContext* cx, JSString* s);

  /* Like init(), but Latin1 chars are inflated to TwoByte. */
  MOZ_MUST_USE
  bool initTwoByte(JSContext* cx, JSString* s);

  bool isLatin1() const { return state_ == Latin1; }
  bool isTwoByte() const { return state_ == TwoByte; }

  const JS::Latin1Char* latin1Chars() const {
    MOZ_ASSERT(state_ == Latin1);
    return latin1Chars_;
  }
  const char16_t* twoByteChars() const {
    MOZ_ASSERT(state_ == TwoByte);
    return twoByteChars_;
  }

  mozilla::Range<const JS::Latin1Char> latin1Range() const {
    MOZ_ASSERT(state_ == Latin1);
    return mozilla::Range<const JS::Latin1Char>(latin1Chars_,
                                                GetStringLength(s_));
  }

  mozilla::Range<const char16_t> twoByteRange() const {
    MOZ_ASSERT(state_ == TwoByte);
    return mozilla::Range<const char16_t>(twoByteChars_, GetStringLength(s_));
  }

  /* If we own the chars, transfer ownership to the caller. */
  bool maybeGiveOwnershipToCaller() {
    MOZ_ASSERT(state_ != Uninitialized);
    if (!ownChars_.isSome() || !ownChars_->extractRawBuffer()) return false;
    state_ = Uninitialized;
    ownChars_.reset();
    return true;
  }

 private:
  AutoStableStringChars(const AutoStableStringChars& other) = delete;
  void operator=(const AutoStableStringChars& other) = delete;

  bool baseIsInline(JS::Handle<JSLinearString*> linearString);
  template <typename T>
  T* allocOwnChars(JSContext* cx, size_t count);
  bool copyLatin1Chars(JSContext* cx, JS::Handle<JSLinearString*> linearString);
  bool copyTwoByteChars(JSContext* cx,
                        JS::Handle<JSLinearString*> linearString);
  bool copyAndInflateLatin1Chars(JSContext*,
                                 JS::Handle<JSLinearString*> linearString);
};

struct MOZ_STACK_CLASS JS_FRIEND_API ErrorReport {
  explicit ErrorReport(JSContext* cx);
  ~ErrorReport();

  enum SniffingBehavior { WithSideEffects, NoSideEffects };

  /**
   * Generate a JSErrorReport from the provided thrown value.
   *
   * If the value is a (possibly wrapped) Error object, the JSErrorReport will
   * be exactly initialized from the Error object's information, without
   * observable side effects. (The Error object's JSErrorReport is reused, if
   * it has one.)
   *
   * Otherwise various attempts are made to derive JSErrorReport information
   * from |exn| and from the current execution state.  This process is
   * *definitely* inconsistent with any standard, and particulars of the
   * behavior implemented here generally shouldn't be relied upon.
   *
   * If the value of |sniffingBehavior| is |WithSideEffects|, some of these
   * attempts *may* invoke user-configurable behavior when |exn| is an object:
   * converting |exn| to a string, detecting and getting properties on |exn|,
   * accessing |exn|'s prototype chain, and others are possible.  Users *must*
   * tolerate |ErrorReport::init| potentially having arbitrary effects.  Any
   * exceptions thrown by these operations will be caught and silently
   * ignored, and "default" values will be substituted into the JSErrorReport.
   *
   * But if the value of |sniffingBehavior| is |NoSideEffects|, these attempts
   * *will not* invoke any observable side effects.  The JSErrorReport will
   * simply contain fewer, less precise details.
   *
   * Unlike some functions involved in error handling, this function adheres
   * to the usual JSAPI return value error behavior.
   */
  bool init(JSContext* cx, JS::HandleValue exn,
            SniffingBehavior sniffingBehavior);

  JSErrorReport* report() { return reportp; }

  const JS::ConstUTF8CharsZ toStringResult() { return toStringResult_; }

 private:
  // More or less an equivalent of JS_ReportErrorNumber/js::ReportErrorNumberVA
  // but fills in an ErrorReport instead of reporting it.  Uses varargs to
  // make it simpler to call js::ExpandErrorArgumentsVA.
  //
  // Returns false if we fail to actually populate the ErrorReport
  // for some reason (probably out of memory).
  bool populateUncaughtExceptionReportUTF8(JSContext* cx, ...);
  bool populateUncaughtExceptionReportUTF8VA(JSContext* cx, va_list ap);

  // Reports exceptions from add-on scopes to telemetry.
  void ReportAddonExceptionToTelemetry(JSContext* cx);

  // We may have a provided JSErrorReport, so need a way to represent that.
  JSErrorReport* reportp;

  // Or we may need to synthesize a JSErrorReport one of our own.
  JSErrorReport ownedReport;

  // And we have a string to maybe keep alive that has pointers into
  // it from ownedReport.
  JS::RootedString str;

  // And keep its chars alive too.
  AutoStableStringChars strChars;

  // And we need to root our exception value.
  JS::RootedObject exnObject;

  // And for our filename.
  JSAutoByteString filename;

  // We may have a result of error.toString().
  // FIXME: We should not call error.toString(), since it could have side
  //        effect (see bug 633623).
  JS::ConstUTF8CharsZ toStringResult_;
  JSAutoByteString toStringResultBytesStorage;
};

/* Implemented in vm/StructuredClone.cpp. */
extern JS_FRIEND_API uint64_t GetSCOffset(JSStructuredCloneWriter* writer);

namespace Scalar {

/**
 * Scalar types that can appear in typed arrays and typed objects.  The enum
 * values must to be kept in sync with the JS_SCALARTYPEREPR_ constants, as
 * well as the TypedArrayObject::classes and TypedArrayObject::protoClasses
 * definitions.
 */
enum Type {
  Int8 = 0,
  Uint8,
  Int16,
  Uint16,
  Int32,
  Uint32,
  Float32,
  Float64,

  /**
   * Special type that is a uint8_t, but assignments are clamped to [0, 256).
   * Treat the raw data type as a uint8_t.
   */
  Uint8Clamped,

  /**
   * Types that don't have their own TypedArray equivalent, for now.
   */
  MaxTypedArrayViewType,

  Int64,
  Float32x4,
  Int8x16,
  Int16x8,
  Int32x4
};

static inline size_t byteSize(Type atype) {
  switch (atype) {
    case Int8:
    case Uint8:
    case Uint8Clamped:
      return 1;
    case Int16:
    case Uint16:
      return 2;
    case Int32:
    case Uint32:
    case Float32:
      return 4;
    case Int64:
    case Float64:
      return 8;
    case Int8x16:
    case Int16x8:
    case Int32x4:
    case Float32x4:
      return 16;
    default:
      MOZ_CRASH("invalid scalar type");
  }
}

static inline bool isSignedIntType(Type atype) {
  switch (atype) {
    case Int8:
    case Int16:
    case Int32:
    case Int64:
    case Int8x16:
    case Int16x8:
    case Int32x4:
      return true;
    case Uint8:
    case Uint8Clamped:
    case Uint16:
    case Uint32:
    case Float32:
    case Float64:
    case Float32x4:
      return false;
    default:
      MOZ_CRASH("invalid scalar type");
  }
}

static inline bool isSimdType(Type atype) {
  switch (atype) {
    case Int8:
    case Uint8:
    case Uint8Clamped:
    case Int16:
    case Uint16:
    case Int32:
    case Uint32:
    case Int64:
    case Float32:
    case Float64:
      return false;
    case Int8x16:
    case Int16x8:
    case Int32x4:
    case Float32x4:
      return true;
    case MaxTypedArrayViewType:
      break;
  }
  MOZ_CRASH("invalid scalar type");
}

static inline size_t scalarByteSize(Type atype) {
  switch (atype) {
    case Int8x16:
      return 1;
    case Int16x8:
      return 2;
    case Int32x4:
    case Float32x4:
      return 4;
    case Int8:
    case Uint8:
    case Uint8Clamped:
    case Int16:
    case Uint16:
    case Int32:
    case Uint32:
    case Int64:
    case Float32:
    case Float64:
    case MaxTypedArrayViewType:
      break;
  }
  MOZ_CRASH("invalid simd type");
}

} /* namespace Scalar */
} /* namespace js */

/*
 * Create a new typed array with nelements elements.
 *
 * These functions (except the WithBuffer variants) fill in the array with
 * zeros.
 */

extern JS_FRIEND_API JSObject* JS_NewInt8Array(JSContext* cx,
                                               uint32_t nelements);
extern JS_FRIEND_API JSObject* JS_NewUint8Array(JSContext* cx,
                                                uint32_t nelements);
extern JS_FRIEND_API JSObject* JS_NewUint8ClampedArray(JSContext* cx,
                                                       uint32_t nelements);
extern JS_FRIEND_API JSObject* JS_NewInt16Array(JSContext* cx,
                                                uint32_t nelements);
extern JS_FRIEND_API JSObject* JS_NewUint16Array(JSContext* cx,
                                                 uint32_t nelements);
extern JS_FRIEND_API JSObject* JS_NewInt32Array(JSContext* cx,
                                                uint32_t nelements);
extern JS_FRIEND_API JSObject* JS_NewUint32Array(JSContext* cx,
                                                 uint32_t nelements);
extern JS_FRIEND_API JSObject* JS_NewFloat32Array(JSContext* cx,
                                                  uint32_t nelements);
extern JS_FRIEND_API JSObject* JS_NewFloat64Array(JSContext* cx,
                                                  uint32_t nelements);

/*
 * Create a new typed array and copy in values from the given object. The
 * object is used as if it were an array; that is, the new array (if
 * successfully created) will have length given by array.length, and its
 * elements will be those specified by array[0], array[1], and so on, after
 * conversion to the typed array element type.
 */

extern JS_FRIEND_API JSObject* JS_NewInt8ArrayFromArray(JSContext* cx,
                                                        JS::HandleObject array);
extern JS_FRIEND_API JSObject* JS_NewUint8ArrayFromArray(
    JSContext* cx, JS::HandleObject array);
extern JS_FRIEND_API JSObject* JS_NewUint8ClampedArrayFromArray(
    JSContext* cx, JS::HandleObject array);
extern JS_FRIEND_API JSObject* JS_NewInt16ArrayFromArray(
    JSContext* cx, JS::HandleObject array);
extern JS_FRIEND_API JSObject* JS_NewUint16ArrayFromArray(
    JSContext* cx, JS::HandleObject array);
extern JS_FRIEND_API JSObject* JS_NewInt32ArrayFromArray(
    JSContext* cx, JS::HandleObject array);
extern JS_FRIEND_API JSObject* JS_NewUint32ArrayFromArray(
    JSContext* cx, JS::HandleObject array);
extern JS_FRIEND_API JSObject* JS_NewFloat32ArrayFromArray(
    JSContext* cx, JS::HandleObject array);
extern JS_FRIEND_API JSObject* JS_NewFloat64ArrayFromArray(
    JSContext* cx, JS::HandleObject array);

/*
 * Create a new typed array using the given ArrayBuffer or
 * SharedArrayBuffer for storage.  The length value is optional; if -1
 * is passed, enough elements to use up the remainder of the byte
 * array is used as the default value.
 */

extern JS_FRIEND_API JSObject* JS_NewInt8ArrayWithBuffer(
    JSContext* cx, JS::HandleObject arrayBuffer, uint32_t byteOffset,
    int32_t length);
extern JS_FRIEND_API JSObject* JS_NewUint8ArrayWithBuffer(
    JSContext* cx, JS::HandleObject arrayBuffer, uint32_t byteOffset,
    int32_t length);
extern JS_FRIEND_API JSObject* JS_NewUint8ClampedArrayWithBuffer(
    JSContext* cx, JS::HandleObject arrayBuffer, uint32_t byteOffset,
    int32_t length);
extern JS_FRIEND_API JSObject* JS_NewInt16ArrayWithBuffer(
    JSContext* cx, JS::HandleObject arrayBuffer, uint32_t byteOffset,
    int32_t length);
extern JS_FRIEND_API JSObject* JS_NewUint16ArrayWithBuffer(
    JSContext* cx, JS::HandleObject arrayBuffer, uint32_t byteOffset,
    int32_t length);
extern JS_FRIEND_API JSObject* JS_NewInt32ArrayWithBuffer(
    JSContext* cx, JS::HandleObject arrayBuffer, uint32_t byteOffset,
    int32_t length);
extern JS_FRIEND_API JSObject* JS_NewUint32ArrayWithBuffer(
    JSContext* cx, JS::HandleObject arrayBuffer, uint32_t byteOffset,
    int32_t length);
extern JS_FRIEND_API JSObject* JS_NewFloat32ArrayWithBuffer(
    JSContext* cx, JS::HandleObject arrayBuffer, uint32_t byteOffset,
    int32_t length);
extern JS_FRIEND_API JSObject* JS_NewFloat64ArrayWithBuffer(
    JSContext* cx, JS::HandleObject arrayBuffer, uint32_t byteOffset,
    int32_t length);

/**
 * Create a new SharedArrayBuffer with the given byte length.  This
 * may only be called if
 * JS::CompartmentCreationOptionsRef(cx).getSharedMemoryAndAtomicsEnabled() is
 * true.
 */
extern JS_FRIEND_API JSObject* JS_NewSharedArrayBuffer(JSContext* cx,
                                                       uint32_t nbytes);

/**
 * Create a new ArrayBuffer with the given byte length.
 */
extern JS_FRIEND_API JSObject* JS_NewArrayBuffer(JSContext* cx,
                                                 uint32_t nbytes);

/**
 * Check whether obj supports JS_GetTypedArray* APIs. Note that this may return
 * false if a security wrapper is encountered that denies the unwrapping. If
 * this test or one of the JS_Is*Array tests succeeds, then it is safe to call
 * the various accessor JSAPI calls defined below.
 */
extern JS_FRIEND_API bool JS_IsTypedArrayObject(JSObject* obj);

/**
 * Check whether obj supports JS_GetArrayBufferView* APIs. Note that this may
 * return false if a security wrapper is encountered that denies the
 * unwrapping. If this test or one of the more specific tests succeeds, then it
 * is safe to call the various ArrayBufferView accessor JSAPI calls defined
 * below.
 */
extern JS_FRIEND_API bool JS_IsArrayBufferViewObject(JSObject* obj);

/*
 * Test for specific typed array types (ArrayBufferView subtypes)
 */

extern JS_FRIEND_API bool JS_IsInt8Array(JSObject* obj);
extern JS_FRIEND_API bool JS_IsUint8Array(JSObject* obj);
extern JS_FRIEND_API bool JS_IsUint8ClampedArray(JSObject* obj);
extern JS_FRIEND_API bool JS_IsInt16Array(JSObject* obj);
extern JS_FRIEND_API bool JS_IsUint16Array(JSObject* obj);
extern JS_FRIEND_API bool JS_IsInt32Array(JSObject* obj);
extern JS_FRIEND_API bool JS_IsUint32Array(JSObject* obj);
extern JS_FRIEND_API bool JS_IsFloat32Array(JSObject* obj);
extern JS_FRIEND_API bool JS_IsFloat64Array(JSObject* obj);

/**
 * Return the isShared flag of a typed array, which denotes whether
 * the underlying buffer is a SharedArrayBuffer.
 *
 * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
 * be known that it would pass such a test: it is a typed array or a wrapper of
 * a typed array, and the unwrapping will succeed.
 */
extern JS_FRIEND_API bool JS_GetTypedArraySharedness(JSObject* obj);

/*
 * Test for specific typed array types (ArrayBufferView subtypes) and return
 * the unwrapped object if so, else nullptr.  Never throws.
 */

namespace js {

extern JS_FRIEND_API JSObject* UnwrapInt8Array(JSObject* obj);
extern JS_FRIEND_API JSObject* UnwrapUint8Array(JSObject* obj);
extern JS_FRIEND_API JSObject* UnwrapUint8ClampedArray(JSObject* obj);
extern JS_FRIEND_API JSObject* UnwrapInt16Array(JSObject* obj);
extern JS_FRIEND_API JSObject* UnwrapUint16Array(JSObject* obj);
extern JS_FRIEND_API JSObject* UnwrapInt32Array(JSObject* obj);
extern JS_FRIEND_API JSObject* UnwrapUint32Array(JSObject* obj);
extern JS_FRIEND_API JSObject* UnwrapFloat32Array(JSObject* obj);
extern JS_FRIEND_API JSObject* UnwrapFloat64Array(JSObject* obj);

extern JS_FRIEND_API JSObject* UnwrapArrayBuffer(JSObject* obj);

extern JS_FRIEND_API JSObject* UnwrapArrayBufferView(JSObject* obj);

extern JS_FRIEND_API JSObject* UnwrapSharedArrayBuffer(JSObject* obj);

extern JS_FRIEND_API JSObject* UnwrapReadableStream(JSObject* obj);

namespace detail {

extern JS_FRIEND_DATA const Class* const Int8ArrayClassPtr;
extern JS_FRIEND_DATA const Class* const Uint8ArrayClassPtr;
extern JS_FRIEND_DATA const Class* const Uint8ClampedArrayClassPtr;
extern JS_FRIEND_DATA const Class* const Int16ArrayClassPtr;
extern JS_FRIEND_DATA const Class* const Uint16ArrayClassPtr;
extern JS_FRIEND_DATA const Class* const Int32ArrayClassPtr;
extern JS_FRIEND_DATA const Class* const Uint32ArrayClassPtr;
extern JS_FRIEND_DATA const Class* const Float32ArrayClassPtr;
extern JS_FRIEND_DATA const Class* const Float64ArrayClassPtr;

const size_t TypedArrayLengthSlot = 1;

}  // namespace detail

#define JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Type, type)                      \
  inline void Get##Type##ArrayLengthAndData(                                \
      JSObject* obj, uint32_t* length, bool* isSharedMemory, type** data) { \
    MOZ_ASSERT(GetObjectClass(obj) == detail::Type##ArrayClassPtr);         \
    const JS::Value& lenSlot =                                              \
        GetReservedSlot(obj, detail::TypedArrayLengthSlot);                 \
    *length = mozilla::AssertedCast<uint32_t>(lenSlot.toInt32());           \
    *isSharedMemory = JS_GetTypedArraySharedness(obj);                      \
    *data = static_cast<type*>(GetObjectPrivate(obj));                      \
  }

JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int8, int8_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8, uint8_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8Clamped, uint8_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int16, int16_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint16, uint16_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int32, int32_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint32, uint32_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float32, float)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float64, double)

#undef JS_DEFINE_DATA_AND_LENGTH_ACCESSOR

// This one isn't inlined because it's rather tricky (by dint of having to deal
// with a dozen-plus classes and varying slot layouts.
extern JS_FRIEND_API void GetArrayBufferViewLengthAndData(JSObject* obj,
                                                          uint32_t* length,
                                                          bool* isSharedMemory,
                                                          uint8_t** data);

// This one isn't inlined because there are a bunch of different ArrayBuffer
// classes that would have to be individually handled here.
//
// There is an isShared out argument for API consistency (eases use from DOM).
// It will always be set to false.
extern JS_FRIEND_API void GetArrayBufferLengthAndData(JSObject* obj,
                                                      uint32_t* length,
                                                      bool* isSharedMemory,
                                                      uint8_t** data);

// Ditto for SharedArrayBuffer.
//
// There is an isShared out argument for API consistency (eases use from DOM).
// It will always be set to true.
extern JS_FRIEND_API void GetSharedArrayBufferLengthAndData(
    JSObject* obj, uint32_t* length, bool* isSharedMemory, uint8_t** data);

}  // namespace js

JS_FRIEND_API uint8_t* JS_GetSharedArrayBufferData(JSObject* obj,
                                                   bool* isSharedMemory,
                                                   const JS::AutoRequireNoGC&);

/*
 * Unwrap Typed arrays all at once. Return nullptr without throwing if the
 * object cannot be viewed as the correct typed array, or the typed array
 * object on success, filling both outparameters.
 */
extern JS_FRIEND_API JSObject* JS_GetObjectAsInt8Array(JSObject* obj,
                                                       uint32_t* length,
                                                       bool* isSharedMemory,
                                                       int8_t** data);
extern JS_FRIEND_API JSObject* JS_GetObjectAsUint8Array(JSObject* obj,
                                                        uint32_t* length,
                                                        bool* isSharedMemory,
                                                        uint8_t** data);
extern JS_FRIEND_API JSObject* JS_GetObjectAsUint8ClampedArray(
    JSObject* obj, uint32_t* length, bool* isSharedMemory, uint8_t** data);
extern JS_FRIEND_API JSObject* JS_GetObjectAsInt16Array(JSObject* obj,
                                                        uint32_t* length,
                                                        bool* isSharedMemory,
                                                        int16_t** data);
extern JS_FRIEND_API JSObject* JS_GetObjectAsUint16Array(JSObject* obj,
                                                         uint32_t* length,
                                                         bool* isSharedMemory,
                                                         uint16_t** data);
extern JS_FRIEND_API JSObject* JS_GetObjectAsInt32Array(JSObject* obj,
                                                        uint32_t* length,
                                                        bool* isSharedMemory,
                                                        int32_t** data);
extern JS_FRIEND_API JSObject* JS_GetObjectAsUint32Array(JSObject* obj,
                                                         uint32_t* length,
                                                         bool* isSharedMemory,
                                                         uint32_t** data);
extern JS_FRIEND_API JSObject* JS_GetObjectAsFloat32Array(JSObject* obj,
                                                          uint32_t* length,
                                                          bool* isSharedMemory,
                                                          float** data);
extern JS_FRIEND_API JSObject* JS_GetObjectAsFloat64Array(JSObject* obj,
                                                          uint32_t* length,
                                                          bool* isSharedMemory,
                                                          double** data);
extern JS_FRIEND_API JSObject* JS_GetObjectAsArrayBufferView(
    JSObject* obj, uint32_t* length, bool* isSharedMemory, uint8_t** data);

/*
 * Unwrap an ArrayBuffer, return nullptr if it's a different type.
 */
extern JS_FRIEND_API JSObject* JS_GetObjectAsArrayBuffer(JSObject* obj,
                                                         uint32_t* length,
                                                         uint8_t** data);

/*
 * Get the type of elements in a typed array, or MaxTypedArrayViewType if a
 * DataView.
 *
 * |obj| must have passed a JS_IsArrayBufferView/JS_Is*Array test, or somehow
 * be known that it would pass such a test: it is an ArrayBufferView or a
 * wrapper of an ArrayBufferView, and the unwrapping will succeed.
 */
extern JS_FRIEND_API js::Scalar::Type JS_GetArrayBufferViewType(JSObject* obj);

extern JS_FRIEND_API js::Scalar::Type JS_GetSharedArrayBufferViewType(
    JSObject* obj);

/*
 * Check whether obj supports the JS_GetArrayBuffer* APIs. Note that this may
 * return false if a security wrapper is encountered that denies the
 * unwrapping. If this test succeeds, then it is safe to call the various
 * accessor JSAPI calls defined below.
 */
extern JS_FRIEND_API bool JS_IsArrayBufferObject(JSObject* obj);

extern JS_FRIEND_API bool JS_IsSharedArrayBufferObject(JSObject* obj);

/**
 * Return the available byte length of an array buffer.
 *
 * |obj| must have passed a JS_IsArrayBufferObject test, or somehow be known
 * that it would pass such a test: it is an ArrayBuffer or a wrapper of an
 * ArrayBuffer, and the unwrapping will succeed.
 */
extern JS_FRIEND_API uint32_t JS_GetArrayBufferByteLength(JSObject* obj);

extern JS_FRIEND_API uint32_t JS_GetSharedArrayBufferByteLength(JSObject* obj);

/**
 * Return true if the arrayBuffer contains any data. This will return false for
 * ArrayBuffer.prototype and detached ArrayBuffers.
 *
 * |obj| must have passed a JS_IsArrayBufferObject test, or somehow be known
 * that it would pass such a test: it is an ArrayBuffer or a wrapper of an
 * ArrayBuffer, and the unwrapping will succeed.
 */
extern JS_FRIEND_API bool JS_ArrayBufferHasData(JSObject* obj);

/**
 * Return a pointer to the start of the data referenced by a typed array. The
 * data is still owned by the typed array, and should not be modified on
 * another thread. Furthermore, the pointer can become invalid on GC (if the
 * data is small and fits inside the array's GC header), so callers must take
 * care not to hold on across anything that could GC.
 *
 * |obj| must have passed a JS_IsArrayBufferObject test, or somehow be known
 * that it would pass such a test: it is an ArrayBuffer or a wrapper of an
 * ArrayBuffer, and the unwrapping will succeed.
 *
 * |*isSharedMemory| will be set to false, the argument is present to simplify
 * its use from code that also interacts with SharedArrayBuffer.
 */
extern JS_FRIEND_API uint8_t* JS_GetArrayBufferData(JSObject* obj,
                                                    bool* isSharedMemory,
                                                    const JS::AutoRequireNoGC&);

/**
 * Check whether the obj is ArrayBufferObject and memory mapped. Note that this
 * may return false if a security wrapper is encountered that denies the
 * unwrapping.
 */
extern JS_FRIEND_API bool JS_IsMappedArrayBufferObject(JSObject* obj);

/**
 * Return the number of elements in a typed array.
 *
 * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
 * be known that it would pass such a test: it is a typed array or a wrapper of
 * a typed array, and the unwrapping will succeed.
 */
extern JS_FRIEND_API uint32_t JS_GetTypedArrayLength(JSObject* obj);

/**
 * Return the byte offset from the start of an array buffer to the start of a
 * typed array view.
 *
 * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
 * be known that it would pass such a test: it is a typed array or a wrapper of
 * a typed array, and the unwrapping will succeed.
 */
extern JS_FRIEND_API uint32_t JS_GetTypedArrayByteOffset(JSObject* obj);

/**
 * Return the byte length of a typed array.
 *
 * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
 * be known that it would pass such a test: it is a typed array or a wrapper of
 * a typed array, and the unwrapping will succeed.
 */
extern JS_FRIEND_API uint32_t JS_GetTypedArrayByteLength(JSObject* obj);

/**
 * Check whether obj supports JS_ArrayBufferView* APIs. Note that this may
 * return false if a security wrapper is encountered that denies the
 * unwrapping.
 */
extern JS_FRIEND_API bool JS_IsArrayBufferViewObject(JSObject* obj);

/**
 * More generic name for JS_GetTypedArrayByteLength to cover DataViews as well
 */
extern JS_FRIEND_API uint32_t JS_GetArrayBufferViewByteLength(JSObject* obj);

/**
 * More generic name for JS_GetTypedArrayByteOffset to cover DataViews as well
 */
extern JS_FRIEND_API uint32_t JS_GetArrayBufferViewByteOffset(JSObject* obj);

/*
 * Return a pointer to the start of the data referenced by a typed array. The
 * data is still owned by the typed array, and should not be modified on
 * another thread. Furthermore, the pointer can become invalid on GC (if the
 * data is small and fits inside the array's GC header), so callers must take
 * care not to hold on across anything that could GC.
 *
 * |obj| must have passed a JS_Is*Array test, or somehow be known that it would
 * pass such a test: it is a typed array or a wrapper of a typed array, and the
 * unwrapping will succeed.
 *
 * |*isSharedMemory| will be set to true if the typed array maps a
 * SharedArrayBuffer, otherwise to false.
 */

extern JS_FRIEND_API int8_t* JS_GetInt8ArrayData(JSObject* obj,
                                                 bool* isSharedMemory,
                                                 const JS::AutoRequireNoGC&);
extern JS_FRIEND_API uint8_t* JS_GetUint8ArrayData(JSObject* obj,
                                                   bool* isSharedMemory,
                                                   const JS::AutoRequireNoGC&);
extern JS_FRIEND_API uint8_t* JS_GetUint8ClampedArrayData(
    JSObject* obj, bool* isSharedMemory, const JS::AutoRequireNoGC&);
extern JS_FRIEND_API int16_t* JS_GetInt16ArrayData(JSObject* obj,
                                                   bool* isSharedMemory,
                                                   const JS::AutoRequireNoGC&);
extern JS_FRIEND_API uint16_t* JS_GetUint16ArrayData(
    JSObject* obj, bool* isSharedMemory, const JS::AutoRequireNoGC&);
extern JS_FRIEND_API int32_t* JS_GetInt32ArrayData(JSObject* obj,
                                                   bool* isSharedMemory,
                                                   const JS::AutoRequireNoGC&);
extern JS_FRIEND_API uint32_t* JS_GetUint32ArrayData(
    JSObject* obj, bool* isSharedMemory, const JS::AutoRequireNoGC&);
extern JS_FRIEND_API float* JS_GetFloat32ArrayData(JSObject* obj,
                                                   bool* isSharedMemory,
                                                   const JS::AutoRequireNoGC&);
extern JS_FRIEND_API double* JS_GetFloat64ArrayData(JSObject* obj,
                                                    bool* isSharedMemory,
                                                    const JS::AutoRequireNoGC&);

/**
 * Same as above, but for any kind of ArrayBufferView. Prefer the type-specific
 * versions when possible.
 */
extern JS_FRIEND_API void* JS_GetArrayBufferViewData(
    JSObject* obj, bool* isSharedMemory, const JS::AutoRequireNoGC&);

/**
 * Return the ArrayBuffer or SharedArrayBuffer underlying an ArrayBufferView.
 * This may return a detached buffer.  |obj| must be an object that would
 * return true for JS_IsArrayBufferViewObject().
 */
extern JS_FRIEND_API JSObject* JS_GetArrayBufferViewBuffer(
    JSContext* cx, JS::HandleObject obj, bool* isSharedMemory);

/**
 * Detach an ArrayBuffer, causing all associated views to no longer refer to
 * the ArrayBuffer's original attached memory.
 *
 * The |changeData| argument is obsolete and ignored.
 */
extern JS_FRIEND_API bool JS_DetachArrayBuffer(JSContext* cx,
                                               JS::HandleObject obj);

/**
 * Check whether the obj is a detached ArrayBufferObject. Note that this may
 * return false if a security wrapper is encountered that denies the
 * unwrapping.
 */
extern JS_FRIEND_API bool JS_IsDetachedArrayBufferObject(JSObject* obj);

/**
 * Check whether obj supports JS_GetDataView* APIs.
 */
JS_FRIEND_API bool JS_IsDataViewObject(JSObject* obj);

/**
 * Create a new DataView using the given buffer for storage. The given buffer
 * must be an ArrayBuffer or SharedArrayBuffer (or a cross-compartment wrapper
 * of either type), and the offset and length must fit within the bounds of the
 * buffer. Currently, nullptr will be returned and an exception will be thrown
 * if these conditions do not hold, but do not depend on that behavior.
 */
JS_FRIEND_API JSObject* JS_NewDataView(JSContext* cx, JS::HandleObject buffer,
                                       uint32_t byteOffset, int32_t byteLength);

/**
 * Return the byte offset of a data view into its array buffer. |obj| must be a
 * DataView.
 *
 * |obj| must have passed a JS_IsDataViewObject test, or somehow be known that
 * it would pass such a test: it is a data view or a wrapper of a data view,
 * and the unwrapping will succeed.
 */
JS_FRIEND_API uint32_t JS_GetDataViewByteOffset(JSObject* obj);

/**
 * Return the byte length of a data view.
 *
 * |obj| must have passed a JS_IsDataViewObject test, or somehow be known that
 * it would pass such a test: it is a data view or a wrapper of a data view,
 * and the unwrapping will succeed. If cx is nullptr, then DEBUG builds may be
 * unable to assert when unwrapping should be disallowed.
 */
JS_FRIEND_API uint32_t JS_GetDataViewByteLength(JSObject* obj);

/**
 * Return a pointer to the beginning of the data referenced by a DataView.
 *
 * |obj| must have passed a JS_IsDataViewObject test, or somehow be known that
 * it would pass such a test: it is a data view or a wrapper of a data view,
 * and the unwrapping will succeed. If cx is nullptr, then DEBUG builds may be
 * unable to assert when unwrapping should be disallowed.
 *
 * |*isSharedMemory| will be set to true if the DataView maps a
 * SharedArrayBuffer, otherwise to false.
 */
JS_FRIEND_API void* JS_GetDataViewData(JSObject* obj, bool* isSharedMemory,
                                       const JS::AutoRequireNoGC&);

namespace js {
namespace jit {

enum class InlinableNative : uint16_t;

}  // namespace jit
}  // namespace js

/**
 * A class, expected to be passed by value, which represents the CallArgs for a
 * JSJitGetterOp.
 */
class JSJitGetterCallArgs : protected JS::MutableHandleValue {
 public:
  explicit JSJitGetterCallArgs(const JS::CallArgs& args)
      : JS::MutableHandleValue(args.rval()) {}

  explicit JSJitGetterCallArgs(JS::RootedValue* rooted)
      : JS::MutableHandleValue(rooted) {}

  JS::MutableHandleValue rval() { return *this; }
};

/**
 * A class, expected to be passed by value, which represents the CallArgs for a
 * JSJitSetterOp.
 */
class JSJitSetterCallArgs : protected JS::MutableHandleValue {
 public:
  explicit JSJitSetterCallArgs(const JS::CallArgs& args)
      : JS::MutableHandleValue(args[0]) {}

  JS::MutableHandleValue operator[](unsigned i) {
    MOZ_ASSERT(i == 0);
    return *this;
  }

  unsigned length() const { return 1; }

  // Add get() or maybe hasDefined() as needed
};

struct JSJitMethodCallArgsTraits;

/**
 * A class, expected to be passed by reference, which represents the CallArgs
 * for a JSJitMethodOp.
 */
class JSJitMethodCallArgs
    : protected JS::detail::CallArgsBase<JS::detail::NoUsedRval> {
 private:
  typedef JS::detail::CallArgsBase<JS::detail::NoUsedRval> Base;
  friend struct JSJitMethodCallArgsTraits;

 public:
  explicit JSJitMethodCallArgs(const JS::CallArgs& args) {
    argv_ = args.array();
    argc_ = args.length();
  }

  JS::MutableHandleValue rval() const { return Base::rval(); }

  unsigned length() const { return Base::length(); }

  JS::MutableHandleValue operator[](unsigned i) const {
    return Base::operator[](i);
  }

  bool hasDefined(unsigned i) const { return Base::hasDefined(i); }

  JSObject& callee() const {
    // We can't use Base::callee() because that will try to poke at
    // this->usedRval_, which we don't have.
    return argv_[-2].toObject();
  }

  JS::HandleValue get(unsigned i) const { return Base::get(i); }
};

struct JSJitMethodCallArgsTraits {
  static const size_t offsetOfArgv = offsetof(JSJitMethodCallArgs, argv_);
  static const size_t offsetOfArgc = offsetof(JSJitMethodCallArgs, argc_);
};

typedef bool (*JSJitGetterOp)(JSContext* cx, JS::HandleObject thisObj,
                              void* specializedThis, JSJitGetterCallArgs args);
typedef bool (*JSJitSetterOp)(JSContext* cx, JS::HandleObject thisObj,
                              void* specializedThis, JSJitSetterCallArgs args);
typedef bool (*JSJitMethodOp)(JSContext* cx, JS::HandleObject thisObj,
                              void* specializedThis,
                              const JSJitMethodCallArgs& args);

/**
 * This struct contains metadata passed from the DOM to the JS Engine for JIT
 * optimizations on DOM property accessors. Eventually, this should be made
 * available to general JSAPI users, but we are not currently ready to do so.
 */
struct JSJitInfo {
  enum OpType {
    Getter,
    Setter,
    Method,
    StaticMethod,
    InlinableNative,
    IgnoresReturnValueNative,
    // Must be last
    OpTypeCount
  };

  enum ArgType {
    // Basic types
    String = (1 << 0),
    Integer = (1 << 1),  // Only 32-bit or less
    Double = (1 << 2),   // Maybe we want to add Float sometime too
    Boolean = (1 << 3),
    Object = (1 << 4),
    Null = (1 << 5),

    // And derived types
    Numeric = Integer | Double,
    // Should "Primitive" use the WebIDL definition, which
    // excludes string and null, or the typical JS one that includes them?
    Primitive = Numeric | Boolean | Null | String,
    ObjectOrNull = Object | Null,
    Any = ObjectOrNull | Primitive,

    // Our sentinel value.
    ArgTypeListEnd = (1 << 31)
  };

  static_assert(Any & String, "Any must include String.");
  static_assert(Any & Integer, "Any must include Integer.");
  static_assert(Any & Double, "Any must include Double.");
  static_assert(Any & Boolean, "Any must include Boolean.");
  static_assert(Any & Object, "Any must include Object.");
  static_assert(Any & Null, "Any must include Null.");

  /**
   * An enum that describes what this getter/setter/method aliases.  This
   * determines what things can be hoisted past this call, and if this
   * call is movable what it can be hoisted past.
   */
  enum AliasSet {
    /**
     * Alias nothing: a constant value, getting it can't affect any other
     * values, nothing can affect it.
     */
    AliasNone,

    /**
     * Alias things that can modify the DOM but nothing else.  Doing the
     * call can't affect the behavior of any other function.
     */
    AliasDOMSets,

    /**
     * Alias the world.  Calling this can change arbitrary values anywhere
     * in the system.  Most things fall in this bucket.
     */
    AliasEverything,

    /** Must be last. */
    AliasSetCount
  };

  bool needsOuterizedThisObject() const {
    return type() != Getter && type() != Setter;
  }

  bool isTypedMethodJitInfo() const { return isTypedMethod; }

  OpType type() const { return OpType(type_); }

  AliasSet aliasSet() const { return AliasSet(aliasSet_); }

  JSValueType returnType() const { return JSValueType(returnType_); }

  union {
    JSJitGetterOp getter;
    JSJitSetterOp setter;
    JSJitMethodOp method;
    /** A DOM static method, used for Promise wrappers */
    JSNative staticMethod;
    JSNative ignoresReturnValueMethod;
  };

  static unsigned offsetOfIgnoresReturnValueNative() {
    return offsetof(JSJitInfo, ignoresReturnValueMethod);
  }

  union {
    uint16_t protoID;
    js::jit::InlinableNative inlinableNative;
  };

  union {
    uint16_t depth;

    // Additional opcode for some InlinableNative functions.
    uint16_t nativeOp;
  };

  // These fields are carefully packed to take up 4 bytes.  If you need more
  // bits for whatever reason, please see if you can steal bits from existing
  // fields before adding more members to this structure.

#define JITINFO_OP_TYPE_BITS 4
#define JITINFO_ALIAS_SET_BITS 4
#define JITINFO_RETURN_TYPE_BITS 8
#define JITINFO_SLOT_INDEX_BITS 10

  /** The OpType that says what sort of function we are. */
  uint32_t type_ : JITINFO_OP_TYPE_BITS;

  /**
   * The alias set for this op.  This is a _minimal_ alias set; in
   * particular for a method it does not include whatever argument
   * conversions might do.  That's covered by argTypes and runtime
   * analysis of the actual argument types being passed in.
   */
  uint32_t aliasSet_ : JITINFO_ALIAS_SET_BITS;

  /** The return type tag.  Might be JSVAL_TYPE_UNKNOWN. */
  uint32_t returnType_ : JITINFO_RETURN_TYPE_BITS;

  static_assert(OpTypeCount <= (1 << JITINFO_OP_TYPE_BITS),
                "Not enough space for OpType");
  static_assert(AliasSetCount <= (1 << JITINFO_ALIAS_SET_BITS),
                "Not enough space for AliasSet");
  static_assert((sizeof(JSValueType) * 8) <= JITINFO_RETURN_TYPE_BITS,
                "Not enough space for JSValueType");

#undef JITINFO_RETURN_TYPE_BITS
#undef JITINFO_ALIAS_SET_BITS
#undef JITINFO_OP_TYPE_BITS

  /** Is op fallible? False in setters. */
  uint32_t isInfallible : 1;

  /**
   * Is op movable?  To be movable the op must
   * not AliasEverything, but even that might
   * not be enough (e.g. in cases when it can
   * throw or is explicitly not movable).
   */
  uint32_t isMovable : 1;

  /**
   * Can op be dead-code eliminated? Again, this
   * depends on whether the op can throw, in
   * addition to the alias set.
   */
  uint32_t isEliminatable : 1;

  // XXXbz should we have a JSValueType for the type of the member?
  /**
   * True if this is a getter that can always
   * get the value from a slot of the "this" object.
   */
  uint32_t isAlwaysInSlot : 1;

  /**
   * True if this is a getter that can sometimes (if the slot doesn't contain
   * UndefinedValue()) get the value from a slot of the "this" object.
   */
  uint32_t isLazilyCachedInSlot : 1;

  /** True if this is an instance of JSTypedMethodJitInfo. */
  uint32_t isTypedMethod : 1;

  /**
   * If isAlwaysInSlot or isSometimesInSlot is true,
   * the index of the slot to get the value from.
   * Otherwise 0.
   */
  uint32_t slotIndex : JITINFO_SLOT_INDEX_BITS;

  static const size_t maxSlotIndex = (1 << JITINFO_SLOT_INDEX_BITS) - 1;

#undef JITINFO_SLOT_INDEX_BITS
};

static_assert(sizeof(JSJitInfo) == (sizeof(void*) + 2 * sizeof(uint32_t)),
              "There are several thousand instances of JSJitInfo stored in "
              "a binary. Please don't increase its space requirements without "
              "verifying that there is no other way forward (better packing, "
              "smaller datatypes for fields, subclassing, etc.).");

struct JSTypedMethodJitInfo {
  // We use C-style inheritance here, rather than C++ style inheritance
  // because not all compilers support brace-initialization for non-aggregate
  // classes. Using C++ style inheritance and constructors instead of
  // brace-initialization would also force the creation of static
  // constructors (on some compilers) when JSJitInfo and JSTypedMethodJitInfo
  // structures are declared. Since there can be several thousand of these
  // structures present and we want to have roughly equivalent performance
  // across a range of compilers, we do things manually.
  JSJitInfo base;

  const JSJitInfo::ArgType* const argTypes; /* For a method, a list of sets of
                                               types that the function
                                               expects.  This can be used,
                                               for example, to figure out
                                               when argument coercions can
                                               have side-effects. */
};

namespace js {

static MOZ_ALWAYS_INLINE shadow::Function* FunctionObjectToShadowFunction(
    JSObject* fun) {
  MOZ_ASSERT(GetObjectClass(fun) == FunctionClassPtr);
  return reinterpret_cast<shadow::Function*>(fun);
}

/* Statically asserted in JSFunction.h. */
static const unsigned JS_FUNCTION_INTERPRETED_BITS = 0x0201;

// Return whether the given function object is native.
static MOZ_ALWAYS_INLINE bool FunctionObjectIsNative(JSObject* fun) {
  return !(FunctionObjectToShadowFunction(fun)->flags &
           JS_FUNCTION_INTERPRETED_BITS);
}

static MOZ_ALWAYS_INLINE JSNative GetFunctionObjectNative(JSObject* fun) {
  MOZ_ASSERT(FunctionObjectIsNative(fun));
  return FunctionObjectToShadowFunction(fun)->native;
}

}  // namespace js

static MOZ_ALWAYS_INLINE const JSJitInfo* FUNCTION_VALUE_TO_JITINFO(
    const JS::Value& v) {
  MOZ_ASSERT(js::FunctionObjectIsNative(&v.toObject()));
  return js::FunctionObjectToShadowFunction(&v.toObject())->jitinfo;
}

static MOZ_ALWAYS_INLINE void SET_JITINFO(JSFunction* func,
                                          const JSJitInfo* info) {
  js::shadow::Function* fun = reinterpret_cast<js::shadow::Function*>(func);
  MOZ_ASSERT(!(fun->flags & js::JS_FUNCTION_INTERPRETED_BITS));
  fun->jitinfo = info;
}

/*
 * Engine-internal extensions of jsid.  This code is here only until we
 * eliminate Gecko's dependencies on it!
 */

static MOZ_ALWAYS_INLINE jsid JSID_FROM_BITS(size_t bits) {
  jsid id;
  JSID_BITS(id) = bits;
  return id;
}

namespace js {
namespace detail {
bool IdMatchesAtom(jsid id, JSAtom* atom);
bool IdMatchesAtom(jsid id, JSString* atom);
}  // namespace detail
}  // namespace js

/**
 * Must not be used on atoms that are representable as integer jsids.
 * Prefer NameToId or AtomToId over this function:
 *
 * A PropertyName is an atom that does not contain an integer in the range
 * [0, UINT32_MAX]. However, jsid can only hold an integer in the range
 * [0, JSID_INT_MAX] (where JSID_INT_MAX == 2^31-1).  Thus, for the range of
 * integers (JSID_INT_MAX, UINT32_MAX], to represent as a jsid 'id', it must be
 * the case JSID_IS_ATOM(id) and !JSID_TO_ATOM(id)->isPropertyName().  In most
 * cases when creating a jsid, code does not have to care about this corner
 * case because:
 *
 * - When given an arbitrary JSAtom*, AtomToId must be used, which checks for
 *   integer atoms representable as integer jsids, and does this conversion.
 *
 * - When given a PropertyName*, NameToId can be used which which does not need
 *   to do any dynamic checks.
 *
 * Thus, it is only the rare third case which needs this function, which
 * handles any JSAtom* that is known not to be representable with an int jsid.
 */
static MOZ_ALWAYS_INLINE jsid NON_INTEGER_ATOM_TO_JSID(JSAtom* atom) {
  MOZ_ASSERT(((size_t)atom & 0x7) == 0);
  jsid id = JSID_FROM_BITS((size_t)atom);
  MOZ_ASSERT(js::detail::IdMatchesAtom(id, atom));
  return id;
}

static MOZ_ALWAYS_INLINE jsid NON_INTEGER_ATOM_TO_JSID(JSString* atom) {
  MOZ_ASSERT(((size_t)atom & 0x7) == 0);
  jsid id = JSID_FROM_BITS((size_t)atom);
  MOZ_ASSERT(js::detail::IdMatchesAtom(id, atom));
  return id;
}

/* All strings stored in jsids are atomized, but are not necessarily property
 * names. */
static MOZ_ALWAYS_INLINE bool JSID_IS_ATOM(jsid id) {
  return JSID_IS_STRING(id);
}

static MOZ_ALWAYS_INLINE bool JSID_IS_ATOM(jsid id, JSAtom* atom) {
  return id == JSID_FROM_BITS((size_t)atom);
}

static MOZ_ALWAYS_INLINE JSAtom* JSID_TO_ATOM(jsid id) {
  return (JSAtom*)JSID_TO_STRING(id);
}

JS_STATIC_ASSERT(sizeof(jsid) == sizeof(void*));

namespace js {

static MOZ_ALWAYS_INLINE JS::Value IdToValue(jsid id) {
  if (JSID_IS_STRING(id)) return JS::StringValue(JSID_TO_STRING(id));
  if (JSID_IS_INT(id)) return JS::Int32Value(JSID_TO_INT(id));
  if (JSID_IS_SYMBOL(id)) return JS::SymbolValue(JSID_TO_SYMBOL(id));
  MOZ_ASSERT(JSID_IS_VOID(id));
  return JS::UndefinedValue();
}

/**
 * If the embedder has registered a ScriptEnvironmentPreparer,
 * PrepareScriptEnvironmentAndInvoke will call the preparer's 'invoke' method
 * with the given |closure|, with the assumption that the preparer will set up
 * any state necessary to run script in |scope|, invoke |closure| with a valid
 * JSContext*, report any exceptions thrown from the closure, and return.
 *
 * If no preparer is registered, PrepareScriptEnvironmentAndInvoke will assert
 * that |rt| has exactly one JSContext associated with it, enter the compartment
 * of |scope| on that context, and invoke |closure|.
 *
 * In both cases, PrepareScriptEnvironmentAndInvoke will report any exceptions
 * that are thrown by the closure.  Consumers who want to propagate back
 * whether the closure succeeded should do so via members of the closure
 * itself.
 */

struct ScriptEnvironmentPreparer {
  struct Closure {
    virtual bool operator()(JSContext* cx) = 0;
  };

  virtual void invoke(JS::HandleObject scope, Closure& closure) = 0;
};

extern JS_FRIEND_API void PrepareScriptEnvironmentAndInvoke(
    JSContext* cx, JS::HandleObject scope,
    ScriptEnvironmentPreparer::Closure& closure);

JS_FRIEND_API void SetScriptEnvironmentPreparer(
    JSContext* cx, ScriptEnvironmentPreparer* preparer);

enum CTypesActivityType {
  CTYPES_CALL_BEGIN,
  CTYPES_CALL_END,
  CTYPES_CALLBACK_BEGIN,
  CTYPES_CALLBACK_END
};

typedef void (*CTypesActivityCallback)(JSContext* cx, CTypesActivityType type);

/**
 * Sets a callback that is run whenever js-ctypes is about to be used when
 * calling into C.
 */
JS_FRIEND_API void SetCTypesActivityCallback(JSContext* cx,
                                             CTypesActivityCallback cb);

class MOZ_RAII JS_FRIEND_API AutoCTypesActivityCallback {
 private:
  JSContext* cx;
  CTypesActivityCallback callback;
  CTypesActivityType endType;
  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER

 public:
  AutoCTypesActivityCallback(JSContext* cx, CTypesActivityType beginType,
                             CTypesActivityType endType
                                 MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
  ~AutoCTypesActivityCallback() { DoEndCallback(); }
  void DoEndCallback() {
    if (callback) {
      callback(cx, endType);
      callback = nullptr;
    }
  }
};

// Abstract base class for objects that build allocation metadata for JavaScript
// values.
struct AllocationMetadataBuilder {
  AllocationMetadataBuilder() {}

  // Return a metadata object for the newly constructed object |obj|, or
  // nullptr if there's no metadata to attach.
  //
  // Implementations should treat all errors as fatal; there is no way to
  // report errors from this callback. In particular, the caller provides an
  // oomUnsafe for overriding implementations to use.
  virtual JSObject* build(JSContext* cx, JS::HandleObject obj,
                          AutoEnterOOMUnsafeRegion& oomUnsafe) const {
    return nullptr;
  }
};

/**
 * Specify a callback to invoke when creating each JS object in the current
 * compartment, which may return a metadata object to associate with the
 * object.
 */
JS_FRIEND_API void SetAllocationMetadataBuilder(
    JSContext* cx, const AllocationMetadataBuilder* callback);

/** Get the metadata associated with an object. */
JS_FRIEND_API JSObject* GetAllocationMetadata(JSObject* obj);

JS_FRIEND_API bool GetElementsWithAdder(JSContext* cx, JS::HandleObject obj,
                                        JS::HandleObject receiver,
                                        uint32_t begin, uint32_t end,
                                        js::ElementAdder* adder);

JS_FRIEND_API bool ForwardToNative(JSContext* cx, JSNative native,
                                   const JS::CallArgs& args);

/**
 * Helper function for HTMLDocument and HTMLFormElement.
 *
 * These are the only two interfaces that have [OverrideBuiltins], a named
 * getter, and no named setter. They're implemented as proxies with a custom
 * getOwnPropertyDescriptor() method. Unfortunately, overriding
 * getOwnPropertyDescriptor() automatically affects the behavior of set(),
 * which normally is just common sense but is *not* desired for these two
 * interfaces.
 *
 * The fix is for these two interfaces to override set() to ignore the
 * getOwnPropertyDescriptor() override.
 *
 * SetPropertyIgnoringNamedGetter is exposed to make it easier to override
 * set() in this way.  It carries out all the steps of BaseProxyHandler::set()
 * except the initial getOwnPropertyDescriptor() call.  The caller must supply
 * that descriptor as the 'ownDesc' parameter.
 *
 * Implemented in proxy/BaseProxyHandler.cpp.
 */
JS_FRIEND_API bool SetPropertyIgnoringNamedGetter(
    JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v,
    JS::HandleValue receiver, JS::Handle<JS::PropertyDescriptor> ownDesc,
    JS::ObjectOpResult& result);

// This function is for one specific use case, please don't use this for
// anything else!
extern JS_FRIEND_API bool ExecuteInGlobalAndReturnScope(
    JSContext* cx, JS::HandleObject obj, JS::HandleScript script,
    JS::MutableHandleObject scope);

// These functions are provided for the JSM component loader in Gecko.
//
// A 'JSMEnvironment' refers to an environment chain constructed for JSM loading
// in a shared global. Internally it is a NonSyntacticVariablesObject with a
// corresponding extensible LexicalEnvironmentObject that is accessible by
// JS_ExtensibleLexicalEnvironment. The |this| value of that lexical environment
// is the NSVO itself.
//
// Normal global environment (ES6):     JSM "global" environment:
//
//                                      * - extensible lexical environment
//                                      |   (code runs in this environment;
//                                      |    `let/const` bindings go here)
//                                      |
//                                      * - JSMEnvironment (=== `this`)
//                                      |   (`var` bindings go here)
//                                      |
// * - extensible lexical environment   * - extensible lexical environment
// |   (code runs in this environment;  |   (empty)
// |    `let/const` bindings go here)   |
// |                                    |
// * - actual global (=== `this`)       * - shared JSM global
//     (var bindings go here; and           (Object, Math, etc. live here)
//      Object, Math, etc. live here)

// Allocate a new environment in current compartment that is compatible with JSM
// shared loading.
extern JS_FRIEND_API JSObject* NewJSMEnvironment(JSContext* cx);

// Execute the given script (copied into compartment if necessary) in the given
// JSMEnvironment. The script must have been compiled for hasNonSyntacticScope.
// The |jsmEnv| must have been previously allocated by NewJSMEnvironment.
//
// NOTE: The associated extensible lexical environment is reused.
extern JS_FRIEND_API bool ExecuteInJSMEnvironment(JSContext* cx,
                                                  JS::HandleScript script,
                                                  JS::HandleObject jsmEnv);

// Additionally, target objects may be specified as required by the Gecko
// subscript loader. These are wrapped in non-syntactic WithEnvironments and
// temporarily placed on environment chain.
//
// See also: JS::CloneAndExecuteScript(...)
extern JS_FRIEND_API bool ExecuteInJSMEnvironment(
    JSContext* cx, JS::HandleScript script, JS::HandleObject jsmEnv,
    JS::AutoObjectVector& targetObj);

// Used by native methods to determine the JSMEnvironment of caller if possible
// by looking at stack frames. Returns nullptr if top frame isn't a scripted
// caller in a JSM.
//
// NOTE: This may find NonSyntacticVariablesObject generated by other embedding
// such as a Gecko FrameScript. Caller can check the compartment if needed.
extern JS_FRIEND_API JSObject* GetJSMEnvironmentOfScriptedCaller(JSContext* cx);

// Determine if obj is a JSMEnvironment
//
// NOTE: This may return true for an NonSyntacticVariablesObject generated by
// other embedding such as a Gecko FrameScript. Caller can check compartment.
extern JS_FRIEND_API bool IsJSMEnvironment(JSObject* obj);

#if defined(XP_WIN) && defined(_WIN64)
// Parameters use void* types to avoid #including windows.h. The return value of
// this function is returned from the exception handler.
typedef long (*JitExceptionHandler)(void* exceptionRecord,  // PEXECTION_RECORD
                                    void* context);         // PCONTEXT

/**
 * Windows uses "structured exception handling" to handle faults. When a fault
 * occurs, the stack is searched for a handler (similar to C++ exception
 * handling). If the search does not find a handler, the "unhandled exception
 * filter" is called. Breakpad uses the unhandled exception filter to do crash
 * reporting. Unfortunately, on Win64, JIT code on the stack completely throws
 * off this unwinding process and prevents the unhandled exception filter from
 * being called. The reason is that Win64 requires unwind information be
 * registered for all code regions and JIT code has none. While it is possible
 * to register full unwind information for JIT code, this is a lot of work (one
 * has to be able to recover the frame pointer at any PC) so instead we register
 * a handler for all JIT code that simply calls breakpad's unhandled exception
 * filter (which will perform crash reporting and then terminate the process).
 * This would be wrong if there was an outer __try block that expected to handle
 * the fault, but this is not generally allowed.
 *
 * Gecko must call SetJitExceptionFilter before any JIT code is compiled and
 * only once per process.
 */
extern JS_FRIEND_API void SetJitExceptionHandler(JitExceptionHandler handler);
#endif

/**
 * Get the first SavedFrame object in this SavedFrame stack whose principals are
 * subsumed by the cx's principals. If there is no such frame, return nullptr.
 *
 * Do NOT pass a non-SavedFrame object here.
 *
 * The savedFrame and cx do not need to be in the same compartment.
 */
extern JS_FRIEND_API JSObject* GetFirstSubsumedSavedFrame(
    JSContext* cx, JS::HandleObject savedFrame,
    JS::SavedFrameSelfHosted selfHosted);

/**
 * Get the first SavedFrame object in this SavedFrame stack whose principals are
 * subsumed by the given |principals|. If there is no such frame, return
 * nullptr.
 *
 * Do NOT pass a non-SavedFrame object here.
 */
extern JS_FRIEND_API JSObject* GetFirstSubsumedSavedFrame(
    JSContext* cx, JSPrincipals* principals, JS::HandleObject savedFrame,
    JS::SavedFrameSelfHosted selfHosted);

extern JS_FRIEND_API bool ReportIsNotFunction(JSContext* cx, JS::HandleValue v);

extern JS_FRIEND_API JSObject* ConvertArgsToArray(JSContext* cx,
                                                  const JS::CallArgs& args);

/**
 * Window and WindowProxy
 *
 * The functions below have to do with Windows and WindowProxies. There's an
 * invariant that actual Window objects (the global objects of web pages) are
 * never directly exposed to script. Instead we often substitute a WindowProxy.
 *
 * The environment chain, on the other hand, contains the Window and never its
 * WindowProxy.
 *
 * As a result, we have calls to these "substitute-this-object-for-that-object"
 * functions sprinkled at apparently arbitrary (but actually *very* carefully
 * and nervously selected) places throughout the engine and indeed the
 * universe.
 */

/**
 * Tell the JS engine which Class is used for WindowProxy objects. Used by the
 * functions below.
 */
extern JS_FRIEND_API void SetWindowProxyClass(JSContext* cx,
                                              const Class* clasp);

/**
 * Associates a WindowProxy with a Window (global object). `windowProxy` must
 * have the Class set by SetWindowProxyClass.
 */
extern JS_FRIEND_API void SetWindowProxy(JSContext* cx, JS::HandleObject global,
                                         JS::HandleObject windowProxy);

namespace detail {

JS_FRIEND_API bool IsWindowSlow(JSObject* obj);

JS_FRIEND_API JSObject* ToWindowProxyIfWindowSlow(JSObject* obj);

}  // namespace detail

/**
 * Returns true iff `obj` is a global object with an associated WindowProxy,
 * see SetWindowProxy.
 */
inline bool IsWindow(JSObject* obj) {
  if (GetObjectClass(obj)->flags & JSCLASS_IS_GLOBAL)
    return detail::IsWindowSlow(obj);
  return false;
}

/**
 * Returns true iff `obj` has the WindowProxy Class (see SetWindowProxyClass).
 */
JS_FRIEND_API bool IsWindowProxy(JSObject* obj);

/**
 * If `obj` is a Window, get its associated WindowProxy (or a CCW or dead
 * wrapper if the page was navigated away from), else return `obj`. This
 * function is infallible and never returns nullptr.
 */
MOZ_ALWAYS_INLINE JSObject* ToWindowProxyIfWindow(JSObject* obj) {
  if (GetObjectClass(obj)->flags & JSCLASS_IS_GLOBAL)
    return detail::ToWindowProxyIfWindowSlow(obj);
  return obj;
}

/**
 * If `obj` is a WindowProxy, get its associated Window (the compartment's
 * global), else return `obj`. This function is infallible and never returns
 * nullptr.
 */
extern JS_FRIEND_API JSObject* ToWindowIfWindowProxy(JSObject* obj);

// Create and add the Intl.MozDateTimeFormat constructor function to the
// provided object.
//
// This custom date/time formatter constructor gives users the ability
// to specify a custom format pattern. This pattern is passed *directly*
// to ICU with NO SYNTAX PARSING OR VALIDATION WHATSOEVER. ICU appears to
// have a a modicum of testing of this, and it won't fall over completely
// if passed bad input. But the current behavior is entirely under-specified
// and emphatically not shippable on the web, and it *must* be fixed before
// this functionality can be exposed in the real world. (There are also some
// questions about whether the format exposed here is the *right* one to
// standardize, that will also need to be resolved to ship this.)
extern bool AddMozDateTimeFormatConstructor(JSContext* cx,
                                            JS::Handle<JSObject*> intl);

// Create and add the Intl.RelativeTimeFormat constructor function to the
// provided object.  This function throws if called more than once per
// realm/global object.
extern bool AddRelativeTimeFormatConstructor(JSContext* cx,
                                             JS::Handle<JSObject*> intl);

class MOZ_STACK_CLASS JS_FRIEND_API AutoAssertNoContentJS {
 public:
  explicit AutoAssertNoContentJS(JSContext* cx);
  ~AutoAssertNoContentJS();

 private:
  JSContext* context_;
  bool prevAllowContentJS_;
};

// Turn on assertions so that we assert that
//     !comp->validAccessPtr || *comp->validAccessPtr
// is true for every |comp| that we run JS code in. The compartment's
// validAccessPtr is set via SetCompartmentValidAccessPtr.
extern JS_FRIEND_API void EnableAccessValidation(JSContext* cx, bool enabled);

// See EnableAccessValidation above. The caller must guarantee that accessp will
// live at least as long as |global| is alive. The JS engine reads accessp from
// threads that are allowed to run code on |global|, so all changes to *accessp
// should be made from whichever thread owns |global| at a given time.
extern JS_FRIEND_API void SetCompartmentValidAccessPtr(JSContext* cx,
                                                       JS::HandleObject global,
                                                       bool* accessp);

// If the JS engine wants to block so that other cooperative threads can run, it
// will call the yield callback. It may do this if it needs to access a
// ZoneGroup that is held by another thread (such as the system zone group).
typedef void (*YieldCallback)(JSContext* cx);

extern JS_FRIEND_API void SetCooperativeYieldCallback(JSContext* cx,
                                                      YieldCallback callback);

// Returns true if the system zone is available (i.e., if no cooperative
// contexts are using it now).
extern JS_FRIEND_API bool SystemZoneAvailable(JSContext* cx);

} /* namespace js */

#endif /* jsfriendapi_h */
