diff options
Diffstat (limited to 'js/src/jsfriendapi.h')
-rw-r--r-- | js/src/jsfriendapi.h | 3067 |
1 files changed, 3067 insertions, 0 deletions
diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h new file mode 100644 index 000000000..b1c7cb0dc --- /dev/null +++ b/js/src/jsfriendapi.h @@ -0,0 +1,3067 @@ +/* -*- 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/Maybe.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/UniquePtr.h" + +#include "jsapi.h" // For JSAutoByteString. See bug 1033916. +#include "jsbytecode.h" +#include "jspubtd.h" + +#include "js/CallArgs.h" +#include "js/CallNonGenericMethod.h" +#include "js/Class.h" +#include "js/Utility.h" + +#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 + +class JSAtom; +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(size_t) +JS_SetProtoCalled(JSContext* cx); + +extern JS_FRIEND_API(size_t) +JS_GetCustomIteratorCount(JSContext* cx); + +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); + +/* + * 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); + +enum { + JS_TELEMETRY_GC_REASON, + JS_TELEMETRY_GC_IS_ZONE_GC, + JS_TELEMETRY_GC_MS, + JS_TELEMETRY_GC_BUDGET_MS, + JS_TELEMETRY_GC_ANIMATION_MS, + JS_TELEMETRY_GC_MAX_PAUSE_MS, + 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_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_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS, + JS_TELEMETRY_ADDON_EXCEPTIONS, + JS_TELEMETRY_AOT_USAGE, + 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); + +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); + +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(char*) +FormatStackDump(JSContext* cx, char* 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); + +} // 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 | JSFUN_STUB_GSOPS, nullptr, usage, help} +#define JS_INLINABLE_FN_HELP(name,call,nargs,flags,native,usage,help) \ + {name, call, nargs, (flags) | JSPROP_ENUMERATE | JSFUN_STUB_GSOPS, &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_DATA(const js::ClassOps) ProxyClassOps; +extern JS_FRIEND_DATA(const js::ClassExtension) ProxyClassExtension; +extern JS_FRIEND_DATA(const js::ObjectOps) ProxyObjectOps; + +/* + * Helper Macros for creating JSClasses that function as proxies. + * + * NB: The macro invocation must be surrounded by braces, so as to + * allow for potential JSClass extensions. + */ +#define PROXY_MAKE_EXT(objectMoved) \ + { \ + js::proxy_WeakmapKeyDelegate, \ + objectMoved \ + } + +#define PROXY_CLASS_WITH_EXT(name, flags, extPtr) \ + { \ + name, \ + js::Class::NON_NATIVE | \ + JSCLASS_IS_PROXY | \ + JSCLASS_DELAY_METADATA_BUILDER | \ + flags, \ + &js::ProxyClassOps, \ + JS_NULL_CLASS_SPEC, \ + extPtr, \ + &js::ProxyObjectOps \ + } + +#define PROXY_CLASS_DEF(name, flags) \ + PROXY_CLASS_WITH_EXT(name, flags, &js::ProxyClassExtension) + +/* + * Proxy stubs, similar to JS_*Stub, for embedder proxy class definitions. + * + * NB: Should not be called directly. + */ + +extern JS_FRIEND_API(bool) +proxy_LookupProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleObject objp, + JS::MutableHandle<Shape*> propp); +extern JS_FRIEND_API(bool) +proxy_DefineProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, + JS::Handle<JS::PropertyDescriptor> desc, + JS::ObjectOpResult& result); +extern JS_FRIEND_API(bool) +proxy_HasProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* foundp); +extern JS_FRIEND_API(bool) +proxy_GetProperty(JSContext* cx, JS::HandleObject obj, JS::HandleValue receiver, JS::HandleId id, + JS::MutableHandleValue vp); +extern JS_FRIEND_API(bool) +proxy_SetProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue bp, + JS::HandleValue receiver, JS::ObjectOpResult& result); +extern JS_FRIEND_API(bool) +proxy_GetOwnPropertyDescriptor(JSContext* cx, JS::HandleObject obj, JS::HandleId id, + JS::MutableHandle<JS::PropertyDescriptor> desc); +extern JS_FRIEND_API(bool) +proxy_DeleteProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, + JS::ObjectOpResult& result); + +extern JS_FRIEND_API(void) +proxy_Trace(JSTracer* trc, JSObject* obj); +extern JS_FRIEND_API(JSObject*) +proxy_WeakmapKeyDelegate(JSObject* obj); +extern JS_FRIEND_API(bool) +proxy_Convert(JSContext* cx, JS::HandleObject proxy, JSType hint, JS::MutableHandleValue vp); +extern JS_FRIEND_API(void) +proxy_Finalize(FreeOp* fop, JSObject* obj); +extern JS_FRIEND_API(void) +proxy_ObjectMoved(JSObject* obj, const JSObject* old); +extern JS_FRIEND_API(bool) +proxy_HasInstance(JSContext* cx, JS::HandleObject proxy, JS::MutableHandleValue v, bool* bp); +extern JS_FRIEND_API(bool) +proxy_Call(JSContext* cx, unsigned argc, JS::Value* vp); +extern JS_FRIEND_API(bool) +proxy_Construct(JSContext* cx, unsigned argc, JS::Value* vp); +extern JS_FRIEND_API(JSObject*) +proxy_innerObject(JSObject* obj); +extern JS_FRIEND_API(bool) +proxy_Watch(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable); +extern JS_FRIEND_API(bool) +proxy_Unwatch(JSContext* cx, JS::HandleObject obj, JS::HandleId id); +extern JS_FRIEND_API(bool) +proxy_GetElements(JSContext* cx, JS::HandleObject proxy, uint32_t begin, uint32_t end, + ElementAdder* adder); +extern JS_FRIEND_API(JSString*) +proxy_FunToString(JSContext* cx, JS::HandleObject proxy, unsigned indent); + +/** + * 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); + +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 +{ + JSContext* context; + + explicit WeakMapTracer(JSContext* cx) : context(cx) {} + + // 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(JSContext* cx); + +extern JS_FRIEND_API(bool) +ZoneGlobalsAreAllGray(JS::Zone* zone); + +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); + +#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; + jsid _1; + 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; + + 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 INLINE_CHARS_BIT = JS_BIT(2); + static const uint32_t LATIN1_CHARS_BIT = JS_BIT(6); + static const uint32_t ROPE_FLAGS = 0; + static const uint32_t TYPE_FLAGS_MASK = JS_BIT(6) - 1; + uint32_t flags; + uint32_t length; + union { + const JS::Latin1Char* nonInlineCharsLatin1; + const char16_t* nonInlineCharsTwoByte; + JS::Latin1Char inlineStorageLatin1[1]; + char16_t inlineStorageTwoByte[1]; + }; +}; + +} /* namespace shadow */ + +// This is equal to |&JSObject::class_|. Use it in places where you don't want +// to #include jsobj.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); + +#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); + +/** + * Return the outermost enclosing function (script) of the scripted caller. + * This function returns nullptr in several cases: + * - no script is running on the context + * - the caller is in global or eval code + * In particular, this function will "stop" its outermost search at eval() and + * thus it will really return the outermost enclosing function *since the + * innermost eval*. + */ +JS_FRIEND_API(JSFunction*) +GetOutermostEnclosingFunctionOfScriptedCaller(JSContext* cx); + +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; +} + +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) +SetReservedOrProxyPrivateSlotWithBarrier(JSObject* obj, size_t slot, const JS::Value& value); + +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).isMarkable() || value.isMarkable()) + SetReservedOrProxyPrivateSlotWithBarrier(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::AutoCheckCannotGC& 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::AutoCheckCannotGC& 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::AutoCheckCannotGC& nogc, JSAtom* atom) +{ + return GetLatin1LinearStringChars(nogc, AtomToLinearString(atom)); +} + +MOZ_ALWAYS_INLINE const char16_t* +GetTwoByteAtomChars(const JS::AutoCheckCannotGC& nogc, JSAtom* atom) +{ + return GetTwoByteLinearStringChars(nogc, AtomToLinearString(atom)); +} + +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::TYPE_FLAGS_MASK) == String::ROPE_FLAGS)) + 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. + * The first three are omitted because they shouldn't be used in new code. + */ +#define JSITER_ENUMERATE 0x1 /* for-in compatible hidden default iterator */ +#define JSITER_FOREACH 0x2 /* get obj[key] for each property */ +#define JSITER_KEYVALUE 0x4 /* obsolete destructuring for-in wants [key, value] */ +#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 */ + +JS_FRIEND_API(bool) +RunningWithTrustedPrincipals(JSContext* cx); + +inline uintptr_t +GetNativeStackLimit(JSContext* cx, StackKind kind, int extraAllowance = 0) +{ + uintptr_t limit = ContextFriendFields::get(cx)->nativeStackLimit[kind]; +#if JS_STACK_GROWTH_DIRECTION > 0 + limit += extraAllowance; +#else + limit -= extraAllowance; +#endif + return limit; +} + +inline uintptr_t +GetNativeStackLimit(JSContext* cx, int extraAllowance = 0) +{ + StackKind kind = RunningWithTrustedPrincipals(cx) ? StackForTrustedScript + : StackForUntrustedScript; + return GetNativeStackLimit(cx, kind, extraAllowance); +} + +/* + * These macros report a stack overflow and run |onerror| if we are close to + * using up the C stack. The JS_CHECK_CHROME_RECURSION variant gives us a + * little extra space so that we can ensure that crucial code is able to run. + * JS_CHECK_RECURSION_CONSERVATIVE 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). + */ + +#define JS_CHECK_RECURSION_LIMIT(cx, limit, onerror) \ + JS_BEGIN_MACRO \ + int stackDummy_; \ + if (!JS_CHECK_STACK_SIZE(limit, &stackDummy_)) { \ + js::ReportOverRecursed(cx); \ + onerror; \ + } \ + JS_END_MACRO + +#define JS_CHECK_RECURSION(cx, onerror) \ + JS_CHECK_RECURSION_LIMIT(cx, js::GetNativeStackLimit(cx), onerror) + +#define JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, limit, onerror) \ + JS_BEGIN_MACRO \ + int stackDummy_; \ + if (!JS_CHECK_STACK_SIZE(limit, &stackDummy_)) { \ + onerror; \ + } \ + JS_END_MACRO + +#define JS_CHECK_RECURSION_DONT_REPORT(cx, onerror) \ + JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, js::GetNativeStackLimit(cx), onerror) + +#define JS_CHECK_RECURSION_WITH_SP_DONT_REPORT(cx, sp, onerror) \ + JS_BEGIN_MACRO \ + if (!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp)) { \ + onerror; \ + } \ + JS_END_MACRO + +#define JS_CHECK_RECURSION_WITH_SP(cx, sp, onerror) \ + JS_BEGIN_MACRO \ + if (!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp)) { \ + js::ReportOverRecursed(cx); \ + onerror; \ + } \ + JS_END_MACRO + +#define JS_CHECK_SYSTEM_RECURSION(cx, onerror) \ + JS_CHECK_RECURSION_LIMIT(cx, js::GetNativeStackLimit(cx, js::StackForSystemCode), onerror) + +#define JS_CHECK_RECURSION_CONSERVATIVE(cx, onerror) \ + JS_CHECK_RECURSION_LIMIT(cx, \ + js::GetNativeStackLimit(cx, js::StackForUntrustedScript, -1024 * int(sizeof(size_t))), \ + onerror) + +#define JS_CHECK_RECURSION_CONSERVATIVE_DONT_REPORT(cx, onerror) \ + JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, \ + js::GetNativeStackLimit(cx, js::StackForUntrustedScript, -1024 * int(sizeof(size_t))), \ + onerror) + +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 + +class RegExpGuard; +extern JS_FRIEND_API(bool) +RegExpToSharedNonInline(JSContext* cx, JS::HandleObject regexp, RegExpGuard* shared); + +/* Implemented in jswrapper.cpp. */ +typedef enum NukeReferencesToWindow { + NukeWindowReferences, + DontNukeWindowReferences +} NukeReferencesToWindow; + +/* + * 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, + const CompartmentFilter& targetFilter, + NukeReferencesToWindow nukeReferencesToWindow); + +/* 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, uint32_t domProxyExpandoSlot, + DOMProxyShadowsCheck domProxyShadowsCheck); + +const void* GetDOMProxyHandlerFamily(); +uint32_t GetDOMProxyExpandoSlot(); +DOMProxyShadowsCheck GetDOMProxyShadowsCheck(); +inline bool DOMProxyIsShadowing(DOMProxyShadowsResult result) { + return result == Shadows || + result == ShadowsViaDirectExpando || + result == ShadowsViaIndirectExpando; +} + +/* 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 */ + +/* Implemented in jscntxt.cpp. */ + +/** + * Report an exception, which is currently realized as a printf-style format + * string and its arguments. + */ +typedef enum JSErrNum { +#define MSG_DEF(name, count, exception, format) \ + name, +#include "js.msg" +#undef MSG_DEF + JSErr_Limit +} JSErrNum; + +namespace js { + +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 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 telementry. + void ReportAddonExceptionToTelementry(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); + + +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::AutoCheckCannotGC&); + +/* + * 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::AutoCheckCannotGC&); + +/** + * 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); + +/* + * 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::AutoCheckCannotGC&); +extern JS_FRIEND_API(uint8_t*) +JS_GetUint8ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&); +extern JS_FRIEND_API(uint8_t*) +JS_GetUint8ClampedArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&); +extern JS_FRIEND_API(int16_t*) +JS_GetInt16ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&); +extern JS_FRIEND_API(uint16_t*) +JS_GetUint16ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&); +extern JS_FRIEND_API(int32_t*) +JS_GetInt32ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&); +extern JS_FRIEND_API(uint32_t*) +JS_GetUint32ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&); +extern JS_FRIEND_API(float*) +JS_GetFloat32ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&); +extern JS_FRIEND_API(double*) +JS_GetFloat64ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&); + +/** + * 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::AutoCheckCannotGC&); + +/** + * 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 ArrayBuffer for storage. The given + * buffer must be an ArrayBuffer (or a cross-compartment wrapper of an + * ArrayBuffer), and the offset and length must fit within the bounds of the + * arrayBuffer. 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 arrayBuffer, 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. + */ +JS_FRIEND_API(void*) +JS_GetDataViewData(JSObject* obj, const JS::AutoCheckCannotGC&); + +namespace js { + +/** + * Add a watchpoint -- in the Object.prototype.watch sense -- to |obj| for the + * property |id|, using the callable object |callable| as the function to be + * called for notifications. + * + * This is an internal function exposed -- temporarily -- only so that DOM + * proxies can be watchable. Don't use it! We'll soon kill off the + * Object.prototype.{,un}watch functions, at which point this will go too. + */ +extern JS_FRIEND_API(bool) +WatchGuts(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable); + +/** + * Remove a watchpoint -- in the Object.prototype.watch sense -- from |obj| for + * the property |id|. + * + * This is an internal function exposed -- temporarily -- only so that DOM + * proxies can be watchable. Don't use it! We'll soon kill off the + * Object.prototype.{,un}watch functions, at which point this will go too. + */ +extern JS_FRIEND_API(bool) +UnwatchGuts(JSContext* cx, JS::HandleObject obj, JS::HandleId id); + +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, + // 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; + }; + + 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 jsfun.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); +} // 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; +} + +/* 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); + +JS_FRIEND_API(void) +ReportASCIIErrorWithId(JSContext* cx, const char* msg, JS::HandleId id); + +// 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); + +#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 nearest enclosing with environment object for a given function. If + * the function is not scripted or is not enclosed by a with scope, returns + * the global. + */ +extern JS_FRIEND_API(JSObject*) +GetNearestEnclosingWithEnvironmentObjectForFunction(JSFunction* fun); + +/** + * 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); + +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); + +} // 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. + */ +extern JS_FRIEND_API(JSObject*) +ToWindowProxyIfWindow(JSObject* 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); + +} /* namespace js */ + +class NativeProfiler +{ + public: + virtual ~NativeProfiler() {}; + virtual void sampleNative(void* addr, uint32_t size) = 0; + virtual void removeNative(void* addr) = 0; + virtual void reset() = 0; +}; + +class GCHeapProfiler +{ + public: + virtual ~GCHeapProfiler() {}; + virtual void sampleTenured(void* addr, uint32_t size) = 0; + virtual void sampleNursery(void* addr, uint32_t size) = 0; + virtual void markTenuredStart() = 0; + virtual void markTenured(void* addr) = 0; + virtual void sweepTenured() = 0; + virtual void sweepNursery() = 0; + virtual void moveNurseryToTenured(void* addrOld, void* addrNew) = 0; + virtual void reset() = 0; +}; + +class MemProfiler +{ + static mozilla::Atomic<uint32_t, mozilla::Relaxed> sActiveProfilerCount; + static JS_FRIEND_DATA(NativeProfiler*) sNativeProfiler; + + static GCHeapProfiler* GetGCHeapProfiler(void* addr); + static GCHeapProfiler* GetGCHeapProfiler(JSRuntime* runtime); + + static NativeProfiler* GetNativeProfiler() { + return sNativeProfiler; + } + + GCHeapProfiler* mGCHeapProfiler; + JSRuntime* mRuntime; + + public: + explicit MemProfiler(JSRuntime* aRuntime) : mGCHeapProfiler(nullptr), mRuntime(aRuntime) {} + + JS_FRIEND_API(void) start(GCHeapProfiler* aGCHeapProfiler); + JS_FRIEND_API(void) stop(); + + GCHeapProfiler* getGCHeapProfiler() const { + return mGCHeapProfiler; + } + + static MOZ_ALWAYS_INLINE bool enabled() { + return sActiveProfilerCount > 0; + } + + static JS_FRIEND_API(MemProfiler*) GetMemProfiler(JSContext* context); + + static void SetNativeProfiler(NativeProfiler* aProfiler) { + sNativeProfiler = aProfiler; + } + + static MOZ_ALWAYS_INLINE void SampleNative(void* addr, uint32_t size) { + JS::AutoSuppressGCAnalysis nogc; + + if (MOZ_LIKELY(!enabled())) + return; + + NativeProfiler* profiler = GetNativeProfiler(); + if (profiler) + profiler->sampleNative(addr, size); + } + + static MOZ_ALWAYS_INLINE void SampleTenured(void* addr, uint32_t size) { + JS::AutoSuppressGCAnalysis nogc; + + if (MOZ_LIKELY(!enabled())) + return; + + GCHeapProfiler* profiler = GetGCHeapProfiler(addr); + if (profiler) + profiler->sampleTenured(addr, size); + } + + static MOZ_ALWAYS_INLINE void SampleNursery(void* addr, uint32_t size) { + JS::AutoSuppressGCAnalysis nogc; + + if (MOZ_LIKELY(!enabled())) + return; + + GCHeapProfiler* profiler = GetGCHeapProfiler(addr); + if (profiler) + profiler->sampleNursery(addr, size); + } + + static MOZ_ALWAYS_INLINE void RemoveNative(void* addr) { + JS::AutoSuppressGCAnalysis nogc; + + if (MOZ_LIKELY(!enabled())) + return; + + NativeProfiler* profiler = GetNativeProfiler(); + if (profiler) + profiler->removeNative(addr); + } + + static MOZ_ALWAYS_INLINE void MarkTenuredStart(JSRuntime* runtime) { + JS::AutoSuppressGCAnalysis nogc; + + if (MOZ_LIKELY(!enabled())) + return; + + GCHeapProfiler* profiler = GetGCHeapProfiler(runtime); + if (profiler) + profiler->markTenuredStart(); + } + + static MOZ_ALWAYS_INLINE void MarkTenured(void* addr) { + JS::AutoSuppressGCAnalysis nogc; + + if (MOZ_LIKELY(!enabled())) + return; + + GCHeapProfiler* profiler = GetGCHeapProfiler(addr); + if (profiler) + profiler->markTenured(addr); + } + + static MOZ_ALWAYS_INLINE void SweepTenured(JSRuntime* runtime) { + JS::AutoSuppressGCAnalysis nogc; + + if (MOZ_LIKELY(!enabled())) + return; + + GCHeapProfiler* profiler = GetGCHeapProfiler(runtime); + if (profiler) + profiler->sweepTenured(); + } + + static MOZ_ALWAYS_INLINE void SweepNursery(JSRuntime* runtime) { + JS::AutoSuppressGCAnalysis nogc; + + if (MOZ_LIKELY(!enabled())) + return; + + GCHeapProfiler* profiler = GetGCHeapProfiler(runtime); + if (profiler) + profiler->sweepNursery(); + } + + static MOZ_ALWAYS_INLINE void MoveNurseryToTenured(void* addrOld, void* addrNew) { + JS::AutoSuppressGCAnalysis nogc; + + if (MOZ_LIKELY(!enabled())) + return; + + GCHeapProfiler* profiler = GetGCHeapProfiler(addrOld); + if (profiler) + profiler->moveNurseryToTenured(addrOld, addrNew); + } +}; + +#endif /* jsfriendapi_h */ |