/* -*- 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/. */ /* * JavaScript API. */ #include "jsapi.h" #include "mozilla/FloatingPoint.h" #include "mozilla/Maybe.h" #include "mozilla/PodOperations.h" #include "mozilla/Sprintf.h" #include #include #include #include #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" #include "jscntxt.h" #include "jsdate.h" #include "jsexn.h" #include "jsfriendapi.h" #include "jsfun.h" #include "jsgc.h" #include "jsiter.h" #include "jsmath.h" #include "jsnum.h" #include "jsobj.h" #include "json.h" #include "jsprf.h" #include "jsscript.h" #include "jsstr.h" #include "jstypes.h" #include "jsutil.h" #include "jswatchpoint.h" #include "jsweakmap.h" #include "jswrapper.h" #include "builtin/AtomicsObject.h" #include "builtin/Eval.h" #include "builtin/Intl.h" #include "builtin/MapObject.h" #include "builtin/Promise.h" #include "builtin/RegExp.h" #include "builtin/SymbolObject.h" #ifdef ENABLE_SIMD # include "builtin/SIMD.h" #endif #ifdef ENABLE_BINARYDATA # include "builtin/TypedObject.h" #endif #include "frontend/BytecodeCompiler.h" #include "frontend/FullParseHandler.h" // for JS_BufferIsCompileableUnit #include "frontend/Parser.h" // for JS_BufferIsCompileableUnit #include "gc/Marking.h" #include "gc/Policy.h" #include "jit/JitCommon.h" #include "js/CharacterEncoding.h" #include "js/Conversions.h" #include "js/Date.h" #include "js/Initialization.h" #include "js/Proxy.h" #include "js/SliceBudget.h" #include "js/StructuredClone.h" #include "js/Utility.h" #include "vm/AsyncFunction.h" #include "vm/DateObject.h" #include "vm/Debugger.h" #include "vm/EnvironmentObject.h" #include "vm/ErrorObject.h" #include "vm/HelperThreads.h" #include "vm/Interpreter.h" #include "vm/RegExpStatics.h" #include "vm/Runtime.h" #include "vm/SavedStacks.h" #include "vm/SelfHosting.h" #include "vm/Shape.h" #include "vm/StopIterationObject.h" #include "vm/String.h" #include "vm/StringBuffer.h" #include "vm/Symbol.h" #include "vm/TypedArrayCommon.h" #include "vm/WrapperObject.h" #include "vm/Xdr.h" #include "wasm/AsmJS.h" #include "wasm/WasmModule.h" #include "jsatominlines.h" #include "jsfuninlines.h" #include "jsscriptinlines.h" #include "vm/Interpreter-inl.h" #include "vm/NativeObject-inl.h" #include "vm/SavedStacks-inl.h" #include "vm/String-inl.h" using namespace js; using namespace js::gc; using mozilla::Maybe; using mozilla::PodCopy; using mozilla::PodZero; using mozilla::Some; using JS::AutoGCRooter; using JS::ToInt32; using JS::ToInteger; using JS::ToUint32; #ifdef HAVE_VA_LIST_AS_ARRAY #define JS_ADDRESSOF_VA_LIST(ap) ((va_list*)(ap)) #else #define JS_ADDRESSOF_VA_LIST(ap) (&(ap)) #endif JS_PUBLIC_API(bool) JS::CallArgs::requireAtLeast(JSContext* cx, const char* fnname, unsigned required) const { if (length() < required) { char numArgsStr[40]; SprintfLiteral(numArgsStr, "%u", required - 1); JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, fnname, numArgsStr, required == 2 ? "" : "s"); return false; } return true; } static bool ErrorTakesArguments(unsigned msg) { MOZ_ASSERT(msg < JSErr_Limit); unsigned argCount = js_ErrorFormatString[msg].argCount; MOZ_ASSERT(argCount <= 2); return argCount == 1 || argCount == 2; } static bool ErrorTakesObjectArgument(unsigned msg) { MOZ_ASSERT(msg < JSErr_Limit); unsigned argCount = js_ErrorFormatString[msg].argCount; MOZ_ASSERT(argCount <= 2); return argCount == 2; } JS_PUBLIC_API(bool) JS::ObjectOpResult::reportStrictErrorOrWarning(JSContext* cx, HandleObject obj, HandleId id, bool strict) { static_assert(unsigned(OkCode) == unsigned(JSMSG_NOT_AN_ERROR), "unsigned value of OkCode must not be an error code"); MOZ_ASSERT(code_ != Uninitialized); MOZ_ASSERT(!ok()); unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_WARNING | JSREPORT_STRICT); if (code_ == JSMSG_OBJECT_NOT_EXTENSIBLE || code_ == JSMSG_SET_NON_OBJECT_RECEIVER) { RootedValue val(cx, ObjectValue(*obj)); return ReportValueErrorFlags(cx, flags, code_, JSDVG_IGNORE_STACK, val, nullptr, nullptr, nullptr); } if (ErrorTakesArguments(code_)) { RootedValue idv(cx, IdToValue(id)); RootedString str(cx, ValueToSource(cx, idv)); if (!str) return false; JSAutoByteString propName; if (!propName.encodeUtf8(cx, str)) return false; if (ErrorTakesObjectArgument(code_)) { return JS_ReportErrorFlagsAndNumberUTF8(cx, flags, GetErrorMessage, nullptr, code_, obj->getClass()->name, propName.ptr()); } return JS_ReportErrorFlagsAndNumberUTF8(cx, flags, GetErrorMessage, nullptr, code_, propName.ptr()); } return JS_ReportErrorFlagsAndNumberASCII(cx, flags, GetErrorMessage, nullptr, code_); } JS_PUBLIC_API(bool) JS::ObjectOpResult::reportStrictErrorOrWarning(JSContext* cx, HandleObject obj, bool strict) { MOZ_ASSERT(code_ != Uninitialized); MOZ_ASSERT(!ok()); MOZ_ASSERT(!ErrorTakesArguments(code_)); unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_WARNING | JSREPORT_STRICT); return JS_ReportErrorFlagsAndNumberASCII(cx, flags, GetErrorMessage, nullptr, code_); } JS_PUBLIC_API(bool) JS::ObjectOpResult::failCantRedefineProp() { return fail(JSMSG_CANT_REDEFINE_PROP); } JS_PUBLIC_API(bool) JS::ObjectOpResult::failReadOnly() { return fail(JSMSG_READ_ONLY); } JS_PUBLIC_API(bool) JS::ObjectOpResult::failGetterOnly() { return fail(JSMSG_GETTER_ONLY); } JS_PUBLIC_API(bool) JS::ObjectOpResult::failCantDelete() { return fail(JSMSG_CANT_DELETE); } JS_PUBLIC_API(bool) JS::ObjectOpResult::failCantSetInterposed() { return fail(JSMSG_CANT_SET_INTERPOSED); } JS_PUBLIC_API(bool) JS::ObjectOpResult::failCantDefineWindowElement() { return fail(JSMSG_CANT_DEFINE_WINDOW_ELEMENT); } JS_PUBLIC_API(bool) JS::ObjectOpResult::failCantDeleteWindowElement() { return fail(JSMSG_CANT_DELETE_WINDOW_ELEMENT); } JS_PUBLIC_API(bool) JS::ObjectOpResult::failCantDeleteWindowNamedProperty() { return fail(JSMSG_CANT_DELETE_WINDOW_NAMED_PROPERTY); } JS_PUBLIC_API(bool) JS::ObjectOpResult::failCantPreventExtensions() { return fail(JSMSG_CANT_PREVENT_EXTENSIONS); } JS_PUBLIC_API(bool) JS::ObjectOpResult::failCantSetProto() { return fail(JSMSG_CANT_SET_PROTO); } JS_PUBLIC_API(bool) JS::ObjectOpResult::failNoNamedSetter() { return fail(JSMSG_NO_NAMED_SETTER); } JS_PUBLIC_API(bool) JS::ObjectOpResult::failNoIndexedSetter() { return fail(JSMSG_NO_INDEXED_SETTER); } JS_PUBLIC_API(int64_t) JS_Now() { return PRMJ_Now(); } JS_PUBLIC_API(Value) JS_GetNaNValue(JSContext* cx) { return cx->runtime()->NaNValue; } JS_PUBLIC_API(Value) JS_GetNegativeInfinityValue(JSContext* cx) { return cx->runtime()->negativeInfinityValue; } JS_PUBLIC_API(Value) JS_GetPositiveInfinityValue(JSContext* cx) { return cx->runtime()->positiveInfinityValue; } JS_PUBLIC_API(Value) JS_GetEmptyStringValue(JSContext* cx) { return StringValue(cx->runtime()->emptyString); } JS_PUBLIC_API(JSString*) JS_GetEmptyString(JSContext* cx) { MOZ_ASSERT(cx->emptyString()); return cx->emptyString(); } namespace js { void AssertHeapIsIdle(JSRuntime* rt) { MOZ_ASSERT(!rt->isHeapBusy()); } } // namespace js static void AssertHeapIsIdleOrIterating(JSRuntime* rt) { MOZ_ASSERT(!rt->isHeapCollecting()); } static void AssertHeapIsIdleOrStringIsFlat(JSContext* cx, JSString* str) { /* * We allow some functions to be called during a GC as long as the argument * is a flat string, since that will not cause allocation. */ MOZ_ASSERT_IF(cx->runtime()->isHeapBusy(), str->isFlat()); } JS_PUBLIC_API(bool) JS_ValueToObject(JSContext* cx, HandleValue value, MutableHandleObject objp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, value); if (value.isNullOrUndefined()) { objp.set(nullptr); return true; } JSObject* obj = ToObject(cx, value); if (!obj) return false; objp.set(obj); return true; } JS_PUBLIC_API(JSFunction*) JS_ValueToFunction(JSContext* cx, HandleValue value) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, value); return ReportIfNotFunction(cx, value); } JS_PUBLIC_API(JSFunction*) JS_ValueToConstructor(JSContext* cx, HandleValue value) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, value); return ReportIfNotFunction(cx, value); } JS_PUBLIC_API(JSString*) JS_ValueToSource(JSContext* cx, HandleValue value) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, value); return ValueToSource(cx, value); } JS_PUBLIC_API(bool) JS_DoubleIsInt32(double d, int32_t* ip) { return mozilla::NumberIsInt32(d, ip); } JS_PUBLIC_API(JSType) JS_TypeOfValue(JSContext* cx, HandleValue value) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, value); return TypeOfValue(value); } JS_PUBLIC_API(bool) JS_StrictlyEqual(JSContext* cx, HandleValue value1, HandleValue value2, bool* equal) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, value1, value2); MOZ_ASSERT(equal); return StrictlyEqual(cx, value1, value2, equal); } JS_PUBLIC_API(bool) JS_LooselyEqual(JSContext* cx, HandleValue value1, HandleValue value2, bool* equal) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, value1, value2); MOZ_ASSERT(equal); return LooselyEqual(cx, value1, value2, equal); } JS_PUBLIC_API(bool) JS_SameValue(JSContext* cx, HandleValue value1, HandleValue value2, bool* same) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, value1, value2); MOZ_ASSERT(same); return SameValue(cx, value1, value2, same); } JS_PUBLIC_API(bool) JS_IsBuiltinEvalFunction(JSFunction* fun) { return IsAnyBuiltinEval(fun); } JS_PUBLIC_API(bool) JS_IsBuiltinFunctionConstructor(JSFunction* fun) { return fun->isBuiltinFunctionConstructor(); } JS_PUBLIC_API(bool) JS_IsFunctionBound(JSFunction* fun) { return fun->isBoundFunction(); } JS_PUBLIC_API(JSObject*) JS_GetBoundFunctionTarget(JSFunction* fun) { return fun->isBoundFunction() ? fun->getBoundFunctionTarget() : nullptr; } /************************************************************************/ #ifdef DEBUG JS_FRIEND_API(bool) JS::isGCEnabled() { return !TlsPerThreadData.get()->suppressGC; } #else JS_FRIEND_API(bool) JS::isGCEnabled() { return true; } #endif JS_PUBLIC_API(JSContext*) JS_NewContext(uint32_t maxbytes, uint32_t maxNurseryBytes, JSContext* parentContext) { MOZ_ASSERT(JS::detail::libraryInitState == JS::detail::InitState::Running, "must call JS_Init prior to creating any JSContexts"); // Make sure that all parent runtimes are the topmost parent. JSRuntime* parentRuntime = nullptr; if (parentContext) { parentRuntime = parentContext->runtime(); while (parentRuntime && parentRuntime->parentRuntime) parentRuntime = parentRuntime->parentRuntime; } return NewContext(maxbytes, maxNurseryBytes, parentRuntime); } JS_PUBLIC_API(void) JS_DestroyContext(JSContext* cx) { DestroyContext(cx); } static JS_CurrentEmbedderTimeFunction currentEmbedderTimeFunction; JS_PUBLIC_API(void) JS_SetCurrentEmbedderTimeFunction(JS_CurrentEmbedderTimeFunction timeFn) { currentEmbedderTimeFunction = timeFn; } JS_PUBLIC_API(double) JS_GetCurrentEmbedderTime() { if (currentEmbedderTimeFunction) return currentEmbedderTimeFunction(); return PRMJ_Now() / static_cast(PRMJ_USEC_PER_MSEC); } JS_PUBLIC_API(void*) JS_GetContextPrivate(JSContext* cx) { return cx->data; } JS_PUBLIC_API(void) JS_SetContextPrivate(JSContext* cx, void* data) { cx->data = data; } JS_PUBLIC_API(void) JS_SetFutexCanWait(JSContext* cx) { cx->fx.setCanWait(true); } static void StartRequest(JSContext* cx) { JSRuntime* rt = cx->runtime(); MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); if (rt->requestDepth) { rt->requestDepth++; } else { /* Indicate that a request is running. */ rt->requestDepth = 1; rt->triggerActivityCallback(true); } } static void StopRequest(JSContext* cx) { JSRuntime* rt = cx->runtime(); MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); MOZ_ASSERT(rt->requestDepth != 0); if (rt->requestDepth != 1) { rt->requestDepth--; } else { rt->requestDepth = 0; rt->triggerActivityCallback(false); } } JS_PUBLIC_API(void) JS_BeginRequest(JSContext* cx) { cx->outstandingRequests++; StartRequest(cx); } JS_PUBLIC_API(void) JS_EndRequest(JSContext* cx) { MOZ_ASSERT(cx->outstandingRequests != 0); cx->outstandingRequests--; StopRequest(cx); } JS_PUBLIC_API(JSContext*) JS_GetParentContext(JSContext* cx) { return cx->parentRuntime ? cx->parentRuntime->unsafeContextFromAnyThread() : cx; } JS_PUBLIC_API(JSVersion) JS_GetVersion(JSContext* cx) { return VersionNumber(cx->findVersion()); } JS_PUBLIC_API(void) JS_SetVersionForCompartment(JSCompartment* compartment, JSVersion version) { compartment->behaviors().setVersion(version); } static const struct v2smap { JSVersion version; const char* string; } v2smap[] = { {JSVERSION_ECMA_3, "ECMAv3"}, {JSVERSION_1_6, "1.6"}, {JSVERSION_1_7, "1.7"}, {JSVERSION_1_8, "1.8"}, {JSVERSION_ECMA_5, "ECMAv5"}, {JSVERSION_DEFAULT, js_default_str}, {JSVERSION_DEFAULT, "1.0"}, {JSVERSION_DEFAULT, "1.1"}, {JSVERSION_DEFAULT, "1.2"}, {JSVERSION_DEFAULT, "1.3"}, {JSVERSION_DEFAULT, "1.4"}, {JSVERSION_DEFAULT, "1.5"}, {JSVERSION_UNKNOWN, nullptr}, /* must be last, nullptr is sentinel */ }; JS_PUBLIC_API(const char*) JS_VersionToString(JSVersion version) { int i; for (i = 0; v2smap[i].string; i++) if (v2smap[i].version == version) return v2smap[i].string; return "unknown"; } JS_PUBLIC_API(JSVersion) JS_StringToVersion(const char* string) { int i; for (i = 0; v2smap[i].string; i++) if (strcmp(v2smap[i].string, string) == 0) return v2smap[i].version; return JSVERSION_UNKNOWN; } JS_PUBLIC_API(JS::ContextOptions&) JS::ContextOptionsRef(JSContext* cx) { return cx->options(); } JS_PUBLIC_API(bool) JS::InitSelfHostedCode(JSContext* cx) { MOZ_RELEASE_ASSERT(!cx->runtime()->hasInitializedSelfHosting(), "JS::InitSelfHostedCode() called more than once"); JSRuntime* rt = cx->runtime(); JSAutoRequest ar(cx); if (!rt->initializeAtoms(cx)) return false; if (!cx->cycleDetectorSet.init()) return false; if (!rt->initSelfHosting(cx)) return false; if (!rt->parentRuntime && !rt->transformToPermanentAtoms(cx)) return false; return true; } JS_PUBLIC_API(const char*) JS_GetImplementationVersion(void) { return "JavaScript-C" MOZILLA_VERSION; } JS_PUBLIC_API(void) JS_SetDestroyCompartmentCallback(JSContext* cx, JSDestroyCompartmentCallback callback) { cx->destroyCompartmentCallback = callback; } JS_PUBLIC_API(void) JS_SetSizeOfIncludingThisCompartmentCallback(JSContext* cx, JSSizeOfIncludingThisCompartmentCallback callback) { cx->sizeOfIncludingThisCompartmentCallback = callback; } JS_PUBLIC_API(void) JS_SetDestroyZoneCallback(JSContext* cx, JSZoneCallback callback) { cx->destroyZoneCallback = callback; } JS_PUBLIC_API(void) JS_SetSweepZoneCallback(JSContext* cx, JSZoneCallback callback) { cx->sweepZoneCallback = callback; } JS_PUBLIC_API(void) JS_SetCompartmentNameCallback(JSContext* cx, JSCompartmentNameCallback callback) { cx->compartmentNameCallback = callback; } JS_PUBLIC_API(void) JS_SetWrapObjectCallbacks(JSContext* cx, const JSWrapObjectCallbacks* callbacks) { cx->wrapObjectCallbacks = callbacks; } JS_PUBLIC_API(JSCompartment*) JS_EnterCompartment(JSContext* cx, JSObject* target) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); MOZ_ASSERT(!JS::ObjectIsMarkedGray(target)); JSCompartment* oldCompartment = cx->compartment(); cx->enterCompartment(target->compartment()); return oldCompartment; } JS_PUBLIC_API(void) JS_LeaveCompartment(JSContext* cx, JSCompartment* oldCompartment) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); cx->leaveCompartment(oldCompartment); } JSAutoCompartment::JSAutoCompartment(JSContext* cx, JSObject* target MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) : cx_(cx), oldCompartment_(cx->compartment()) { AssertHeapIsIdleOrIterating(cx_); MOZ_GUARD_OBJECT_NOTIFIER_INIT; MOZ_ASSERT(!JS::ObjectIsMarkedGray(target)); cx_->enterCompartment(target->compartment()); } JSAutoCompartment::JSAutoCompartment(JSContext* cx, JSScript* target MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) : cx_(cx), oldCompartment_(cx->compartment()) { AssertHeapIsIdleOrIterating(cx_); MOZ_GUARD_OBJECT_NOTIFIER_INIT; MOZ_ASSERT(!JS::ScriptIsMarkedGray(target)); cx_->enterCompartment(target->compartment()); } JSAutoCompartment::~JSAutoCompartment() { cx_->leaveCompartment(oldCompartment_); } JSAutoNullableCompartment::JSAutoNullableCompartment(JSContext* cx, JSObject* targetOrNull MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) : cx_(cx), oldCompartment_(cx->compartment()) { AssertHeapIsIdleOrIterating(cx_); MOZ_GUARD_OBJECT_NOTIFIER_INIT; if (targetOrNull) { MOZ_ASSERT(!JS::ObjectIsMarkedGray(targetOrNull)); cx_->enterCompartment(targetOrNull->compartment()); } else { cx_->enterNullCompartment(); } } JSAutoNullableCompartment::~JSAutoNullableCompartment() { cx_->leaveCompartment(oldCompartment_); } JS_PUBLIC_API(void) JS_SetCompartmentPrivate(JSCompartment* compartment, void* data) { compartment->data = data; } JS_PUBLIC_API(void*) JS_GetCompartmentPrivate(JSCompartment* compartment) { return compartment->data; } JS_PUBLIC_API(JSAddonId*) JS::NewAddonId(JSContext* cx, HandleString str) { return static_cast(JS_AtomizeAndPinJSString(cx, str)); } JS_PUBLIC_API(JSString*) JS::StringOfAddonId(JSAddonId* id) { return id; } JS_PUBLIC_API(JSAddonId*) JS::AddonIdOfObject(JSObject* obj) { return obj->compartment()->creationOptions().addonIdOrNull(); } JS_PUBLIC_API(void) JS_SetZoneUserData(JS::Zone* zone, void* data) { zone->data = data; } JS_PUBLIC_API(void*) JS_GetZoneUserData(JS::Zone* zone) { return zone->data; } JS_PUBLIC_API(bool) JS_WrapObject(JSContext* cx, MutableHandleObject objp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); if (objp) JS::ExposeObjectToActiveJS(objp); return cx->compartment()->wrap(cx, objp); } JS_PUBLIC_API(bool) JS_WrapValue(JSContext* cx, MutableHandleValue vp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); JS::ExposeValueToActiveJS(vp); return cx->compartment()->wrap(cx, vp); } /* * Identity remapping. Not for casual consumers. * * Normally, an object's contents and its identity are inextricably linked. * Identity is determined by the address of the JSObject* in the heap, and * the contents are what is located at that address. Transplanting allows these * concepts to be separated through a combination of swapping (exchanging the * contents of two same-compartment objects) and remapping cross-compartment * identities by altering wrappers. * * The |origobj| argument should be the object whose identity needs to be * remapped, usually to another compartment. The contents of |origobj| are * destroyed. * * The |target| argument serves two purposes: * * First, |target| serves as a hint for the new identity of the object. The new * identity object will always be in the same compartment as |target|, but * if that compartment already had an object representing |origobj| (either a * cross-compartment wrapper for it, or |origobj| itself if the two arguments * are same-compartment), the existing object is used. Otherwise, |target| * itself is used. To avoid ambiguity, JS_TransplantObject always returns the * new identity. * * Second, the new identity object's contents will be those of |target|. A swap() * is used to make this happen if an object other than |target| is used. * * We don't have a good way to recover from failure in this function, so * we intentionally crash instead. */ JS_PUBLIC_API(JSObject*) JS_TransplantObject(JSContext* cx, HandleObject origobj, HandleObject target) { AssertHeapIsIdle(cx); MOZ_ASSERT(origobj != target); MOZ_ASSERT(!origobj->is()); MOZ_ASSERT(!target->is()); RootedValue origv(cx, ObjectValue(*origobj)); RootedObject newIdentity(cx); // Don't allow a compacting GC to observe any intermediate state. AutoDisableCompactingGC nocgc(cx); AutoDisableProxyCheck adpc(cx->runtime()); JSCompartment* destination = target->compartment(); if (origobj->compartment() == destination) { // If the original object is in the same compartment as the // destination, then we know that we won't find a wrapper in the // destination's cross compartment map and that the same // object will continue to work. if (!JSObject::swap(cx, origobj, target)) MOZ_CRASH(); newIdentity = origobj; } else if (WrapperMap::Ptr p = destination->lookupWrapper(origv)) { // There might already be a wrapper for the original object in // the new compartment. If there is, we use its identity and swap // in the contents of |target|. newIdentity = &p->value().get().toObject(); // When we remove origv from the wrapper map, its wrapper, newIdentity, // must immediately cease to be a cross-compartment wrapper. Nuke it. destination->removeWrapper(p); NukeCrossCompartmentWrapper(cx, newIdentity); if (!JSObject::swap(cx, newIdentity, target)) MOZ_CRASH(); } else { // Otherwise, we use |target| for the new identity object. newIdentity = target; } // Now, iterate through other scopes looking for references to the // old object, and update the relevant cross-compartment wrappers. if (!RemapAllWrappersForObject(cx, origobj, newIdentity)) MOZ_CRASH(); // Lastly, update the original object to point to the new one. if (origobj->compartment() != destination) { RootedObject newIdentityWrapper(cx, newIdentity); AutoCompartment ac(cx, origobj); if (!JS_WrapObject(cx, &newIdentityWrapper)) MOZ_CRASH(); MOZ_ASSERT(Wrapper::wrappedObject(newIdentityWrapper) == newIdentity); if (!JSObject::swap(cx, origobj, newIdentityWrapper)) MOZ_CRASH(); if (!origobj->compartment()->putWrapper(cx, CrossCompartmentKey(newIdentity), origv)) MOZ_CRASH(); } // The new identity object might be one of several things. Return it to avoid // ambiguity. return newIdentity; } /* * Recompute all cross-compartment wrappers for an object, resetting state. * Gecko uses this to clear Xray wrappers when doing a navigation that reuses * the inner window and global object. */ JS_PUBLIC_API(bool) JS_RefreshCrossCompartmentWrappers(JSContext* cx, HandleObject obj) { return RemapAllWrappersForObject(cx, obj, obj); } JS_PUBLIC_API(bool) JS_InitStandardClasses(JSContext* cx, HandleObject obj) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); Rooted global(cx, &obj->global()); return GlobalObject::initStandardClasses(cx, global); } #define EAGER_ATOM(name) NAME_OFFSET(name) typedef struct JSStdName { size_t atomOffset; /* offset of atom pointer in JSAtomState */ JSProtoKey key; bool isDummy() const { return key == JSProto_Null; } bool isSentinel() const { return key == JSProto_LIMIT; } } JSStdName; static const JSStdName* LookupStdName(const JSAtomState& names, JSAtom* name, const JSStdName* table) { for (unsigned i = 0; !table[i].isSentinel(); i++) { if (table[i].isDummy()) continue; JSAtom* atom = AtomStateOffsetToName(names, table[i].atomOffset); MOZ_ASSERT(atom); if (name == atom) return &table[i]; } return nullptr; } /* * Table of standard classes, indexed by JSProtoKey. For entries where the * JSProtoKey does not correspond to a class with a meaningful constructor, we * insert a null entry into the table. */ #define STD_NAME_ENTRY(name, code, init, clasp) { EAGER_ATOM(name), static_cast(code) }, #define STD_DUMMY_ENTRY(name, code, init, dummy) { 0, JSProto_Null }, static const JSStdName standard_class_names[] = { JS_FOR_PROTOTYPES(STD_NAME_ENTRY, STD_DUMMY_ENTRY) { 0, JSProto_LIMIT } }; /* * Table of top-level function and constant names and the JSProtoKey of the * standard class that initializes them. */ static const JSStdName builtin_property_names[] = { { EAGER_ATOM(eval), JSProto_Object }, /* Global properties and functions defined by the Number class. */ { EAGER_ATOM(NaN), JSProto_Number }, { EAGER_ATOM(Infinity), JSProto_Number }, { EAGER_ATOM(isNaN), JSProto_Number }, { EAGER_ATOM(isFinite), JSProto_Number }, { EAGER_ATOM(parseFloat), JSProto_Number }, { EAGER_ATOM(parseInt), JSProto_Number }, /* String global functions. */ { EAGER_ATOM(escape), JSProto_String }, { EAGER_ATOM(unescape), JSProto_String }, { EAGER_ATOM(decodeURI), JSProto_String }, { EAGER_ATOM(encodeURI), JSProto_String }, { EAGER_ATOM(decodeURIComponent), JSProto_String }, { EAGER_ATOM(encodeURIComponent), JSProto_String }, #if JS_HAS_UNEVAL { EAGER_ATOM(uneval), JSProto_String }, #endif { 0, JSProto_LIMIT } }; #undef EAGER_ATOM JS_PUBLIC_API(bool) JS_ResolveStandardClass(JSContext* cx, HandleObject obj, HandleId id, bool* resolved) { const JSStdName* stdnm; AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id); Handle global = obj.as(); *resolved = false; if (!JSID_IS_ATOM(id)) return true; /* Check whether we're resolving 'undefined', and define it if so. */ JSAtom* idAtom = JSID_TO_ATOM(id); JSAtom* undefinedAtom = cx->names().undefined; if (idAtom == undefinedAtom) { *resolved = true; return DefineProperty(cx, global, id, UndefinedHandleValue, nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING); } /* Try for class constructors/prototypes named by well-known atoms. */ stdnm = LookupStdName(cx->names(), idAtom, standard_class_names); /* Try less frequently used top-level functions and constants. */ if (!stdnm) stdnm = LookupStdName(cx->names(), idAtom, builtin_property_names); if (stdnm && GlobalObject::skipDeselectedConstructor(cx, stdnm->key)) stdnm = nullptr; // If this class is anonymous, then it doesn't exist as a global // property, so we won't resolve anything. JSProtoKey key = stdnm ? stdnm->key : JSProto_Null; if (key != JSProto_Null) { const Class* clasp = ProtoKeyToClass(key); if (!clasp || !(clasp->flags & JSCLASS_IS_ANONYMOUS)) { if (!GlobalObject::ensureConstructor(cx, global, key)) return false; *resolved = true; return true; } } // There is no such property to resolve. An ordinary resolve hook would // just return true at this point. But the global object is special in one // more way: its prototype chain is lazily initialized. That is, // global->getProto() might be null right now because we haven't created // Object.prototype yet. Force it now. return GlobalObject::getOrCreateObjectPrototype(cx, global); } JS_PUBLIC_API(bool) JS_MayResolveStandardClass(const JSAtomState& names, jsid id, JSObject* maybeObj) { MOZ_ASSERT_IF(maybeObj, maybeObj->is()); // The global object's resolve hook is special: JS_ResolveStandardClass // initializes the prototype chain lazily. Only attempt to optimize here // if we know the prototype chain has been initialized. if (!maybeObj || !maybeObj->staticPrototype()) return true; if (!JSID_IS_ATOM(id)) return false; JSAtom* atom = JSID_TO_ATOM(id); // This will return true even for deselected constructors. (To do // better, we need a JSContext here; it's fine as it is.) return atom == names.undefined || LookupStdName(names, atom, standard_class_names) || LookupStdName(names, atom, builtin_property_names); } JS_PUBLIC_API(bool) JS_EnumerateStandardClasses(JSContext* cx, HandleObject obj) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); Handle global = obj.as(); return GlobalObject::initStandardClasses(cx, global); } JS_PUBLIC_API(bool) JS_GetClassObject(JSContext* cx, JSProtoKey key, MutableHandleObject objp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return GetBuiltinConstructor(cx, key, objp); } JS_PUBLIC_API(bool) JS_GetClassPrototype(JSContext* cx, JSProtoKey key, MutableHandleObject objp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return GetBuiltinPrototype(cx, key, objp); } namespace JS { JS_PUBLIC_API(void) ProtoKeyToId(JSContext* cx, JSProtoKey key, MutableHandleId idp) { idp.set(NameToId(ClassName(key, cx))); } } /* namespace JS */ JS_PUBLIC_API(JSProtoKey) JS_IdToProtoKey(JSContext* cx, HandleId id) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); if (!JSID_IS_ATOM(id)) return JSProto_Null; JSAtom* atom = JSID_TO_ATOM(id); const JSStdName* stdnm = LookupStdName(cx->names(), atom, standard_class_names); if (!stdnm) return JSProto_Null; if (GlobalObject::skipDeselectedConstructor(cx, stdnm->key)) return JSProto_Null; MOZ_ASSERT(MOZ_ARRAY_LENGTH(standard_class_names) == JSProto_LIMIT + 1); return static_cast(stdnm - standard_class_names); } JS_PUBLIC_API(JSObject*) JS_GetObjectPrototype(JSContext* cx, HandleObject forObj) { CHECK_REQUEST(cx); assertSameCompartment(cx, forObj); Rooted global(cx, &forObj->global()); return GlobalObject::getOrCreateObjectPrototype(cx, global); } JS_PUBLIC_API(JSObject*) JS_GetFunctionPrototype(JSContext* cx, HandleObject forObj) { CHECK_REQUEST(cx); assertSameCompartment(cx, forObj); Rooted global(cx, &forObj->global()); return GlobalObject::getOrCreateFunctionPrototype(cx, global); } JS_PUBLIC_API(JSObject*) JS_GetArrayPrototype(JSContext* cx, HandleObject forObj) { CHECK_REQUEST(cx); assertSameCompartment(cx, forObj); Rooted global(cx, &forObj->global()); return GlobalObject::getOrCreateArrayPrototype(cx, global); } JS_PUBLIC_API(JSObject*) JS_GetErrorPrototype(JSContext* cx) { CHECK_REQUEST(cx); Rooted global(cx, cx->global()); return GlobalObject::getOrCreateCustomErrorPrototype(cx, global, JSEXN_ERR); } JS_PUBLIC_API(JSObject*) JS_GetIteratorPrototype(JSContext* cx) { CHECK_REQUEST(cx); Rooted global(cx, cx->global()); return GlobalObject::getOrCreateIteratorPrototype(cx, global); } JS_PUBLIC_API(JSObject*) JS_GetGlobalForObject(JSContext* cx, JSObject* obj) { AssertHeapIsIdle(cx); assertSameCompartment(cx, obj); return &obj->global(); } extern JS_PUBLIC_API(bool) JS_IsGlobalObject(JSObject* obj) { return obj->is(); } extern JS_PUBLIC_API(JSObject*) JS_GlobalLexicalEnvironment(JSObject* obj) { return &obj->as().lexicalEnvironment(); } extern JS_PUBLIC_API(bool) JS_HasExtensibleLexicalEnvironment(JSObject* obj) { return obj->is() || obj->compartment()->getNonSyntacticLexicalEnvironment(obj); } extern JS_PUBLIC_API(JSObject*) JS_ExtensibleLexicalEnvironment(JSObject* obj) { JSObject* lexical = nullptr; if (obj->is()) lexical = JS_GlobalLexicalEnvironment(obj); else lexical = obj->compartment()->getNonSyntacticLexicalEnvironment(obj); MOZ_ASSERT(lexical); return lexical; } JS_PUBLIC_API(JSObject*) JS_GetGlobalForCompartmentOrNull(JSContext* cx, JSCompartment* c) { AssertHeapIsIdleOrIterating(cx); assertSameCompartment(cx, c); return c->maybeGlobal(); } JS_PUBLIC_API(JSObject*) JS::CurrentGlobalOrNull(JSContext* cx) { AssertHeapIsIdleOrIterating(cx); CHECK_REQUEST(cx); if (!cx->compartment()) return nullptr; return cx->global(); } JS_PUBLIC_API(Value) JS::detail::ComputeThis(JSContext* cx, Value* vp) { AssertHeapIsIdle(cx); assertSameCompartment(cx, JSValueArray(vp, 2)); MutableHandleValue thisv = MutableHandleValue::fromMarkedLocation(&vp[1]); if (!BoxNonStrictThis(cx, thisv, thisv)) return NullValue(); return thisv; } JS_PUBLIC_API(void*) JS_malloc(JSContext* cx, size_t nbytes) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return static_cast(cx->runtime()->pod_malloc(nbytes)); } JS_PUBLIC_API(void*) JS_realloc(JSContext* cx, void* p, size_t oldBytes, size_t newBytes) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return static_cast(cx->zone()->pod_realloc(static_cast(p), oldBytes, newBytes)); } JS_PUBLIC_API(void) JS_free(JSContext* cx, void* p) { return js_free(p); } JS_PUBLIC_API(void) JS_freeop(JSFreeOp* fop, void* p) { return FreeOp::get(fop)->free_(p); } JS_PUBLIC_API(void) JS_updateMallocCounter(JSContext* cx, size_t nbytes) { return cx->updateMallocCounter(nbytes); } JS_PUBLIC_API(char*) JS_strdup(JSContext* cx, const char* s) { AssertHeapIsIdle(cx); return DuplicateString(cx, s).release(); } #undef JS_AddRoot JS_PUBLIC_API(bool) JS_AddExtraGCRootsTracer(JSContext* cx, JSTraceDataOp traceOp, void* data) { return cx->gc.addBlackRootsTracer(traceOp, data); } JS_PUBLIC_API(void) JS_RemoveExtraGCRootsTracer(JSContext* cx, JSTraceDataOp traceOp, void* data) { return cx->gc.removeBlackRootsTracer(traceOp, data); } JS_PUBLIC_API(void) JS_GC(JSContext* cx) { AssertHeapIsIdle(cx); JS::PrepareForFullGC(cx); cx->gc.gc(GC_NORMAL, JS::gcreason::API); } JS_PUBLIC_API(void) JS_MaybeGC(JSContext* cx) { GCRuntime& gc = cx->runtime()->gc; gc.maybeGC(cx->zone()); } JS_PUBLIC_API(void) JS_SetGCCallback(JSContext* cx, JSGCCallback cb, void* data) { AssertHeapIsIdle(cx); cx->gc.setGCCallback(cb, data); } JS_PUBLIC_API(void) JS_SetObjectsTenuredCallback(JSContext* cx, JSObjectsTenuredCallback cb, void* data) { AssertHeapIsIdle(cx); cx->gc.setObjectsTenuredCallback(cb, data); } JS_PUBLIC_API(bool) JS_AddFinalizeCallback(JSContext* cx, JSFinalizeCallback cb, void* data) { AssertHeapIsIdle(cx); return cx->gc.addFinalizeCallback(cb, data); } JS_PUBLIC_API(void) JS_RemoveFinalizeCallback(JSContext* cx, JSFinalizeCallback cb) { cx->gc.removeFinalizeCallback(cb); } JS_PUBLIC_API(bool) JS_AddWeakPointerZoneGroupCallback(JSContext* cx, JSWeakPointerZoneGroupCallback cb, void* data) { AssertHeapIsIdle(cx); return cx->gc.addWeakPointerZoneGroupCallback(cb, data); } JS_PUBLIC_API(void) JS_RemoveWeakPointerZoneGroupCallback(JSContext* cx, JSWeakPointerZoneGroupCallback cb) { cx->gc.removeWeakPointerZoneGroupCallback(cb); } JS_PUBLIC_API(bool) JS_AddWeakPointerCompartmentCallback(JSContext* cx, JSWeakPointerCompartmentCallback cb, void* data) { AssertHeapIsIdle(cx); return cx->gc.addWeakPointerCompartmentCallback(cb, data); } JS_PUBLIC_API(void) JS_RemoveWeakPointerCompartmentCallback(JSContext* cx, JSWeakPointerCompartmentCallback cb) { cx->gc.removeWeakPointerCompartmentCallback(cb); } JS_PUBLIC_API(void) JS_UpdateWeakPointerAfterGC(JS::Heap* objp) { JS_UpdateWeakPointerAfterGCUnbarriered(objp->unsafeGet()); } JS_PUBLIC_API(void) JS_UpdateWeakPointerAfterGCUnbarriered(JSObject** objp) { if (IsAboutToBeFinalizedUnbarriered(objp)) *objp = nullptr; } JS_PUBLIC_API(void) JS_SetGCParameter(JSContext* cx, JSGCParamKey key, uint32_t value) { cx->gc.waitBackgroundSweepEnd(); AutoLockGC lock(cx); MOZ_ALWAYS_TRUE(cx->gc.setParameter(key, value, lock)); } JS_PUBLIC_API(void) JS_SetGGCMode(JSContext* cx, bool enabled) { // Control GGC if (enabled && !cx->gc.isGenerationalGCEnabled()) { cx->gc.enableGenerationalGC(); } else if (!enabled && cx->gc.isGenerationalGCEnabled()) { cx->gc.disableGenerationalGC(); } } JS_PUBLIC_API(uint32_t) JS_GetGCParameter(JSContext* cx, JSGCParamKey key) { AutoLockGC lock(cx); return cx->gc.getParameter(key, lock); } static const size_t NumGCConfigs = 14; struct JSGCConfig { JSGCParamKey key; uint32_t value; }; JS_PUBLIC_API(void) JS_SetGCParametersBasedOnAvailableMemory(JSContext* cx, uint32_t availMem) { static const JSGCConfig minimal[NumGCConfigs] = { {JSGC_MAX_MALLOC_BYTES, 6 * 1024 * 1024}, {JSGC_SLICE_TIME_BUDGET, 30}, {JSGC_HIGH_FREQUENCY_TIME_LIMIT, 1500}, {JSGC_HIGH_FREQUENCY_HIGH_LIMIT, 40}, {JSGC_HIGH_FREQUENCY_LOW_LIMIT, 0}, {JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX, 300}, {JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN, 120}, {JSGC_LOW_FREQUENCY_HEAP_GROWTH, 120}, {JSGC_HIGH_FREQUENCY_TIME_LIMIT, 1500}, {JSGC_HIGH_FREQUENCY_TIME_LIMIT, 1500}, {JSGC_HIGH_FREQUENCY_TIME_LIMIT, 1500}, {JSGC_ALLOCATION_THRESHOLD, 1}, {JSGC_MODE, JSGC_MODE_INCREMENTAL} }; const JSGCConfig* config = minimal; if (availMem > 512) { static const JSGCConfig nominal[NumGCConfigs] = { {JSGC_MAX_MALLOC_BYTES, 6 * 1024 * 1024}, {JSGC_SLICE_TIME_BUDGET, 30}, {JSGC_HIGH_FREQUENCY_TIME_LIMIT, 1000}, {JSGC_HIGH_FREQUENCY_HIGH_LIMIT, 500}, {JSGC_HIGH_FREQUENCY_LOW_LIMIT, 100}, {JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX, 300}, {JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN, 150}, {JSGC_LOW_FREQUENCY_HEAP_GROWTH, 150}, {JSGC_HIGH_FREQUENCY_TIME_LIMIT, 1500}, {JSGC_HIGH_FREQUENCY_TIME_LIMIT, 1500}, {JSGC_HIGH_FREQUENCY_TIME_LIMIT, 1500}, {JSGC_ALLOCATION_THRESHOLD, 30}, {JSGC_MODE, JSGC_MODE_ZONE} }; config = nominal; } for (size_t i = 0; i < NumGCConfigs; i++) JS_SetGCParameter(cx, config[i].key, config[i].value); } JS_PUBLIC_API(JSString*) JS_NewExternalString(JSContext* cx, const char16_t* chars, size_t length, const JSStringFinalizer* fin) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); JSString* s = JSExternalString::new_(cx, chars, length, fin); return s; } extern JS_PUBLIC_API(bool) JS_IsExternalString(JSString* str) { return str->isExternal(); } extern JS_PUBLIC_API(const JSStringFinalizer*) JS_GetExternalStringFinalizer(JSString* str) { return str->asExternal().externalFinalizer(); } static void SetNativeStackQuotaAndLimit(JSContext* cx, StackKind kind, size_t stackSize) { cx->nativeStackQuota[kind] = stackSize; #if JS_STACK_GROWTH_DIRECTION > 0 if (stackSize == 0) { cx->nativeStackLimit[kind] = UINTPTR_MAX; } else { MOZ_ASSERT(cx->nativeStackBase <= size_t(-1) - stackSize); cx->nativeStackLimit[kind] = cx->nativeStackBase + stackSize - 1; } #else if (stackSize == 0) { cx->nativeStackLimit[kind] = 0; } else { MOZ_ASSERT(cx->nativeStackBase >= stackSize); cx->nativeStackLimit[kind] = cx->nativeStackBase - (stackSize - 1); } #endif } JS_PUBLIC_API(void) JS_SetNativeStackQuota(JSContext* cx, size_t systemCodeStackSize, size_t trustedScriptStackSize, size_t untrustedScriptStackSize) { MOZ_ASSERT(cx->requestDepth == 0); if (!trustedScriptStackSize) trustedScriptStackSize = systemCodeStackSize; else MOZ_ASSERT(trustedScriptStackSize < systemCodeStackSize); if (!untrustedScriptStackSize) untrustedScriptStackSize = trustedScriptStackSize; else MOZ_ASSERT(untrustedScriptStackSize < trustedScriptStackSize); SetNativeStackQuotaAndLimit(cx, StackForSystemCode, systemCodeStackSize); SetNativeStackQuotaAndLimit(cx, StackForTrustedScript, trustedScriptStackSize); SetNativeStackQuotaAndLimit(cx, StackForUntrustedScript, untrustedScriptStackSize); cx->initJitStackLimit(); } /************************************************************************/ JS_PUBLIC_API(bool) JS_ValueToId(JSContext* cx, HandleValue value, MutableHandleId idp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, value); return ValueToId(cx, value, idp); } JS_PUBLIC_API(bool) JS_StringToId(JSContext* cx, HandleString string, MutableHandleId idp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, string); RootedValue value(cx, StringValue(string)); return ValueToId(cx, value, idp); } JS_PUBLIC_API(bool) JS_IdToValue(JSContext* cx, jsid id, MutableHandleValue vp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); vp.set(IdToValue(id)); assertSameCompartment(cx, vp); return true; } JS_PUBLIC_API(bool) JS::ToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); MOZ_ASSERT(obj != nullptr); MOZ_ASSERT(hint == JSTYPE_VOID || hint == JSTYPE_STRING || hint == JSTYPE_NUMBER); vp.setObject(*obj); return ToPrimitiveSlow(cx, hint, vp); } JS_PUBLIC_API(bool) JS::GetFirstArgumentAsTypeHint(JSContext* cx, CallArgs args, JSType *result) { if (!args.get(0).isString()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, "Symbol.toPrimitive", "\"string\", \"number\", or \"default\"", InformalValueTypeName(args.get(0))); return false; } RootedString str(cx, args.get(0).toString()); bool match; if (!EqualStrings(cx, str, cx->names().default_, &match)) return false; if (match) { *result = JSTYPE_VOID; return true; } if (!EqualStrings(cx, str, cx->names().string, &match)) return false; if (match) { *result = JSTYPE_STRING; return true; } if (!EqualStrings(cx, str, cx->names().number, &match)) return false; if (match) { *result = JSTYPE_NUMBER; return true; } JSAutoByteString bytes; const char* source = ValueToSourceForError(cx, args.get(0), bytes); if (!source) { ReportOutOfMemory(cx); return false; } JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, "Symbol.toPrimitive", "\"string\", \"number\", or \"default\"", source); return false; } JS_PUBLIC_API(bool) JS_PropertyStub(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp) { return true; } JS_PUBLIC_API(bool) JS_StrictPropertyStub(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp, ObjectOpResult& result) { return result.succeed(); } JS_PUBLIC_API(JSObject*) JS_InitClass(JSContext* cx, HandleObject obj, HandleObject parent_proto, const JSClass* clasp, JSNative constructor, unsigned nargs, const JSPropertySpec* ps, const JSFunctionSpec* fs, const JSPropertySpec* static_ps, const JSFunctionSpec* static_fs) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, parent_proto); return InitClass(cx, obj, parent_proto, Valueify(clasp), constructor, nargs, ps, fs, static_ps, static_fs); } JS_PUBLIC_API(bool) JS_LinkConstructorAndPrototype(JSContext* cx, HandleObject ctor, HandleObject proto) { return LinkConstructorAndPrototype(cx, ctor, proto); } JS_PUBLIC_API(const JSClass*) JS_GetClass(JSObject* obj) { return obj->getJSClass(); } JS_PUBLIC_API(bool) JS_InstanceOf(JSContext* cx, HandleObject obj, const JSClass* clasp, CallArgs* args) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); #ifdef DEBUG if (args) { assertSameCompartment(cx, obj); assertSameCompartment(cx, args->thisv(), args->calleev()); } #endif if (!obj || obj->getJSClass() != clasp) { if (args) ReportIncompatibleMethod(cx, *args, Valueify(clasp)); return false; } return true; } JS_PUBLIC_API(bool) JS_HasInstance(JSContext* cx, HandleObject obj, HandleValue value, bool* bp) { AssertHeapIsIdle(cx); assertSameCompartment(cx, obj, value); return HasInstance(cx, obj, value, bp); } JS_PUBLIC_API(void*) JS_GetPrivate(JSObject* obj) { /* This function can be called by a finalizer. */ return obj->as().getPrivate(); } JS_PUBLIC_API(void) JS_SetPrivate(JSObject* obj, void* data) { /* This function can be called by a finalizer. */ obj->as().setPrivate(data); } JS_PUBLIC_API(void*) JS_GetInstancePrivate(JSContext* cx, HandleObject obj, const JSClass* clasp, CallArgs* args) { if (!JS_InstanceOf(cx, obj, clasp, args)) return nullptr; return obj->as().getPrivate(); } JS_PUBLIC_API(JSObject*) JS_GetConstructor(JSContext* cx, HandleObject proto) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, proto); RootedValue cval(cx); if (!GetProperty(cx, proto, proto, cx->names().constructor, &cval)) return nullptr; if (!IsFunctionObject(cval)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR, proto->getClass()->name); return nullptr; } return &cval.toObject(); } bool JS::CompartmentBehaviors::extraWarnings(JSContext* cx) const { return extraWarningsOverride_.get(cx->options().extraWarnings()); } JS::CompartmentCreationOptions& JS::CompartmentCreationOptions::setZone(ZoneSpecifier spec) { zone_.spec = spec; return *this; } JS::CompartmentCreationOptions& JS::CompartmentCreationOptions::setSameZoneAs(JSObject* obj) { zone_.pointer = static_cast(obj->zone()); return *this; } const JS::CompartmentCreationOptions& JS::CompartmentCreationOptionsRef(JSCompartment* compartment) { return compartment->creationOptions(); } const JS::CompartmentCreationOptions& JS::CompartmentCreationOptionsRef(JSObject* obj) { return obj->compartment()->creationOptions(); } const JS::CompartmentCreationOptions& JS::CompartmentCreationOptionsRef(JSContext* cx) { return cx->compartment()->creationOptions(); } bool JS::CompartmentCreationOptions::getSharedMemoryAndAtomicsEnabled() const { #if defined(ENABLE_SHARED_ARRAY_BUFFER) return sharedMemoryAndAtomics_; #else return false; #endif } JS::CompartmentCreationOptions& JS::CompartmentCreationOptions::setSharedMemoryAndAtomicsEnabled(bool flag) { #if defined(ENABLE_SHARED_ARRAY_BUFFER) sharedMemoryAndAtomics_ = flag; #endif return *this; } JS::CompartmentBehaviors& JS::CompartmentBehaviorsRef(JSCompartment* compartment) { return compartment->behaviors(); } JS::CompartmentBehaviors& JS::CompartmentBehaviorsRef(JSObject* obj) { return obj->compartment()->behaviors(); } JS::CompartmentBehaviors& JS::CompartmentBehaviorsRef(JSContext* cx) { return cx->compartment()->behaviors(); } JS_PUBLIC_API(JSObject*) JS_NewGlobalObject(JSContext* cx, const JSClass* clasp, JSPrincipals* principals, JS::OnNewGlobalHookOption hookOption, const JS::CompartmentOptions& options) { MOZ_RELEASE_ASSERT(cx->runtime()->hasInitializedSelfHosting(), "Must call JS::InitSelfHostedCode() before creating a global"); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return GlobalObject::new_(cx, Valueify(clasp), principals, hookOption, options); } JS_PUBLIC_API(void) JS_GlobalObjectTraceHook(JSTracer* trc, JSObject* global) { MOZ_ASSERT(global->is()); // Off thread parsing and compilation tasks create a dummy global which is // then merged back into the host compartment. Since it used to be a // global, it will still have this trace hook, but it does not have a // meaning relative to its new compartment. We can safely skip it. // // Similarly, if we GC when creating the global, we may not have set that // global's compartment's global pointer yet. In this case, the compartment // will not yet contain anything that needs to be traced. if (!global->isOwnGlobal(trc)) return; // Trace the compartment for any GC things that should only stick around if // we know the compartment is live. global->compartment()->trace(trc); if (JSTraceOp trace = global->compartment()->creationOptions().getTrace()) trace(trc, global); } JS_PUBLIC_API(void) JS_FireOnNewGlobalObject(JSContext* cx, JS::HandleObject global) { // This hook is infallible, because we don't really want arbitrary script // to be able to throw errors during delicate global creation routines. // This infallibility will eat OOM and slow script, but if that happens // we'll likely run up into them again soon in a fallible context. Rooted globalObject(cx, &global->as()); Debugger::onNewGlobalObject(cx, globalObject); } JS_PUBLIC_API(JSObject*) JS_NewObject(JSContext* cx, const JSClass* jsclasp) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); const Class* clasp = Valueify(jsclasp); if (!clasp) clasp = &PlainObject::class_; /* default class is Object */ MOZ_ASSERT(clasp != &JSFunction::class_); MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL)); return NewObjectWithClassProto(cx, clasp, nullptr); } JS_PUBLIC_API(JSObject*) JS_NewObjectWithGivenProto(JSContext* cx, const JSClass* jsclasp, HandleObject proto) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, proto); const Class* clasp = Valueify(jsclasp); if (!clasp) clasp = &PlainObject::class_; /* default class is Object */ MOZ_ASSERT(clasp != &JSFunction::class_); MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL)); return NewObjectWithGivenProto(cx, clasp, proto); } JS_PUBLIC_API(JSObject*) JS_NewPlainObject(JSContext* cx) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return NewBuiltinClassInstance(cx); } JS_PUBLIC_API(JSObject*) JS_NewObjectForConstructor(JSContext* cx, const JSClass* clasp, const CallArgs& args) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); Value callee = args.calleev(); assertSameCompartment(cx, callee); RootedObject obj(cx, &callee.toObject()); return CreateThis(cx, Valueify(clasp), obj); } JS_PUBLIC_API(bool) JS_IsNative(JSObject* obj) { return obj->isNative(); } JS_PUBLIC_API(void) JS::AssertObjectBelongsToCurrentThread(JSObject* obj) { JSRuntime* rt = obj->compartment()->runtimeFromAnyThread(); MOZ_RELEASE_ASSERT(CurrentThreadCanAccessRuntime(rt)); } /*** Standard internal methods *******************************************************************/ JS_PUBLIC_API(bool) JS_GetPrototype(JSContext* cx, HandleObject obj, MutableHandleObject result) { return GetPrototype(cx, obj, result); } JS_PUBLIC_API(bool) JS_SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, proto); return SetPrototype(cx, obj, proto); } JS_PUBLIC_API(bool) JS_GetPrototypeIfOrdinary(JSContext* cx, HandleObject obj, bool* isOrdinary, MutableHandleObject result) { return GetPrototypeIfOrdinary(cx, obj, isOrdinary, result); } JS_PUBLIC_API(bool) JS_IsExtensible(JSContext* cx, HandleObject obj, bool* extensible) { return IsExtensible(cx, obj, extensible); } JS_PUBLIC_API(bool) JS_PreventExtensions(JSContext* cx, JS::HandleObject obj, ObjectOpResult& result) { return PreventExtensions(cx, obj, result); } JS_PUBLIC_API(bool) JS_SetImmutablePrototype(JSContext *cx, JS::HandleObject obj, bool *succeeded) { return SetImmutablePrototype(cx, obj, succeeded); } JS_PUBLIC_API(bool) JS_GetOwnPropertyDescriptorById(JSContext* cx, HandleObject obj, HandleId id, MutableHandle desc) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return GetOwnPropertyDescriptor(cx, obj, id, desc); } JS_PUBLIC_API(bool) JS_GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, const char* name, MutableHandle desc) { JSAtom* atom = Atomize(cx, name, strlen(name)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); return JS_GetOwnPropertyDescriptorById(cx, obj, id, desc); } JS_PUBLIC_API(bool) JS_GetOwnUCPropertyDescriptor(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, MutableHandle desc) { JSAtom* atom = AtomizeChars(cx, name, namelen); if (!atom) return false; RootedId id(cx, AtomToId(atom)); return JS_GetOwnPropertyDescriptorById(cx, obj, id, desc); } JS_PUBLIC_API(bool) JS_GetPropertyDescriptorById(JSContext* cx, HandleObject obj, HandleId id, MutableHandle desc) { return GetPropertyDescriptor(cx, obj, id, desc); } JS_PUBLIC_API(bool) JS_GetPropertyDescriptor(JSContext* cx, HandleObject obj, const char* name, MutableHandle desc) { JSAtom* atom = Atomize(cx, name, strlen(name)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); return JS_GetPropertyDescriptorById(cx, obj, id, desc); } JS_PUBLIC_API(bool) JS_GetUCPropertyDescriptor(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, MutableHandle desc) { JSAtom* atom = AtomizeChars(cx, name, namelen); if (!atom) { return false; } RootedId id(cx, AtomToId(atom)); return JS_GetPropertyDescriptorById(cx, obj, id, desc); } static bool DefinePropertyByDescriptor(JSContext* cx, HandleObject obj, HandleId id, Handle desc, ObjectOpResult& result) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id, desc); return DefineProperty(cx, obj, id, desc.value(), desc.getter(), desc.setter(), desc.attributes(), result); } JS_PUBLIC_API(bool) JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id, Handle desc, ObjectOpResult& result) { return DefinePropertyByDescriptor(cx, obj, id, desc, result); } JS_PUBLIC_API(bool) JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id, Handle desc) { ObjectOpResult result; return DefinePropertyByDescriptor(cx, obj, id, desc, result) && result.checkStrict(cx, obj, id); } static bool DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleValue value, const JSNativeWrapper& get, const JSNativeWrapper& set, unsigned attrs, unsigned flags) { JSGetterOp getter = JS_CAST_NATIVE_TO(get.op, JSGetterOp); JSSetterOp setter = JS_CAST_NATIVE_TO(set.op, JSSetterOp); // JSPROP_READONLY has no meaning when accessors are involved. Ideally we'd // throw if this happens, but we've accepted it for long enough that it's // not worth trying to make callers change their ways. Just flip it off on // its way through the API layer so that we can enforce this internally. if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) attrs &= ~JSPROP_READONLY; // When we use DefineProperty, we need full scriptable Function objects rather // than JSNatives. However, we might be pulling this property descriptor off // of something with JSNative property descriptors. If we are, wrap them in // JS Function objects. // // But skip doing this if our accessors are the well-known stub // accessors, since those are known to be JSGetterOps. Assert // some sanity about it, though. MOZ_ASSERT_IF(getter == JS_PropertyStub, setter == JS_StrictPropertyStub || (attrs & JSPROP_PROPOP_ACCESSORS)); MOZ_ASSERT_IF(setter == JS_StrictPropertyStub, getter == JS_PropertyStub || (attrs & JSPROP_PROPOP_ACCESSORS)); // If !(attrs & JSPROP_PROPOP_ACCESSORS), then either getter/setter are both // possibly-null JSNatives (or possibly-null JSFunction* if JSPROP_GETTER or // JSPROP_SETTER is appropriately set), or both are the well-known property // stubs. The subsequent block must handle only the first of these cases, // so carefully exclude the latter case. if (!(attrs & JSPROP_PROPOP_ACCESSORS) && getter != JS_PropertyStub && setter != JS_StrictPropertyStub) { if (getter && !(attrs & JSPROP_GETTER)) { RootedAtom atom(cx, IdToFunctionName(cx, id, FunctionPrefixKind::Get)); if (!atom) return false; JSFunction* getobj = NewNativeFunction(cx, (Native) getter, 0, atom); if (!getobj) return false; if (get.info) getobj->setJitInfo(get.info); getter = JS_DATA_TO_FUNC_PTR(GetterOp, getobj); attrs |= JSPROP_GETTER; } if (setter && !(attrs & JSPROP_SETTER)) { // Root just the getter, since the setter is not yet a JSObject. AutoRooterGetterSetter getRoot(cx, JSPROP_GETTER, &getter, nullptr); RootedAtom atom(cx, IdToFunctionName(cx, id, FunctionPrefixKind::Set)); if (!atom) return false; JSFunction* setobj = NewNativeFunction(cx, (Native) setter, 1, atom); if (!setobj) return false; if (set.info) setobj->setJitInfo(set.info); setter = JS_DATA_TO_FUNC_PTR(SetterOp, setobj); attrs |= JSPROP_SETTER; } } else { attrs &= ~JSPROP_PROPOP_ACCESSORS; } AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id, value, (attrs & JSPROP_GETTER) ? JS_FUNC_TO_DATA_PTR(JSObject*, getter) : nullptr, (attrs & JSPROP_SETTER) ? JS_FUNC_TO_DATA_PTR(JSObject*, setter) : nullptr); // In most places throughout the engine, a property with null getter and // not JSPROP_GETTER/SETTER/SHARED has no getter, and the same for setters: // it's just a plain old data property. However the JS_Define* APIs use // null getter and setter to mean "default to the Class getProperty and // setProperty ops". if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) { if (!getter) getter = obj->getClass()->getGetProperty(); if (!setter) setter = obj->getClass()->getSetProperty(); } if (getter == JS_PropertyStub) getter = nullptr; if (setter == JS_StrictPropertyStub) setter = nullptr; return DefineProperty(cx, obj, id, value, getter, setter, attrs); } /* * Wrapper functions to create wrappers with no corresponding JSJitInfo from API * function arguments. */ static JSNativeWrapper NativeOpWrapper(Native native) { JSNativeWrapper ret; ret.op = native; ret.info = nullptr; return ret; } JS_PUBLIC_API(bool) JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleValue value, unsigned attrs, Native getter, Native setter) { return DefinePropertyById(cx, obj, id, value, NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0); } JS_PUBLIC_API(bool) JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleObject valueArg, unsigned attrs, Native getter, Native setter) { RootedValue value(cx, ObjectValue(*valueArg)); return DefinePropertyById(cx, obj, id, value, NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0); } JS_PUBLIC_API(bool) JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleString valueArg, unsigned attrs, Native getter, Native setter) { RootedValue value(cx, StringValue(valueArg)); return DefinePropertyById(cx, obj, id, value, NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0); } JS_PUBLIC_API(bool) JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id, int32_t valueArg, unsigned attrs, Native getter, Native setter) { Value value = Int32Value(valueArg); return DefinePropertyById(cx, obj, id, HandleValue::fromMarkedLocation(&value), NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0); } JS_PUBLIC_API(bool) JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id, uint32_t valueArg, unsigned attrs, Native getter, Native setter) { Value value = NumberValue(valueArg); return DefinePropertyById(cx, obj, id, HandleValue::fromMarkedLocation(&value), NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0); } JS_PUBLIC_API(bool) JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id, double valueArg, unsigned attrs, Native getter, Native setter) { Value value = NumberValue(valueArg); return DefinePropertyById(cx, obj, id, HandleValue::fromMarkedLocation(&value), NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0); } static bool DefineProperty(JSContext* cx, HandleObject obj, const char* name, HandleValue value, const JSNativeWrapper& getter, const JSNativeWrapper& setter, unsigned attrs, unsigned flags) { AutoRooterGetterSetter gsRoot(cx, attrs, const_cast(&getter.op), const_cast(&setter.op)); JSAtom* atom = Atomize(cx, name, strlen(name)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, flags); } JS_PUBLIC_API(bool) JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, HandleValue value, unsigned attrs, Native getter /* = nullptr */, Native setter /* = nullptr */) { return DefineProperty(cx, obj, name, value, NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0); } JS_PUBLIC_API(bool) JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, HandleObject valueArg, unsigned attrs, Native getter /* = nullptr */, Native setter /* = nullptr */) { RootedValue value(cx, ObjectValue(*valueArg)); return DefineProperty(cx, obj, name, value, NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0); } JS_PUBLIC_API(bool) JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, HandleString valueArg, unsigned attrs, Native getter /* = nullptr */, Native setter /* = nullptr */) { RootedValue value(cx, StringValue(valueArg)); return DefineProperty(cx, obj, name, value, NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0); } JS_PUBLIC_API(bool) JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, int32_t valueArg, unsigned attrs, Native getter /* = nullptr */, Native setter /* = nullptr */) { Value value = Int32Value(valueArg); return DefineProperty(cx, obj, name, HandleValue::fromMarkedLocation(&value), NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0); } JS_PUBLIC_API(bool) JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, uint32_t valueArg, unsigned attrs, Native getter /* = nullptr */, Native setter /* = nullptr */) { Value value = NumberValue(valueArg); return DefineProperty(cx, obj, name, HandleValue::fromMarkedLocation(&value), NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0); } JS_PUBLIC_API(bool) JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, double valueArg, unsigned attrs, Native getter /* = nullptr */, Native setter /* = nullptr */) { Value value = NumberValue(valueArg); return DefineProperty(cx, obj, name, HandleValue::fromMarkedLocation(&value), NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0); } #define AUTO_NAMELEN(s,n) (((n) == (size_t)-1) ? js_strlen(s) : (n)) JS_PUBLIC_API(bool) JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, Handle desc, ObjectOpResult& result) { JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); return DefinePropertyByDescriptor(cx, obj, id, desc, result); } JS_PUBLIC_API(bool) JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, Handle desc) { JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); ObjectOpResult result; return DefinePropertyByDescriptor(cx, obj, id, desc, result) && result.checkStrict(cx, obj, id); } static bool DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, const Value& value_, Native getter, Native setter, unsigned attrs, unsigned flags) { RootedValue value(cx, value_); AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); return DefinePropertyById(cx, obj, id, value, NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, flags); } JS_PUBLIC_API(bool) JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, HandleValue value, unsigned attrs, Native getter, Native setter) { return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, 0); } JS_PUBLIC_API(bool) JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, HandleObject valueArg, unsigned attrs, Native getter, Native setter) { RootedValue value(cx, ObjectValue(*valueArg)); return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, 0); } JS_PUBLIC_API(bool) JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, HandleString valueArg, unsigned attrs, Native getter, Native setter) { RootedValue value(cx, StringValue(valueArg)); return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, 0); } JS_PUBLIC_API(bool) JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, int32_t valueArg, unsigned attrs, Native getter, Native setter) { Value value = Int32Value(valueArg); return DefineUCProperty(cx, obj, name, namelen, HandleValue::fromMarkedLocation(&value), getter, setter, attrs, 0); } JS_PUBLIC_API(bool) JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, uint32_t valueArg, unsigned attrs, Native getter, Native setter) { Value value = NumberValue(valueArg); return DefineUCProperty(cx, obj, name, namelen, HandleValue::fromMarkedLocation(&value), getter, setter, attrs, 0); } JS_PUBLIC_API(bool) JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, double valueArg, unsigned attrs, Native getter, Native setter) { Value value = NumberValue(valueArg); return DefineUCProperty(cx, obj, name, namelen, HandleValue::fromMarkedLocation(&value), getter, setter, attrs, 0); } static bool DefineElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue value, unsigned attrs, Native getter, Native setter) { AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); RootedId id(cx); if (!IndexToId(cx, index, &id)) return false; return DefinePropertyById(cx, obj, id, value, NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0); } JS_PUBLIC_API(bool) JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue value, unsigned attrs, Native getter, Native setter) { return DefineElement(cx, obj, index, value, attrs, getter, setter); } JS_PUBLIC_API(bool) JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, HandleObject valueArg, unsigned attrs, Native getter, Native setter) { RootedValue value(cx, ObjectValue(*valueArg)); return DefineElement(cx, obj, index, value, attrs, getter, setter); } JS_PUBLIC_API(bool) JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, HandleString valueArg, unsigned attrs, Native getter, Native setter) { RootedValue value(cx, StringValue(valueArg)); return DefineElement(cx, obj, index, value, attrs, getter, setter); } JS_PUBLIC_API(bool) JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, int32_t valueArg, unsigned attrs, Native getter, Native setter) { Value value = Int32Value(valueArg); return DefineElement(cx, obj, index, HandleValue::fromMarkedLocation(&value), attrs, getter, setter); } JS_PUBLIC_API(bool) JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, uint32_t valueArg, unsigned attrs, Native getter, Native setter) { Value value = NumberValue(valueArg); return DefineElement(cx, obj, index, HandleValue::fromMarkedLocation(&value), attrs, getter, setter); } JS_PUBLIC_API(bool) JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, double valueArg, unsigned attrs, Native getter, Native setter) { Value value = NumberValue(valueArg); return DefineElement(cx, obj, index, HandleValue::fromMarkedLocation(&value), attrs, getter, setter); } JS_PUBLIC_API(bool) JS_HasPropertyById(JSContext* cx, HandleObject obj, HandleId id, bool* foundp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return HasProperty(cx, obj, id, foundp); } JS_PUBLIC_API(bool) JS_HasProperty(JSContext* cx, HandleObject obj, const char* name, bool* foundp) { JSAtom* atom = Atomize(cx, name, strlen(name)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); return JS_HasPropertyById(cx, obj, id, foundp); } JS_PUBLIC_API(bool) JS_HasUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, bool* foundp) { JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); return JS_HasPropertyById(cx, obj, id, foundp); } JS_PUBLIC_API(bool) JS_HasElement(JSContext* cx, HandleObject obj, uint32_t index, bool* foundp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); RootedId id(cx); if (!IndexToId(cx, index, &id)) return false; return JS_HasPropertyById(cx, obj, id, foundp); } JS_PUBLIC_API(bool) JS_HasOwnPropertyById(JSContext* cx, HandleObject obj, HandleId id, bool* foundp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id); return HasOwnProperty(cx, obj, id, foundp); } JS_PUBLIC_API(bool) JS_HasOwnProperty(JSContext* cx, HandleObject obj, const char* name, bool* foundp) { JSAtom* atom = Atomize(cx, name, strlen(name)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); return JS_HasOwnPropertyById(cx, obj, id, foundp); } JS_PUBLIC_API(bool) JS_ForwardGetPropertyTo(JSContext* cx, HandleObject obj, HandleId id, HandleValue receiver, MutableHandleValue vp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id, receiver); return GetProperty(cx, obj, receiver, id, vp); } JS_PUBLIC_API(bool) JS_ForwardGetElementTo(JSContext* cx, HandleObject obj, uint32_t index, HandleObject receiver, MutableHandleValue vp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); return GetElement(cx, obj, receiver, index, vp); } JS_PUBLIC_API(bool) JS_GetPropertyById(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp) { RootedValue receiver(cx, ObjectValue(*obj)); return JS_ForwardGetPropertyTo(cx, obj, id, receiver, vp); } JS_PUBLIC_API(bool) JS_GetProperty(JSContext* cx, HandleObject obj, const char* name, MutableHandleValue vp) { JSAtom* atom = Atomize(cx, name, strlen(name)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); return JS_GetPropertyById(cx, obj, id, vp); } JS_PUBLIC_API(bool) JS_GetUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, MutableHandleValue vp) { JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); return JS_GetPropertyById(cx, obj, id, vp); } JS_PUBLIC_API(bool) JS_GetElement(JSContext* cx, HandleObject objArg, uint32_t index, MutableHandleValue vp) { return JS_ForwardGetElementTo(cx, objArg, index, objArg, vp); } JS_PUBLIC_API(bool) JS_ForwardSetPropertyTo(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, HandleValue receiver, ObjectOpResult& result) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id, receiver); return SetProperty(cx, obj, id, v, receiver, result); } JS_PUBLIC_API(bool) JS_SetPropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleValue v) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id); RootedValue receiver(cx, ObjectValue(*obj)); ObjectOpResult ignored; return SetProperty(cx, obj, id, v, receiver, ignored); } JS_PUBLIC_API(bool) JS_SetProperty(JSContext* cx, HandleObject obj, const char* name, HandleValue v) { JSAtom* atom = Atomize(cx, name, strlen(name)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); return JS_SetPropertyById(cx, obj, id, v); } JS_PUBLIC_API(bool) JS_SetUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, HandleValue v) { JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); return JS_SetPropertyById(cx, obj, id, v); } static bool SetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue v) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, v); RootedValue receiver(cx, ObjectValue(*obj)); ObjectOpResult ignored; return SetElement(cx, obj, index, v, receiver, ignored); } JS_PUBLIC_API(bool) JS_SetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue v) { return SetElement(cx, obj, index, v); } JS_PUBLIC_API(bool) JS_SetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleObject v) { RootedValue value(cx, ObjectOrNullValue(v)); return SetElement(cx, obj, index, value); } JS_PUBLIC_API(bool) JS_SetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleString v) { RootedValue value(cx, StringValue(v)); return SetElement(cx, obj, index, value); } JS_PUBLIC_API(bool) JS_SetElement(JSContext* cx, HandleObject obj, uint32_t index, int32_t v) { RootedValue value(cx, NumberValue(v)); return SetElement(cx, obj, index, value); } JS_PUBLIC_API(bool) JS_SetElement(JSContext* cx, HandleObject obj, uint32_t index, uint32_t v) { RootedValue value(cx, NumberValue(v)); return SetElement(cx, obj, index, value); } JS_PUBLIC_API(bool) JS_SetElement(JSContext* cx, HandleObject obj, uint32_t index, double v) { RootedValue value(cx, NumberValue(v)); return SetElement(cx, obj, index, value); } JS_PUBLIC_API(bool) JS_DeletePropertyById(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id); return DeleteProperty(cx, obj, id, result); } JS_PUBLIC_API(bool) JS_DeleteProperty(JSContext* cx, HandleObject obj, const char* name, ObjectOpResult& result) { CHECK_REQUEST(cx); assertSameCompartment(cx, obj); JSAtom* atom = Atomize(cx, name, strlen(name)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); return DeleteProperty(cx, obj, id, result); } JS_PUBLIC_API(bool) JS_DeleteUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, ObjectOpResult& result) { CHECK_REQUEST(cx); assertSameCompartment(cx, obj); JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); return DeleteProperty(cx, obj, id, result); } JS_PUBLIC_API(bool) JS_DeleteElement(JSContext* cx, HandleObject obj, uint32_t index, ObjectOpResult& result) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); return DeleteElement(cx, obj, index, result); } JS_PUBLIC_API(bool) JS_DeletePropertyById(JSContext* cx, HandleObject obj, HandleId id) { ObjectOpResult ignored; return JS_DeletePropertyById(cx, obj, id, ignored); } JS_PUBLIC_API(bool) JS_DeleteProperty(JSContext* cx, HandleObject obj, const char* name) { ObjectOpResult ignored; return JS_DeleteProperty(cx, obj, name, ignored); } JS_PUBLIC_API(bool) JS_DeleteElement(JSContext* cx, HandleObject obj, uint32_t index) { ObjectOpResult ignored; return JS_DeleteElement(cx, obj, index, ignored); } JS_PUBLIC_API(bool) JS_Enumerate(JSContext* cx, HandleObject obj, JS::MutableHandle props) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); MOZ_ASSERT(props.empty()); AutoIdVector ids(cx); if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY, &ids)) return false; return props.append(ids.begin(), ids.end()); } JS_PUBLIC_API(bool) JS::IsCallable(JSObject* obj) { return obj->isCallable(); } JS_PUBLIC_API(bool) JS::IsConstructor(JSObject* obj) { return obj->isConstructor(); } JS_PUBLIC_API(bool) JS_CallFunctionValue(JSContext* cx, HandleObject obj, HandleValue fval, const HandleValueArray& args, MutableHandleValue rval) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, fval, args); InvokeArgs iargs(cx); if (!FillArgumentsFromArraylike(cx, iargs, args)) return false; RootedValue thisv(cx, ObjectOrNullValue(obj)); return Call(cx, fval, thisv, iargs, rval); } JS_PUBLIC_API(bool) JS_CallFunction(JSContext* cx, HandleObject obj, HandleFunction fun, const HandleValueArray& args, MutableHandleValue rval) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, fun, args); InvokeArgs iargs(cx); if (!FillArgumentsFromArraylike(cx, iargs, args)) return false; RootedValue fval(cx, ObjectValue(*fun)); RootedValue thisv(cx, ObjectOrNullValue(obj)); return Call(cx, fval, thisv, iargs, rval); } JS_PUBLIC_API(bool) JS_CallFunctionName(JSContext* cx, HandleObject obj, const char* name, const HandleValueArray& args, MutableHandleValue rval) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, args); JSAtom* atom = Atomize(cx, name, strlen(name)); if (!atom) return false; RootedValue fval(cx); RootedId id(cx, AtomToId(atom)); if (!GetProperty(cx, obj, obj, id, &fval)) return false; InvokeArgs iargs(cx); if (!FillArgumentsFromArraylike(cx, iargs, args)) return false; RootedValue thisv(cx, ObjectOrNullValue(obj)); return Call(cx, fval, thisv, iargs, rval); } JS_PUBLIC_API(bool) JS::Call(JSContext* cx, HandleValue thisv, HandleValue fval, const JS::HandleValueArray& args, MutableHandleValue rval) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, thisv, fval, args); InvokeArgs iargs(cx); if (!FillArgumentsFromArraylike(cx, iargs, args)) return false; return Call(cx, fval, thisv, iargs, rval); } JS_PUBLIC_API(bool) JS::Construct(JSContext* cx, HandleValue fval, HandleObject newTarget, const JS::HandleValueArray& args, MutableHandleObject objp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, fval, newTarget, args); if (!IsConstructor(fval)) { ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, fval, nullptr); return false; } RootedValue newTargetVal(cx, ObjectValue(*newTarget)); if (!IsConstructor(newTargetVal)) { ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, newTargetVal, nullptr); return false; } ConstructArgs cargs(cx); if (!FillArgumentsFromArraylike(cx, cargs, args)) return false; return js::Construct(cx, fval, cargs, newTargetVal, objp); } JS_PUBLIC_API(bool) JS::Construct(JSContext* cx, HandleValue fval, const JS::HandleValueArray& args, MutableHandleObject objp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, fval, args); if (!IsConstructor(fval)) { ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, fval, nullptr); return false; } ConstructArgs cargs(cx); if (!FillArgumentsFromArraylike(cx, cargs, args)) return false; return js::Construct(cx, fval, cargs, fval, objp); } /* * */ JS_PUBLIC_API(bool) JS_AlreadyHasOwnPropertyById(JSContext* cx, HandleObject obj, HandleId id, bool* foundp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id); if (!obj->isNative()) return js::HasOwnProperty(cx, obj, id, foundp); RootedNativeObject nativeObj(cx, &obj->as()); RootedShape prop(cx); NativeLookupOwnPropertyNoResolve(cx, nativeObj, id, &prop); *foundp = !!prop; return true; } JS_PUBLIC_API(bool) JS_AlreadyHasOwnProperty(JSContext* cx, HandleObject obj, const char* name, bool* foundp) { JSAtom* atom = Atomize(cx, name, strlen(name)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp); } JS_PUBLIC_API(bool) JS_AlreadyHasOwnUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, bool* foundp) { JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp); } JS_PUBLIC_API(bool) JS_AlreadyHasOwnElement(JSContext* cx, HandleObject obj, uint32_t index, bool* foundp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); RootedId id(cx); if (!IndexToId(cx, index, &id)) return false; return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp); } JS_PUBLIC_API(bool) JS_FreezeObject(JSContext* cx, HandleObject obj) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); return FreezeObject(cx, obj); } static bool DeepFreezeSlot(JSContext* cx, const Value& v) { if (v.isPrimitive()) return true; RootedObject obj(cx, &v.toObject()); return JS_DeepFreezeObject(cx, obj); } JS_PUBLIC_API(bool) JS_DeepFreezeObject(JSContext* cx, HandleObject obj) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); /* Assume that non-extensible objects are already deep-frozen, to avoid divergence. */ bool extensible; if (!IsExtensible(cx, obj, &extensible)) return false; if (!extensible) return true; if (!FreezeObject(cx, obj)) return false; /* Walk slots in obj and if any value is a non-null object, seal it. */ if (obj->isNative()) { RootedNativeObject nobj(cx, &obj->as()); for (uint32_t i = 0, n = nobj->slotSpan(); i < n; ++i) { if (!DeepFreezeSlot(cx, nobj->getSlot(i))) return false; } for (uint32_t i = 0, n = nobj->getDenseInitializedLength(); i < n; ++i) { if (!DeepFreezeSlot(cx, nobj->getDenseElement(i))) return false; } } return true; } static bool DefineSelfHostedProperty(JSContext* cx, HandleObject obj, HandleId id, const char* getterName, const char* setterName, unsigned attrs, unsigned flags) { JSAtom* getterNameAtom = Atomize(cx, getterName, strlen(getterName)); if (!getterNameAtom) return false; RootedPropertyName getterNameName(cx, getterNameAtom->asPropertyName()); RootedAtom name(cx, IdToFunctionName(cx, id)); if (!name) return false; RootedValue getterValue(cx); if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), getterNameName, name, 0, &getterValue)) { return false; } MOZ_ASSERT(getterValue.isObject() && getterValue.toObject().is()); RootedFunction getterFunc(cx, &getterValue.toObject().as()); JSNative getterOp = JS_DATA_TO_FUNC_PTR(JSNative, getterFunc.get()); RootedFunction setterFunc(cx); if (setterName) { JSAtom* setterNameAtom = Atomize(cx, setterName, strlen(setterName)); if (!setterNameAtom) return false; RootedPropertyName setterNameName(cx, setterNameAtom->asPropertyName()); RootedValue setterValue(cx); if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), setterNameName, name, 0, &setterValue)) { return false; } MOZ_ASSERT(setterValue.isObject() && setterValue.toObject().is()); setterFunc = &setterValue.toObject().as(); } JSNative setterOp = JS_DATA_TO_FUNC_PTR(JSNative, setterFunc.get()); return DefinePropertyById(cx, obj, id, JS::UndefinedHandleValue, NativeOpWrapper(getterOp), NativeOpWrapper(setterOp), attrs, flags); } JS_PUBLIC_API(JSObject*) JS_DefineObject(JSContext* cx, HandleObject obj, const char* name, const JSClass* jsclasp, unsigned attrs) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); const Class* clasp = Valueify(jsclasp); if (!clasp) clasp = &PlainObject::class_; /* default class is Object */ RootedObject nobj(cx, NewObjectWithClassProto(cx, clasp, nullptr)); if (!nobj) return nullptr; RootedValue nobjValue(cx, ObjectValue(*nobj)); if (!DefineProperty(cx, obj, name, nobjValue, NativeOpWrapper(nullptr), NativeOpWrapper(nullptr), attrs, 0)) { return nullptr; } return nobj; } static inline Value ValueFromScalar(double x) { return DoubleValue(x); } static inline Value ValueFromScalar(int32_t x) { return Int32Value(x); } template static bool DefineConstScalar(JSContext* cx, HandleObject obj, const JSConstScalarSpec* cds) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); JSNativeWrapper noget = NativeOpWrapper(nullptr); JSNativeWrapper noset = NativeOpWrapper(nullptr); unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT; for (; cds->name; cds++) { RootedValue value(cx, ValueFromScalar(cds->val)); if (!DefineProperty(cx, obj, cds->name, value, noget, noset, attrs, 0)) return false; } return true; } JS_PUBLIC_API(bool) JS_DefineConstDoubles(JSContext* cx, HandleObject obj, const JSConstDoubleSpec* cds) { return DefineConstScalar(cx, obj, cds); } JS_PUBLIC_API(bool) JS_DefineConstIntegers(JSContext* cx, HandleObject obj, const JSConstIntegerSpec* cis) { return DefineConstScalar(cx, obj, cis); } JS_PUBLIC_API(bool) JSPropertySpec::getValue(JSContext* cx, MutableHandleValue vp) const { MOZ_ASSERT(!isAccessor()); if (value.type == JSVAL_TYPE_STRING) { RootedAtom atom(cx, Atomize(cx, value.string, strlen(value.string))); if (!atom) return false; vp.setString(atom); } else { MOZ_ASSERT(value.type == JSVAL_TYPE_INT32); vp.setInt32(value.int32); } return true; } static JS::SymbolCode PropertySpecNameToSymbolCode(const char* name) { MOZ_ASSERT(JS::PropertySpecNameIsSymbol(name)); uintptr_t u = reinterpret_cast(name); return JS::SymbolCode(u - 1); } bool PropertySpecNameToId(JSContext* cx, const char* name, MutableHandleId id, js::PinningBehavior pin = js::DoNotPinAtom) { if (JS::PropertySpecNameIsSymbol(name)) { JS::SymbolCode which = PropertySpecNameToSymbolCode(name); id.set(SYMBOL_TO_JSID(cx->wellKnownSymbols().get(which))); } else { JSAtom* atom = Atomize(cx, name, strlen(name), pin); if (!atom) return false; id.set(AtomToId(atom)); } return true; } JS_PUBLIC_API(bool) JS::PropertySpecNameToPermanentId(JSContext* cx, const char* name, jsid* idp) { // We are calling fromMarkedLocation(idp) even though idp points to a // location that will never be marked. This is OK because the whole point // of this API is to populate *idp with a jsid that does not need to be // marked. return PropertySpecNameToId(cx, name, MutableHandleId::fromMarkedLocation(idp), js::PinAtom); } JS_PUBLIC_API(bool) JS_DefineProperties(JSContext* cx, HandleObject obj, const JSPropertySpec* ps) { RootedId id(cx); for (; ps->name; ps++) { if (!PropertySpecNameToId(cx, ps->name, &id)) return false; if (ps->isAccessor()) { if (ps->isSelfHosted()) { if (!DefineSelfHostedProperty(cx, obj, id, ps->accessors.getter.selfHosted.funname, ps->accessors.setter.selfHosted.funname, ps->flags, 0)) { return false; } } else { if (!DefinePropertyById(cx, obj, id, JS::UndefinedHandleValue, ps->accessors.getter.native, ps->accessors.setter.native, ps->flags, 0)) { return false; } } } else { RootedValue v(cx); if (!ps->getValue(cx, &v)) return false; if (!DefinePropertyById(cx, obj, id, v, NativeOpWrapper(nullptr), NativeOpWrapper(nullptr), ps->flags & ~JSPROP_INTERNAL_USE_BIT, 0)) { return false; } } } return true; } JS_PUBLIC_API(bool) JS::ObjectToCompletePropertyDescriptor(JSContext* cx, HandleObject obj, HandleValue descObj, MutableHandle desc) { if (!ToPropertyDescriptor(cx, descObj, true, desc)) return false; CompletePropertyDescriptor(desc); desc.object().set(obj); return true; } JS_PUBLIC_API(void) JS_SetAllNonReservedSlotsToUndefined(JSContext* cx, JSObject* objArg) { RootedObject obj(cx, objArg); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); if (!obj->isNative()) return; const Class* clasp = obj->getClass(); unsigned numReserved = JSCLASS_RESERVED_SLOTS(clasp); unsigned numSlots = obj->as().slotSpan(); for (unsigned i = numReserved; i < numSlots; i++) obj->as().setSlot(i, UndefinedValue()); } JS_PUBLIC_API(Value) JS_GetReservedSlot(JSObject* obj, uint32_t index) { return obj->as().getReservedSlot(index); } JS_PUBLIC_API(void) JS_SetReservedSlot(JSObject* obj, uint32_t index, const Value& value) { obj->as().setReservedSlot(index, value); } JS_PUBLIC_API(JSObject*) JS_NewArrayObject(JSContext* cx, const JS::HandleValueArray& contents) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, contents); return NewDenseCopiedArray(cx, contents.length(), contents.begin()); } JS_PUBLIC_API(JSObject*) JS_NewArrayObject(JSContext* cx, size_t length) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return NewDenseFullyAllocatedArray(cx, length); } inline bool IsGivenTypeObject(JSContext* cx, JS::HandleObject obj, const ESClass& typeClass, bool* isType) { assertSameCompartment(cx, obj); ESClass cls; if (!GetBuiltinClass(cx, obj, &cls)) return false; *isType = cls == typeClass; return true; } JS_PUBLIC_API(bool) JS_IsArrayObject(JSContext* cx, JS::HandleObject obj, bool* isArray) { return IsGivenTypeObject(cx, obj, ESClass::Array, isArray); } JS_PUBLIC_API(bool) JS_IsArrayObject(JSContext* cx, JS::HandleValue value, bool* isArray) { if (!value.isObject()) { *isArray = false; return true; } RootedObject obj(cx, &value.toObject()); return JS_IsArrayObject(cx, obj, isArray); } JS_PUBLIC_API(bool) JS_GetArrayLength(JSContext* cx, HandleObject obj, uint32_t* lengthp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); return GetLengthProperty(cx, obj, lengthp); } JS_PUBLIC_API(bool) JS_SetArrayLength(JSContext* cx, HandleObject obj, uint32_t length) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); return SetLengthProperty(cx, obj, length); } JS_PUBLIC_API(bool) JS::IsMapObject(JSContext* cx, JS::HandleObject obj, bool* isMap) { return IsGivenTypeObject(cx, obj, ESClass::Map, isMap); } JS_PUBLIC_API(bool) JS::IsSetObject(JSContext* cx, JS::HandleObject obj, bool* isSet) { return IsGivenTypeObject(cx, obj, ESClass::Set, isSet); } JS_PUBLIC_API(void) JS_HoldPrincipals(JSPrincipals* principals) { ++principals->refcount; } JS_PUBLIC_API(void) JS_DropPrincipals(JSContext* cx, JSPrincipals* principals) { int rc = --principals->refcount; if (rc == 0) cx->destroyPrincipals(principals); } JS_PUBLIC_API(void) JS_SetSecurityCallbacks(JSContext* cx, const JSSecurityCallbacks* scb) { MOZ_ASSERT(scb != &NullSecurityCallbacks); cx->securityCallbacks = scb ? scb : &NullSecurityCallbacks; } JS_PUBLIC_API(const JSSecurityCallbacks*) JS_GetSecurityCallbacks(JSContext* cx) { return (cx->securityCallbacks != &NullSecurityCallbacks) ? cx->securityCallbacks : nullptr; } JS_PUBLIC_API(void) JS_SetTrustedPrincipals(JSContext* cx, JSPrincipals* prin) { cx->setTrustedPrincipals(prin); } extern JS_PUBLIC_API(void) JS_InitDestroyPrincipalsCallback(JSContext* cx, JSDestroyPrincipalsOp destroyPrincipals) { MOZ_ASSERT(destroyPrincipals); MOZ_ASSERT(!cx->destroyPrincipals); cx->destroyPrincipals = destroyPrincipals; } extern JS_PUBLIC_API(void) JS_InitReadPrincipalsCallback(JSContext* cx, JSReadPrincipalsOp read) { MOZ_ASSERT(read); MOZ_ASSERT(!cx->readPrincipals); cx->readPrincipals = read; } JS_PUBLIC_API(JSFunction*) JS_NewFunction(JSContext* cx, JSNative native, unsigned nargs, unsigned flags, const char* name) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); RootedAtom atom(cx); if (name) { atom = Atomize(cx, name, strlen(name)); if (!atom) return nullptr; } return (flags & JSFUN_CONSTRUCTOR) ? NewNativeConstructor(cx, native, nargs, atom) : NewNativeFunction(cx, native, nargs, atom); } JS_PUBLIC_API(JSFunction*) JS::GetSelfHostedFunction(JSContext* cx, const char* selfHostedName, HandleId id, unsigned nargs) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); RootedAtom name(cx, IdToFunctionName(cx, id)); if (!name) return nullptr; JSAtom* shAtom = Atomize(cx, selfHostedName, strlen(selfHostedName)); if (!shAtom) return nullptr; RootedPropertyName shName(cx, shAtom->asPropertyName()); RootedValue funVal(cx); if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), shName, name, nargs, &funVal)) return nullptr; return &funVal.toObject().as(); } JS_PUBLIC_API(JSFunction*) JS::NewFunctionFromSpec(JSContext* cx, const JSFunctionSpec* fs, HandleId id) { // Delay cloning self-hosted functions until they are called. This is // achieved by passing DefineFunction a nullptr JSNative which produces an // interpreted JSFunction where !hasScript. Interpreted call paths then // call InitializeLazyFunctionScript if !hasScript. if (fs->selfHostedName) { MOZ_ASSERT(!fs->call.op); MOZ_ASSERT(!fs->call.info); JSAtom* shAtom = Atomize(cx, fs->selfHostedName, strlen(fs->selfHostedName)); if (!shAtom) return nullptr; RootedPropertyName shName(cx, shAtom->asPropertyName()); RootedAtom name(cx, IdToFunctionName(cx, id)); if (!name) return nullptr; RootedValue funVal(cx); if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), shName, name, fs->nargs, &funVal)) { return nullptr; } return &funVal.toObject().as(); } RootedAtom atom(cx, IdToFunctionName(cx, id)); if (!atom) return nullptr; JSFunction* fun; if (!fs->call.op) fun = NewScriptedFunction(cx, fs->nargs, JSFunction::INTERPRETED_LAZY, atom); else if (fs->flags & JSFUN_CONSTRUCTOR) fun = NewNativeConstructor(cx, fs->call.op, fs->nargs, atom); else fun = NewNativeFunction(cx, fs->call.op, fs->nargs, atom); if (!fun) return nullptr; if (fs->call.info) fun->setJitInfo(fs->call.info); return fun; } static bool CreateNonSyntacticEnvironmentChain(JSContext* cx, AutoObjectVector& envChain, MutableHandleObject env, MutableHandleScope scope) { RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment()); if (!js::CreateObjectsForEnvironmentChain(cx, envChain, globalLexical, env)) return false; if (!envChain.empty()) { scope.set(GlobalScope::createEmpty(cx, ScopeKind::NonSyntactic)); if (!scope) return false; // The XPConnect subscript loader, which may pass in its own // environments to load scripts in, expects the environment chain to // be the holder of "var" declarations. In SpiderMonkey, such objects // are called "qualified varobjs", the "qualified" part meaning the // declaration was qualified by "var". There is only sadness. // // See JSObject::isQualifiedVarObj. if (!JSObject::setQualifiedVarObj(cx, env)) return false; // Also get a non-syntactic lexical environment to capture 'let' and // 'const' bindings. To persist lexical bindings, we have a 1-1 // mapping with the final unwrapped environment object (the // environment that stores the 'var' bindings) and the lexical // environment. // // TODOshu: disallow the subscript loader from using non-distinguished // objects as dynamic scopes. env.set(cx->compartment()->getOrCreateNonSyntacticLexicalEnvironment(cx, env)); if (!env) return false; } else { scope.set(&cx->global()->emptyGlobalScope()); } return true; } static bool IsFunctionCloneable(HandleFunction fun) { if (!fun->isInterpreted()) return true; // If a function was compiled with non-global syntactic environments on // the environment chain, we could have baked in EnvironmentCoordinates // into the script. We cannot clone it without breaking the compiler's // assumptions. for (ScopeIter si(fun->nonLazyScript()->enclosingScope()); si; si++) { if (si.scope()->is()) return true; if (si.hasSyntacticEnvironment()) return false; } return true; } static JSObject* CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject env, HandleScope scope) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, env); MOZ_ASSERT(env); // Note that funobj can be in a different compartment. if (!funobj->is()) { AutoCompartment ac(cx, funobj); RootedValue v(cx, ObjectValue(*funobj)); ReportIsNotFunction(cx, v); return nullptr; } RootedFunction fun(cx, &funobj->as()); if (fun->isInterpretedLazy()) { AutoCompartment ac(cx, funobj); if (!JSFunction::getOrCreateScript(cx, fun)) return nullptr; } if (!IsFunctionCloneable(fun)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_CLONE_FUNOBJ_SCOPE); return nullptr; } if (fun->isBoundFunction()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT); return nullptr; } if (IsAsmJSModule(fun)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT); return nullptr; } if (IsWrappedAsyncFunction(fun)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT); return nullptr; } if (CanReuseScriptForClone(cx->compartment(), fun, env)) { // If the script is to be reused, either the script can already handle // non-syntactic scopes, or there is only the standard global lexical // scope. #ifdef DEBUG // Fail here if we OOM during debug asserting. // CloneFunctionReuseScript will delazify the script anyways, so we // are not creating an extra failure condition for DEBUG builds. if (!JSFunction::getOrCreateScript(cx, fun)) return nullptr; MOZ_ASSERT(scope->as().isSyntactic() || fun->nonLazyScript()->hasNonSyntacticScope()); #endif return CloneFunctionReuseScript(cx, fun, env, fun->getAllocKind()); } JSFunction* clone = CloneFunctionAndScript(cx, fun, env, scope, fun->getAllocKind()); #ifdef DEBUG // The cloned function should itself be cloneable. RootedFunction cloneRoot(cx, clone); MOZ_ASSERT_IF(cloneRoot, IsFunctionCloneable(cloneRoot)); #endif return clone; } JS_PUBLIC_API(JSObject*) JS::CloneFunctionObject(JSContext* cx, HandleObject funobj) { RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment()); RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope()); return CloneFunctionObject(cx, funobj, globalLexical, emptyGlobalScope); } extern JS_PUBLIC_API(JSObject*) JS::CloneFunctionObject(JSContext* cx, HandleObject funobj, AutoObjectVector& envChain) { RootedObject env(cx); RootedScope scope(cx); if (!CreateNonSyntacticEnvironmentChain(cx, envChain, &env, &scope)) return nullptr; return CloneFunctionObject(cx, funobj, env, scope); } JS_PUBLIC_API(JSObject*) JS_GetFunctionObject(JSFunction* fun) { return fun; } JS_PUBLIC_API(JSString*) JS_GetFunctionId(JSFunction* fun) { return fun->explicitName(); } JS_PUBLIC_API(JSString*) JS_GetFunctionDisplayId(JSFunction* fun) { return fun->displayAtom(); } JS_PUBLIC_API(uint16_t) JS_GetFunctionArity(JSFunction* fun) { return fun->nargs(); } JS_PUBLIC_API(bool) JS_ObjectIsFunction(JSContext* cx, JSObject* obj) { return obj->is(); } JS_PUBLIC_API(bool) JS_IsNativeFunction(JSObject* funobj, JSNative call) { if (!funobj->is()) return false; JSFunction* fun = &funobj->as(); return fun->isNative() && fun->native() == call; } extern JS_PUBLIC_API(bool) JS_IsConstructor(JSFunction* fun) { return fun->isConstructor(); } JS_PUBLIC_API(bool) JS_DefineFunctions(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); return DefineFunctions(cx, obj, fs, NotIntrinsic); } JS_PUBLIC_API(JSFunction*) JS_DefineFunction(JSContext* cx, HandleObject obj, const char* name, JSNative call, unsigned nargs, unsigned attrs) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); JSAtom* atom = Atomize(cx, name, strlen(name)); if (!atom) return nullptr; Rooted id(cx, AtomToId(atom)); return DefineFunction(cx, obj, id, call, nargs, attrs); } JS_PUBLIC_API(JSFunction*) JS_DefineUCFunction(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, JSNative call, unsigned nargs, unsigned attrs) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); if (!atom) return nullptr; Rooted id(cx, AtomToId(atom)); return DefineFunction(cx, obj, id, call, nargs, attrs); } extern JS_PUBLIC_API(JSFunction*) JS_DefineFunctionById(JSContext* cx, HandleObject obj, HandleId id, JSNative call, unsigned nargs, unsigned attrs) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); return DefineFunction(cx, obj, id, call, nargs, attrs); } /* Use the fastest available getc. */ #if defined(HAVE_GETC_UNLOCKED) # define fast_getc getc_unlocked #elif defined(HAVE__GETC_NOLOCK) # define fast_getc _getc_nolock #else # define fast_getc getc #endif typedef Vector FileContents; static bool ReadCompleteFile(JSContext* cx, FILE* fp, FileContents& buffer) { /* Get the complete length of the file, if possible. */ struct stat st; int ok = fstat(fileno(fp), &st); if (ok != 0) return false; if (st.st_size > 0) { if (!buffer.reserve(st.st_size)) return false; } // Read in the whole file. Note that we can't assume the data's length // is actually st.st_size, because 1) some files lie about their size // (/dev/zero and /dev/random), and 2) reading files in text mode on // Windows collapses "\r\n" pairs to single \n characters. for (;;) { int c = fast_getc(fp); if (c == EOF) break; if (!buffer.append(c)) return false; } return true; } namespace { class AutoFile { FILE* fp_; public: AutoFile() : fp_(nullptr) {} ~AutoFile() { if (fp_ && fp_ != stdin) fclose(fp_); } FILE* fp() const { return fp_; } bool open(JSContext* cx, const char* filename); bool readAll(JSContext* cx, FileContents& buffer) { MOZ_ASSERT(fp_); return ReadCompleteFile(cx, fp_, buffer); } }; } /* anonymous namespace */ /* * Open a source file for reading. Supports "-" and nullptr to mean stdin. The * return value must be fclosed unless it is stdin. */ bool AutoFile::open(JSContext* cx, const char* filename) { if (!filename || strcmp(filename, "-") == 0) { fp_ = stdin; } else { fp_ = fopen(filename, "r"); if (!fp_) { /* * Use Latin1 variant here because the encoding of filename is * platform dependent. */ JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_CANT_OPEN, filename, "No such file or directory"); return false; } } return true; } void JS::TransitiveCompileOptions::copyPODTransitiveOptions(const TransitiveCompileOptions& rhs) { mutedErrors_ = rhs.mutedErrors_; version = rhs.version; versionSet = rhs.versionSet; utf8 = rhs.utf8; selfHostingMode = rhs.selfHostingMode; canLazilyParse = rhs.canLazilyParse; strictOption = rhs.strictOption; extraWarningsOption = rhs.extraWarningsOption; werrorOption = rhs.werrorOption; asmJSOption = rhs.asmJSOption; throwOnAsmJSValidationFailureOption = rhs.throwOnAsmJSValidationFailureOption; forceAsync = rhs.forceAsync; installedFile = rhs.installedFile; sourceIsLazy = rhs.sourceIsLazy; introductionType = rhs.introductionType; introductionLineno = rhs.introductionLineno; introductionOffset = rhs.introductionOffset; hasIntroductionInfo = rhs.hasIntroductionInfo; }; void JS::ReadOnlyCompileOptions::copyPODOptions(const ReadOnlyCompileOptions& rhs) { copyPODTransitiveOptions(rhs); lineno = rhs.lineno; column = rhs.column; isRunOnce = rhs.isRunOnce; noScriptRval = rhs.noScriptRval; } JS::OwningCompileOptions::OwningCompileOptions(JSContext* cx) : ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx), introductionScriptRoot(cx) { } JS::OwningCompileOptions::~OwningCompileOptions() { // OwningCompileOptions always owns these, so these casts are okay. js_free(const_cast(filename_)); js_free(const_cast(sourceMapURL_)); js_free(const_cast(introducerFilename_)); } bool JS::OwningCompileOptions::copy(JSContext* cx, const ReadOnlyCompileOptions& rhs) { copyPODOptions(rhs); setElement(rhs.element()); setElementAttributeName(rhs.elementAttributeName()); setIntroductionScript(rhs.introductionScript()); return setFileAndLine(cx, rhs.filename(), rhs.lineno) && setSourceMapURL(cx, rhs.sourceMapURL()) && setIntroducerFilename(cx, rhs.introducerFilename()); } bool JS::OwningCompileOptions::setFile(JSContext* cx, const char* f) { char* copy = nullptr; if (f) { copy = JS_strdup(cx, f); if (!copy) return false; } // OwningCompileOptions always owns filename_, so this cast is okay. js_free(const_cast(filename_)); filename_ = copy; return true; } bool JS::OwningCompileOptions::setFileAndLine(JSContext* cx, const char* f, unsigned l) { if (!setFile(cx, f)) return false; lineno = l; return true; } bool JS::OwningCompileOptions::setSourceMapURL(JSContext* cx, const char16_t* s) { UniqueTwoByteChars copy; if (s) { copy = DuplicateString(cx, s); if (!copy) return false; } // OwningCompileOptions always owns sourceMapURL_, so this cast is okay. js_free(const_cast(sourceMapURL_)); sourceMapURL_ = copy.release(); return true; } bool JS::OwningCompileOptions::setIntroducerFilename(JSContext* cx, const char* s) { char* copy = nullptr; if (s) { copy = JS_strdup(cx, s); if (!copy) return false; } // OwningCompileOptions always owns introducerFilename_, so this cast is okay. js_free(const_cast(introducerFilename_)); introducerFilename_ = copy; return true; } JS::CompileOptions::CompileOptions(JSContext* cx, JSVersion version) : ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx), introductionScriptRoot(cx) { this->version = (version != JSVERSION_UNKNOWN) ? version : cx->findVersion(); strictOption = cx->options().strictMode(); extraWarningsOption = cx->compartment()->behaviors().extraWarnings(cx); werrorOption = cx->options().werror(); if (!cx->options().asmJS()) asmJSOption = AsmJSOption::Disabled; else if (cx->compartment()->debuggerObservesAsmJS()) asmJSOption = AsmJSOption::DisabledByDebugger; else asmJSOption = AsmJSOption::Enabled; throwOnAsmJSValidationFailureOption = cx->options().throwOnAsmJSValidationFailure(); } static bool Compile(JSContext* cx, const ReadOnlyCompileOptions& options, ScopeKind scopeKind, SourceBufferHolder& srcBuf, MutableHandleScript script) { MOZ_ASSERT(scopeKind == ScopeKind::Global || scopeKind == ScopeKind::NonSyntactic); MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); script.set(frontend::CompileGlobalScript(cx, cx->tempLifoAlloc(), scopeKind, options, srcBuf)); return !!script; } static bool Compile(JSContext* cx, const ReadOnlyCompileOptions& options, ScopeKind scopeKind, const char16_t* chars, size_t length, MutableHandleScript script) { SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership); return ::Compile(cx, options, scopeKind, srcBuf, script); } static bool Compile(JSContext* cx, const ReadOnlyCompileOptions& options, ScopeKind scopeKind, const char* bytes, size_t length, MutableHandleScript script) { UniqueTwoByteChars chars; if (options.utf8) chars.reset(UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get()); else chars.reset(InflateString(cx, bytes, &length)); if (!chars) return false; return ::Compile(cx, options, scopeKind, chars.get(), length, script); } static bool Compile(JSContext* cx, const ReadOnlyCompileOptions& options, ScopeKind scopeKind, FILE* fp, MutableHandleScript script) { FileContents buffer(cx); if (!ReadCompleteFile(cx, fp, buffer)) return false; return ::Compile(cx, options, scopeKind, buffer.begin(), buffer.length(), script); } static bool Compile(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, ScopeKind scopeKind, const char* filename, MutableHandleScript script) { AutoFile file; if (!file.open(cx, filename)) return false; CompileOptions options(cx, optionsArg); options.setFileAndLine(filename, 1); return ::Compile(cx, options, scopeKind, file.fp(), script); } bool JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf, JS::MutableHandleScript script) { return ::Compile(cx, options, ScopeKind::Global, srcBuf, script); } bool JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options, const char* bytes, size_t length, JS::MutableHandleScript script) { return ::Compile(cx, options, ScopeKind::Global, bytes, length, script); } bool JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options, const char16_t* chars, size_t length, JS::MutableHandleScript script) { return ::Compile(cx, options, ScopeKind::Global, chars, length, script); } bool JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options, FILE* file, JS::MutableHandleScript script) { return ::Compile(cx, options, ScopeKind::Global, file, script); } bool JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options, const char* filename, JS::MutableHandleScript script) { return ::Compile(cx, options, ScopeKind::Global, filename, script); } bool JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf, JS::MutableHandleScript script) { return ::Compile(cx, options, ScopeKind::NonSyntactic, srcBuf, script); } bool JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, const char* bytes, size_t length, JS::MutableHandleScript script) { return ::Compile(cx, options, ScopeKind::NonSyntactic, bytes, length, script); } bool JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, const char16_t* chars, size_t length, JS::MutableHandleScript script) { return ::Compile(cx, options, ScopeKind::NonSyntactic, chars, length, script); } bool JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, FILE* file, JS::MutableHandleScript script) { return ::Compile(cx, options, ScopeKind::NonSyntactic, file, script); } bool JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, const char* filename, JS::MutableHandleScript script) { return ::Compile(cx, options, ScopeKind::NonSyntactic, filename, script); } JS_PUBLIC_API(bool) JS::CanCompileOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, size_t length) { static const size_t TINY_LENGTH = 5 * 1000; static const size_t HUGE_LENGTH = 100 * 1000; // These are heuristics which the caller may choose to ignore (e.g., for // testing purposes). if (!options.forceAsync) { // Compiling off the main thread inolves creating a new Zone and other // significant overheads. Don't bother if the script is tiny. if (length < TINY_LENGTH) return false; // If the parsing task would have to wait for GC to complete, it'll probably // be faster to just start it synchronously on the main thread unless the // script is huge. if (OffThreadParsingMustWaitForGC(cx->runtime()) && length < HUGE_LENGTH) return false; } return cx->runtime()->canUseParallelParsing() && CanUseExtraThreads(); } JS_PUBLIC_API(bool) JS::CompileOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, const char16_t* chars, size_t length, OffThreadCompileCallback callback, void* callbackData) { MOZ_ASSERT(CanCompileOffThread(cx, options, length)); return StartOffThreadParseScript(cx, options, chars, length, callback, callbackData); } JS_PUBLIC_API(JSScript*) JS::FinishOffThreadScript(JSContext* cx, void* token) { MOZ_ASSERT(cx); MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx)); return HelperThreadState().finishScriptParseTask(cx, token); } JS_PUBLIC_API(void) JS::CancelOffThreadScript(JSContext* cx, void* token) { MOZ_ASSERT(cx); MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx)); HelperThreadState().cancelParseTask(cx, ParseTaskKind::Script, token); } JS_PUBLIC_API(bool) JS::CompileOffThreadModule(JSContext* cx, const ReadOnlyCompileOptions& options, const char16_t* chars, size_t length, OffThreadCompileCallback callback, void* callbackData) { MOZ_ASSERT(CanCompileOffThread(cx, options, length)); return StartOffThreadParseModule(cx, options, chars, length, callback, callbackData); } JS_PUBLIC_API(JSObject*) JS::FinishOffThreadModule(JSContext* cx, void* token) { MOZ_ASSERT(cx); MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx)); return HelperThreadState().finishModuleParseTask(cx, token); } JS_PUBLIC_API(void) JS::CancelOffThreadModule(JSContext* cx, void* token) { MOZ_ASSERT(cx); MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx)); HelperThreadState().cancelParseTask(cx, ParseTaskKind::Module, token); } JS_PUBLIC_API(bool) JS_CompileScript(JSContext* cx, const char* ascii, size_t length, const JS::CompileOptions& options, MutableHandleScript script) { return Compile(cx, options, ascii, length, script); } JS_PUBLIC_API(bool) JS_CompileUCScript(JSContext* cx, const char16_t* chars, size_t length, const JS::CompileOptions& options, MutableHandleScript script) { return Compile(cx, options, chars, length, script); } JS_PUBLIC_API(bool) JS_BufferIsCompilableUnit(JSContext* cx, HandleObject obj, const char* utf8, size_t length) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); cx->clearPendingException(); char16_t* chars = JS::UTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(utf8, length), &length).get(); if (!chars) return true; // Return true on any out-of-memory error or non-EOF-related syntax error, so our // caller doesn't try to collect more buffered source. bool result = true; CompileOptions options(cx); frontend::UsedNameTracker usedNames(cx); if (!usedNames.init()) return false; frontend::Parser parser(cx, cx->tempLifoAlloc(), options, chars, length, /* foldConstants = */ true, usedNames, nullptr, nullptr); JS::WarningReporter older = JS::SetWarningReporter(cx, nullptr); if (!parser.checkOptions() || !parser.parse()) { // We ran into an error. If it was because we ran out of source, we // return false so our caller knows to try to collect more buffered // source. if (parser.isUnexpectedEOF()) result = false; cx->clearPendingException(); } JS::SetWarningReporter(cx, older); js_free(chars); return result; } JS_PUBLIC_API(JSObject*) JS_GetGlobalFromScript(JSScript* script) { MOZ_ASSERT(!script->isCachedEval()); return &script->global(); } JS_PUBLIC_API(const char*) JS_GetScriptFilename(JSScript* script) { // This is called from ThreadStackHelper which can be called from another // thread or inside a signal hander, so we need to be careful in case a // copmacting GC is currently moving things around. return script->maybeForwardedFilename(); } JS_PUBLIC_API(unsigned) JS_GetScriptBaseLineNumber(JSContext* cx, JSScript* script) { return script->lineno(); } JS_PUBLIC_API(JSScript*) JS_GetFunctionScript(JSContext* cx, HandleFunction fun) { if (fun->isNative()) return nullptr; if (fun->isInterpretedLazy()) { AutoCompartment funCompartment(cx, fun); JSScript* script = JSFunction::getOrCreateScript(cx, fun); if (!script) MOZ_CRASH(); return script; } return fun->nonLazyScript(); } /* * enclosingScope is a scope, if any (e.g. a WithScope). If the scope is the * global scope, this must be null. * * enclosingEnv is an environment to use, if it's not the global. */ static bool CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, HandleAtom name, bool isInvalidName, SourceBufferHolder& srcBuf, uint32_t parameterListEnd, HandleObject enclosingEnv, HandleScope enclosingScope, MutableHandleFunction fun) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, enclosingEnv); RootedAtom funAtom(cx); fun.set(NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL, isInvalidName ? nullptr : name, /* proto = */ nullptr, gc::AllocKind::FUNCTION, TenuredObject, enclosingEnv)); if (!fun) return false; // Make sure the static scope chain matches up when we have a // non-syntactic scope. MOZ_ASSERT_IF(!IsGlobalLexicalEnvironment(enclosingEnv), enclosingScope->hasOnChain(ScopeKind::NonSyntactic)); if (!frontend::CompileStandaloneFunction(cx, fun, optionsArg, srcBuf, Some(parameterListEnd), enclosingScope)) { return false; } // When function name is not a valid identifier, the generated function // source in srcBuf doesn't have a function name. Set it here. if (isInvalidName) fun->setAtom(name); return true; } static MOZ_MUST_USE bool BuildFunctionString(const char* name, size_t nameLen, unsigned nargs, const char* const* argnames, const SourceBufferHolder& srcBuf, StringBuffer* out, uint32_t* parameterListEnd) { MOZ_ASSERT(out); MOZ_ASSERT(parameterListEnd); if (!out->ensureTwoByteChars()) return false; if (!out->append("function ")) return false; if (name) { if (!out->append(name, nameLen)) return false; } if (!out->append("(")) return false; for (unsigned i = 0; i < nargs; i++) { if (i != 0) { if (!out->append(", ")) return false; } if (!out->append(argnames[i], strlen(argnames[i]))) return false; } // Remember the position of ")". *parameterListEnd = out->length(); MOZ_ASSERT(FunctionConstructorMedialSigils[0] == ')'); if (!out->append(FunctionConstructorMedialSigils)) return false; if (!out->append(srcBuf.get(), srcBuf.length())) return false; if (!out->append(FunctionConstructorFinalBrace)) return false; return true; } JS_PUBLIC_API(bool) JS::CompileFunction(JSContext* cx, AutoObjectVector& envChain, const ReadOnlyCompileOptions& options, const char* name, unsigned nargs, const char* const* argnames, SourceBufferHolder& srcBuf, MutableHandleFunction fun) { RootedObject env(cx); RootedScope scope(cx); if (!CreateNonSyntacticEnvironmentChain(cx, envChain, &env, &scope)) return false; size_t nameLen = 0; bool isInvalidName = false; RootedAtom nameAtom(cx); if (name) { nameLen = strlen(name); nameAtom = Atomize(cx, name, nameLen); if (!nameAtom) return false; // If name is not valid identifier if (!js::frontend::IsIdentifier(name, nameLen)) isInvalidName = true; } uint32_t parameterListEnd; StringBuffer funStr(cx); if (!BuildFunctionString(isInvalidName ? nullptr : name, nameLen, nargs, argnames, srcBuf, &funStr, ¶meterListEnd)) { return false; } size_t newLen = funStr.length(); SourceBufferHolder newSrcBuf(funStr.stealChars(), newLen, SourceBufferHolder::GiveOwnership); return CompileFunction(cx, options, nameAtom, isInvalidName, newSrcBuf, parameterListEnd, env, scope, fun); } JS_PUBLIC_API(bool) JS::CompileFunction(JSContext* cx, AutoObjectVector& envChain, const ReadOnlyCompileOptions& options, const char* name, unsigned nargs, const char* const* argnames, const char16_t* chars, size_t length, MutableHandleFunction fun) { SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership); return CompileFunction(cx, envChain, options, name, nargs, argnames, srcBuf, fun); } JS_PUBLIC_API(bool) JS::CompileFunction(JSContext* cx, AutoObjectVector& envChain, const ReadOnlyCompileOptions& options, const char* name, unsigned nargs, const char* const* argnames, const char* bytes, size_t length, MutableHandleFunction fun) { UniqueTwoByteChars chars; if (options.utf8) chars.reset(UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get()); else chars.reset(InflateString(cx, bytes, &length)); if (!chars) return false; return CompileFunction(cx, envChain, options, name, nargs, argnames, chars.get(), length, fun); } JS_PUBLIC_API(JSString*) JS_DecompileScript(JSContext* cx, HandleScript script, const char* name, unsigned indent) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); script->ensureNonLazyCanonicalFunction(); RootedFunction fun(cx, script->functionNonDelazifying()); if (fun) return JS_DecompileFunction(cx, fun, indent); bool haveSource = script->scriptSource()->hasSourceData(); if (!haveSource && !JSScript::loadSource(cx, script->scriptSource(), &haveSource)) return nullptr; return haveSource ? JSScript::sourceData(cx, script) : NewStringCopyZ(cx, "[no source]"); } JS_PUBLIC_API(JSString*) JS_DecompileFunction(JSContext* cx, HandleFunction fun, unsigned indent) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, fun); return FunctionToString(cx, fun, !(indent & JS_DONT_PRETTY_PRINT)); } MOZ_NEVER_INLINE static bool ExecuteScript(JSContext* cx, HandleObject scope, HandleScript script, Value* rval) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, scope, script); MOZ_ASSERT_IF(!IsGlobalLexicalEnvironment(scope), script->hasNonSyntacticScope()); return Execute(cx, script, *scope, rval); } static bool ExecuteScript(JSContext* cx, AutoObjectVector& envChain, HandleScript scriptArg, Value* rval) { RootedObject env(cx); RootedScope dummy(cx); if (!CreateNonSyntacticEnvironmentChain(cx, envChain, &env, &dummy)) return false; RootedScript script(cx, scriptArg); if (!script->hasNonSyntacticScope() && !IsGlobalLexicalEnvironment(env)) { script = CloneGlobalScript(cx, ScopeKind::NonSyntactic, script); if (!script) return false; js::Debugger::onNewScript(cx, script); } return ExecuteScript(cx, env, script, rval); } MOZ_NEVER_INLINE JS_PUBLIC_API(bool) JS_ExecuteScript(JSContext* cx, HandleScript scriptArg, MutableHandleValue rval) { RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment()); return ExecuteScript(cx, globalLexical, scriptArg, rval.address()); } MOZ_NEVER_INLINE JS_PUBLIC_API(bool) JS_ExecuteScript(JSContext* cx, HandleScript scriptArg) { RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment()); return ExecuteScript(cx, globalLexical, scriptArg, nullptr); } MOZ_NEVER_INLINE JS_PUBLIC_API(bool) JS_ExecuteScript(JSContext* cx, AutoObjectVector& envChain, HandleScript scriptArg, MutableHandleValue rval) { return ExecuteScript(cx, envChain, scriptArg, rval.address()); } MOZ_NEVER_INLINE JS_PUBLIC_API(bool) JS_ExecuteScript(JSContext* cx, AutoObjectVector& envChain, HandleScript scriptArg) { return ExecuteScript(cx, envChain, scriptArg, nullptr); } JS_PUBLIC_API(bool) JS::CloneAndExecuteScript(JSContext* cx, HandleScript scriptArg, JS::MutableHandleValue rval) { CHECK_REQUEST(cx); RootedScript script(cx, scriptArg); RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment()); if (script->compartment() != cx->compartment()) { script = CloneGlobalScript(cx, ScopeKind::Global, script); if (!script) return false; js::Debugger::onNewScript(cx, script); } return ExecuteScript(cx, globalLexical, script, rval.address()); } static const unsigned LARGE_SCRIPT_LENGTH = 500*1024; static bool Evaluate(JSContext* cx, ScopeKind scopeKind, HandleObject env, const ReadOnlyCompileOptions& optionsArg, SourceBufferHolder& srcBuf, MutableHandleValue rval) { CompileOptions options(cx, optionsArg); MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, env); MOZ_ASSERT_IF(!IsGlobalLexicalEnvironment(env), scopeKind == ScopeKind::NonSyntactic); options.setIsRunOnce(true); SourceCompressionTask sct(cx); RootedScript script(cx, frontend::CompileGlobalScript(cx, cx->tempLifoAlloc(), scopeKind, options, srcBuf, &sct)); if (!script) return false; MOZ_ASSERT(script->getVersion() == options.version); bool result = Execute(cx, script, *env, options.noScriptRval ? nullptr : rval.address()); if (!sct.complete()) result = false; // After evaluation, the compiled script will not be run again. // script->ensureRanAnalysis allocated 1 analyze::Bytecode for every opcode // which for large scripts means significant memory. Perform a GC eagerly // to clear out this analysis data before anything happens to inhibit the // flushing of this memory (such as setting requestAnimationFrame). if (script->length() > LARGE_SCRIPT_LENGTH) { script = nullptr; PrepareZoneForGC(cx->zone()); cx->runtime()->gc.gc(GC_NORMAL, JS::gcreason::FINISH_LARGE_EVALUATE); } return result; } static bool Evaluate(JSContext* cx, AutoObjectVector& envChain, const ReadOnlyCompileOptions& optionsArg, SourceBufferHolder& srcBuf, MutableHandleValue rval) { RootedObject env(cx); RootedScope scope(cx); if (!CreateNonSyntacticEnvironmentChain(cx, envChain, &env, &scope)) return false; return ::Evaluate(cx, scope->kind(), env, optionsArg, srcBuf, rval); } static bool Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, const char16_t* chars, size_t length, MutableHandleValue rval) { SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership); RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment()); return ::Evaluate(cx, ScopeKind::Global, globalLexical, optionsArg, srcBuf, rval); } extern JS_PUBLIC_API(bool) JS::Evaluate(JSContext* cx, const ReadOnlyCompileOptions& options, const char* bytes, size_t length, MutableHandleValue rval) { char16_t* chars; if (options.utf8) chars = UTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(bytes, length), &length).get(); else chars = InflateString(cx, bytes, &length); if (!chars) return false; SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::GiveOwnership); RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment()); bool ok = ::Evaluate(cx, ScopeKind::Global, globalLexical, options, srcBuf, rval); return ok; } static bool Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, const char* filename, MutableHandleValue rval) { FileContents buffer(cx); { AutoFile file; if (!file.open(cx, filename) || !file.readAll(cx, buffer)) return false; } CompileOptions options(cx, optionsArg); options.setFileAndLine(filename, 1); return Evaluate(cx, options, buffer.begin(), buffer.length(), rval); } JS_PUBLIC_API(bool) JS::Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, SourceBufferHolder& srcBuf, MutableHandleValue rval) { RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment()); return ::Evaluate(cx, ScopeKind::Global, globalLexical, optionsArg, srcBuf, rval); } JS_PUBLIC_API(bool) JS::Evaluate(JSContext* cx, AutoObjectVector& envChain, const ReadOnlyCompileOptions& optionsArg, SourceBufferHolder& srcBuf, MutableHandleValue rval) { return ::Evaluate(cx, envChain, optionsArg, srcBuf, rval); } JS_PUBLIC_API(bool) JS::Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, const char16_t* chars, size_t length, MutableHandleValue rval) { return ::Evaluate(cx, optionsArg, chars, length, rval); } JS_PUBLIC_API(bool) JS::Evaluate(JSContext* cx, AutoObjectVector& envChain, const ReadOnlyCompileOptions& optionsArg, const char16_t* chars, size_t length, MutableHandleValue rval) { SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership); return ::Evaluate(cx, envChain, optionsArg, srcBuf, rval); } JS_PUBLIC_API(bool) JS::Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, const char* filename, MutableHandleValue rval) { return ::Evaluate(cx, optionsArg, filename, rval); } JS_PUBLIC_API(JSFunction*) JS::GetModuleResolveHook(JSContext* cx) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return cx->global()->moduleResolveHook(); } JS_PUBLIC_API(void) JS::SetModuleResolveHook(JSContext* cx, HandleFunction func) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, func); cx->global()->setModuleResolveHook(func); } JS_PUBLIC_API(bool) JS::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf, JS::MutableHandleObject module) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); module.set(frontend::CompileModule(cx, options, srcBuf)); return !!module; } JS_PUBLIC_API(void) JS::SetModuleHostDefinedField(JSObject* module, const JS::Value& value) { module->as().setHostDefinedField(value); } JS_PUBLIC_API(JS::Value) JS::GetModuleHostDefinedField(JSObject* module) { return module->as().hostDefinedField(); } JS_PUBLIC_API(bool) JS::ModuleDeclarationInstantiation(JSContext* cx, JS::HandleObject moduleArg) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, moduleArg); return ModuleObject::DeclarationInstantiation(cx, moduleArg.as()); } JS_PUBLIC_API(bool) JS::ModuleEvaluation(JSContext* cx, JS::HandleObject moduleArg) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, moduleArg); return ModuleObject::Evaluation(cx, moduleArg.as()); } JS_PUBLIC_API(JSObject*) JS::GetRequestedModules(JSContext* cx, JS::HandleObject moduleArg) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, moduleArg); return &moduleArg->as().requestedModules(); } JS_PUBLIC_API(JSScript*) JS::GetModuleScript(JSContext* cx, JS::HandleObject moduleArg) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, moduleArg); return moduleArg->as().script(); } JS_PUBLIC_API(JSObject*) JS_New(JSContext* cx, HandleObject ctor, const JS::HandleValueArray& inputArgs) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, ctor, inputArgs); RootedValue ctorVal(cx, ObjectValue(*ctor)); if (!IsConstructor(ctorVal)) { ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, ctorVal, nullptr); return nullptr; } ConstructArgs args(cx); if (!FillArgumentsFromArraylike(cx, args, inputArgs)) return nullptr; RootedObject obj(cx); if (!js::Construct(cx, ctorVal, args, ctorVal, &obj)) return nullptr; return obj; } JS_PUBLIC_API(bool) JS_CheckForInterrupt(JSContext* cx) { return js::CheckForInterrupt(cx); } JS_PUBLIC_API(bool) JS_AddInterruptCallback(JSContext* cx, JSInterruptCallback callback) { return cx->interruptCallbacks.append(callback); } JS_PUBLIC_API(bool) JS_DisableInterruptCallback(JSContext* cx) { bool result = cx->interruptCallbackDisabled; cx->interruptCallbackDisabled = true; return result; } JS_PUBLIC_API(void) JS_ResetInterruptCallback(JSContext* cx, bool enable) { cx->interruptCallbackDisabled = enable; } /************************************************************************/ /* * Promises. */ JS_PUBLIC_API(void) JS::SetGetIncumbentGlobalCallback(JSContext* cx, JSGetIncumbentGlobalCallback callback) { cx->getIncumbentGlobalCallback = callback; } JS_PUBLIC_API(void) JS::SetEnqueuePromiseJobCallback(JSContext* cx, JSEnqueuePromiseJobCallback callback, void* data /* = nullptr */) { cx->enqueuePromiseJobCallback = callback; cx->enqueuePromiseJobCallbackData = data; } extern JS_PUBLIC_API(void) JS::SetPromiseRejectionTrackerCallback(JSContext* cx, JSPromiseRejectionTrackerCallback callback, void* data /* = nullptr */) { cx->promiseRejectionTrackerCallback = callback; cx->promiseRejectionTrackerCallbackData = data; } JS_PUBLIC_API(JSObject*) JS::NewPromiseObject(JSContext* cx, HandleObject executor, HandleObject proto /* = nullptr */) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); MOZ_ASSERT(IsCallable(executor)); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, executor, proto); return PromiseObject::create(cx, executor, proto); } JS_PUBLIC_API(bool) JS::IsPromiseObject(JS::HandleObject obj) { return obj->is(); } JS_PUBLIC_API(JSObject*) JS::GetPromiseConstructor(JSContext* cx) { CHECK_REQUEST(cx); Rooted global(cx, cx->global()); return GlobalObject::getOrCreatePromiseConstructor(cx, global); } JS_PUBLIC_API(JSObject*) JS::GetPromisePrototype(JSContext* cx) { CHECK_REQUEST(cx); Rooted global(cx, cx->global()); return GlobalObject::getOrCreatePromisePrototype(cx, global); } JS_PUBLIC_API(JS::PromiseState) JS::GetPromiseState(JS::HandleObject promiseObj_) { JSObject* promiseObj = CheckedUnwrap(promiseObj_); if (!promiseObj || !promiseObj->is()) return JS::PromiseState::Pending; return promiseObj->as().state(); } JS_PUBLIC_API(uint64_t) JS::GetPromiseID(JS::HandleObject promise) { return promise->as().getID(); } JS_PUBLIC_API(JS::Value) JS::GetPromiseResult(JS::HandleObject promiseObj) { PromiseObject* promise = &promiseObj->as(); MOZ_ASSERT(promise->state() != JS::PromiseState::Pending); return promise->state() == JS::PromiseState::Fulfilled ? promise->value() : promise->reason(); } JS_PUBLIC_API(JSObject*) JS::GetPromiseAllocationSite(JS::HandleObject promise) { return promise->as().allocationSite(); } JS_PUBLIC_API(JSObject*) JS::GetPromiseResolutionSite(JS::HandleObject promise) { return promise->as().resolutionSite(); } #ifdef DEBUG JS_PUBLIC_API(void) JS::DumpPromiseAllocationSite(JSContext* cx, JS::HandleObject promise) { RootedObject stack(cx, promise->as().allocationSite()); UniqueChars stackStr(reinterpret_cast(BuildUTF8StackString(cx, stack).get())); if (stackStr.get()) fputs(stackStr.get(), stderr); } JS_PUBLIC_API(void) JS::DumpPromiseResolutionSite(JSContext* cx, JS::HandleObject promise) { RootedObject stack(cx, promise->as().resolutionSite()); UniqueChars stackStr(reinterpret_cast(BuildUTF8StackString(cx, stack).get())); if (stackStr.get()) fputs(stackStr.get(), stderr); } #endif JS_PUBLIC_API(JSObject*) JS::CallOriginalPromiseResolve(JSContext* cx, JS::HandleValue resolutionValue) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, resolutionValue); RootedObject promise(cx, PromiseObject::unforgeableResolve(cx, resolutionValue)); MOZ_ASSERT_IF(promise, CheckedUnwrap(promise)->is()); return promise; } JS_PUBLIC_API(JSObject*) JS::CallOriginalPromiseReject(JSContext* cx, JS::HandleValue rejectionValue) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, rejectionValue); RootedObject promise(cx, PromiseObject::unforgeableReject(cx, rejectionValue)); MOZ_ASSERT_IF(promise, CheckedUnwrap(promise)->is()); return promise; } static bool ResolveOrRejectPromise(JSContext* cx, JS::HandleObject promiseObj, JS::HandleValue resultOrReason_, bool reject) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, promiseObj, resultOrReason_); mozilla::Maybe ac; Rooted promise(cx); RootedValue resultOrReason(cx, resultOrReason_); if (IsWrapper(promiseObj)) { JSObject* unwrappedPromiseObj = CheckedUnwrap(promiseObj); if (!unwrappedPromiseObj) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OBJECT_ACCESS_DENIED); return false; } promise = &unwrappedPromiseObj->as(); ac.emplace(cx, promise); if (!cx->compartment()->wrap(cx, &resultOrReason)) return false; } else { promise = promiseObj.as(); } return reject ? PromiseObject::reject(cx, promise, resultOrReason) : PromiseObject::resolve(cx, promise, resultOrReason); } JS_PUBLIC_API(bool) JS::ResolvePromise(JSContext* cx, JS::HandleObject promiseObj, JS::HandleValue resolutionValue) { return ResolveOrRejectPromise(cx, promiseObj, resolutionValue, false); } JS_PUBLIC_API(bool) JS::RejectPromise(JSContext* cx, JS::HandleObject promiseObj, JS::HandleValue rejectionValue) { return ResolveOrRejectPromise(cx, promiseObj, rejectionValue, true); } static bool CallOriginalPromiseThenImpl(JSContext* cx, JS::HandleObject promiseObj, JS::HandleObject onResolvedObj_, JS::HandleObject onRejectedObj_, JS::MutableHandleObject resultObj, bool createDependent) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, promiseObj, onResolvedObj_, onRejectedObj_); MOZ_ASSERT_IF(onResolvedObj_, IsCallable(onResolvedObj_)); MOZ_ASSERT_IF(onRejectedObj_, IsCallable(onRejectedObj_)); { mozilla::Maybe ac; Rooted promise(cx); RootedObject onResolvedObj(cx, onResolvedObj_); RootedObject onRejectedObj(cx, onRejectedObj_); if (IsWrapper(promiseObj)) { JSObject* unwrappedPromiseObj = CheckedUnwrap(promiseObj); if (!unwrappedPromiseObj) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OBJECT_ACCESS_DENIED); return false; } promise = &unwrappedPromiseObj->as(); ac.emplace(cx, promise); if (!cx->compartment()->wrap(cx, &onResolvedObj) || !cx->compartment()->wrap(cx, &onRejectedObj)) { return false; } } else { promise = promiseObj.as(); } RootedValue onFulfilled(cx, ObjectOrNullValue(onResolvedObj)); RootedValue onRejected(cx, ObjectOrNullValue(onRejectedObj)); if (!OriginalPromiseThen(cx, promise, onFulfilled, onRejected, resultObj, createDependent)) return false; } if (resultObj) { if (!cx->compartment()->wrap(cx, resultObj)) return false; } return true; } JS_PUBLIC_API(JSObject*) JS::CallOriginalPromiseThen(JSContext* cx, JS::HandleObject promiseObj, JS::HandleObject onResolvedObj, JS::HandleObject onRejectedObj) { RootedObject resultPromise(cx); if (!CallOriginalPromiseThenImpl(cx, promiseObj, onResolvedObj, onRejectedObj, &resultPromise, true)) return nullptr; return resultPromise; } JS_PUBLIC_API(bool) JS::AddPromiseReactions(JSContext* cx, JS::HandleObject promiseObj, JS::HandleObject onResolvedObj, JS::HandleObject onRejectedObj) { RootedObject resultPromise(cx); bool result = CallOriginalPromiseThenImpl(cx, promiseObj, onResolvedObj, onRejectedObj, &resultPromise, false); MOZ_ASSERT(!resultPromise); return result; } /** * Unforgeable version of Promise.all for internal use. * * Takes a dense array of Promise objects and returns a promise that's * resolved with an array of resolution values when all those promises ahve * been resolved, or rejected with the rejection value of the first rejected * promise. * * Asserts that the array is dense and all entries are Promise objects. */ JS_PUBLIC_API(JSObject*) JS::GetWaitForAllPromise(JSContext* cx, const JS::AutoObjectVector& promises) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return js::GetWaitForAllPromise(cx, promises); } JS_PUBLIC_API(void) JS::SetAsyncTaskCallbacks(JSContext* cx, JS::StartAsyncTaskCallback start, JS::FinishAsyncTaskCallback finish) { cx->startAsyncTaskCallback = start; cx->finishAsyncTaskCallback = finish; } JS_PUBLIC_API(void) JS_RequestInterruptCallback(JSContext* cx) { cx->requestInterrupt(JSRuntime::RequestInterruptUrgent); } JS_PUBLIC_API(bool) JS_IsRunning(JSContext* cx) { return cx->currentlyRunning(); } JS::AutoSetAsyncStackForNewCalls::AutoSetAsyncStackForNewCalls( JSContext* cx, HandleObject stack, const char* asyncCause, JS::AutoSetAsyncStackForNewCalls::AsyncCallKind kind) : cx(cx), oldAsyncStack(cx, cx->asyncStackForNewActivations), oldAsyncCause(cx->asyncCauseForNewActivations), oldAsyncCallIsExplicit(cx->asyncCallIsExplicit) { CHECK_REQUEST(cx); // The option determines whether we actually use the new values at this // point. It will not affect restoring the previous values when the object // is destroyed, so if the option changes it won't cause consistency issues. if (!cx->options().asyncStack()) return; SavedFrame* asyncStack = &stack->as(); cx->asyncStackForNewActivations = asyncStack; cx->asyncCauseForNewActivations = asyncCause; cx->asyncCallIsExplicit = kind == AsyncCallKind::EXPLICIT; } JS::AutoSetAsyncStackForNewCalls::~AutoSetAsyncStackForNewCalls() { cx->asyncCauseForNewActivations = oldAsyncCause; cx->asyncStackForNewActivations = oldAsyncStack ? &oldAsyncStack->as() : nullptr; cx->asyncCallIsExplicit = oldAsyncCallIsExplicit; } /************************************************************************/ JS_PUBLIC_API(JSString*) JS_NewStringCopyN(JSContext* cx, const char* s, size_t n) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return NewStringCopyN(cx, s, n); } JS_PUBLIC_API(JSString*) JS_NewStringCopyZ(JSContext* cx, const char* s) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); if (!s) return cx->runtime()->emptyString; return NewStringCopyZ(cx, s); } JS_PUBLIC_API(JSString*) JS_NewStringCopyUTF8Z(JSContext* cx, const JS::ConstUTF8CharsZ s) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return NewStringCopyUTF8Z(cx, s); } JS_PUBLIC_API(JSString*) JS_NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars s) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return NewStringCopyUTF8N(cx, s); } JS_PUBLIC_API(bool) JS_StringHasBeenPinned(JSContext* cx, JSString* str) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); if (!str->isAtom()) return false; return AtomIsPinned(cx, &str->asAtom()); } JS_PUBLIC_API(jsid) INTERNED_STRING_TO_JSID(JSContext* cx, JSString* str) { MOZ_ASSERT(str); MOZ_ASSERT(((size_t)str & JSID_TYPE_MASK) == 0); MOZ_ASSERT_IF(cx, JS_StringHasBeenPinned(cx, str)); return AtomToId(&str->asAtom()); } JS_PUBLIC_API(JSString*) JS_AtomizeAndPinJSString(JSContext* cx, HandleString str) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); JSAtom* atom = AtomizeString(cx, str, PinAtom); MOZ_ASSERT_IF(atom, JS_StringHasBeenPinned(cx, atom)); return atom; } JS_PUBLIC_API(JSString*) JS_AtomizeString(JSContext* cx, const char* s) { return JS_AtomizeStringN(cx, s, strlen(s)); } JS_PUBLIC_API(JSString*) JS_AtomizeStringN(JSContext* cx, const char* s, size_t length) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return Atomize(cx, s, length, DoNotPinAtom); } JS_PUBLIC_API(JSString*) JS_AtomizeAndPinString(JSContext* cx, const char* s) { return JS_AtomizeAndPinStringN(cx, s, strlen(s)); } JS_PUBLIC_API(JSString*) JS_AtomizeAndPinStringN(JSContext* cx, const char* s, size_t length) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); JSAtom* atom = Atomize(cx, s, length, PinAtom); MOZ_ASSERT_IF(atom, JS_StringHasBeenPinned(cx, atom)); return atom; } JS_PUBLIC_API(JSString*) JS_NewUCString(JSContext* cx, char16_t* chars, size_t length) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return NewString(cx, chars, length); } JS_PUBLIC_API(JSString*) JS_NewUCStringCopyN(JSContext* cx, const char16_t* s, size_t n) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); if (!n) return cx->names().empty; return NewStringCopyN(cx, s, n); } JS_PUBLIC_API(JSString*) JS_NewUCStringCopyZ(JSContext* cx, const char16_t* s) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); if (!s) return cx->runtime()->emptyString; return NewStringCopyZ(cx, s); } JS_PUBLIC_API(JSString*) JS_AtomizeUCString(JSContext* cx, const char16_t* s) { return JS_AtomizeUCStringN(cx, s, js_strlen(s)); } JS_PUBLIC_API(JSString*) JS_AtomizeUCStringN(JSContext* cx, const char16_t* s, size_t length) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return AtomizeChars(cx, s, length, DoNotPinAtom); } JS_PUBLIC_API(JSString*) JS_AtomizeAndPinUCStringN(JSContext* cx, const char16_t* s, size_t length) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); JSAtom* atom = AtomizeChars(cx, s, length, PinAtom); MOZ_ASSERT_IF(atom, JS_StringHasBeenPinned(cx, atom)); return atom; } JS_PUBLIC_API(JSString*) JS_AtomizeAndPinUCString(JSContext* cx, const char16_t* s) { return JS_AtomizeAndPinUCStringN(cx, s, js_strlen(s)); } JS_PUBLIC_API(size_t) JS_GetStringLength(JSString* str) { return str->length(); } JS_PUBLIC_API(bool) JS_StringIsFlat(JSString* str) { return str->isFlat(); } JS_PUBLIC_API(bool) JS_StringHasLatin1Chars(JSString* str) { return str->hasLatin1Chars(); } JS_PUBLIC_API(const JS::Latin1Char*) JS_GetLatin1StringCharsAndLength(JSContext* cx, const JS::AutoCheckCannotGC& nogc, JSString* str, size_t* plength) { MOZ_ASSERT(plength); AssertHeapIsIdleOrStringIsFlat(cx, str); CHECK_REQUEST(cx); assertSameCompartment(cx, str); JSLinearString* linear = str->ensureLinear(cx); if (!linear) return nullptr; *plength = linear->length(); return linear->latin1Chars(nogc); } JS_PUBLIC_API(const char16_t*) JS_GetTwoByteStringCharsAndLength(JSContext* cx, const JS::AutoCheckCannotGC& nogc, JSString* str, size_t* plength) { MOZ_ASSERT(plength); AssertHeapIsIdleOrStringIsFlat(cx, str); CHECK_REQUEST(cx); assertSameCompartment(cx, str); JSLinearString* linear = str->ensureLinear(cx); if (!linear) return nullptr; *plength = linear->length(); return linear->twoByteChars(nogc); } JS_PUBLIC_API(const char16_t*) JS_GetTwoByteExternalStringChars(JSString* str) { return str->asExternal().twoByteChars(); } JS_PUBLIC_API(bool) JS_GetStringCharAt(JSContext* cx, JSString* str, size_t index, char16_t* res) { AssertHeapIsIdleOrStringIsFlat(cx, str); CHECK_REQUEST(cx); assertSameCompartment(cx, str); JSLinearString* linear = str->ensureLinear(cx); if (!linear) return false; *res = linear->latin1OrTwoByteChar(index); return true; } JS_PUBLIC_API(char16_t) JS_GetFlatStringCharAt(JSFlatString* str, size_t index) { return str->latin1OrTwoByteChar(index); } JS_PUBLIC_API(bool) JS_CopyStringChars(JSContext* cx, mozilla::Range dest, JSString* str) { AssertHeapIsIdleOrStringIsFlat(cx, str); CHECK_REQUEST(cx); assertSameCompartment(cx, str); JSLinearString* linear = str->ensureLinear(cx); if (!linear) return false; MOZ_ASSERT(linear->length() <= dest.length()); CopyChars(dest.begin().get(), *linear); return true; } JS_PUBLIC_API(const Latin1Char*) JS_GetLatin1InternedStringChars(const JS::AutoCheckCannotGC& nogc, JSString* str) { MOZ_ASSERT(str->isAtom()); JSFlatString* flat = str->ensureFlat(nullptr); if (!flat) return nullptr; return flat->latin1Chars(nogc); } JS_PUBLIC_API(const char16_t*) JS_GetTwoByteInternedStringChars(const JS::AutoCheckCannotGC& nogc, JSString* str) { MOZ_ASSERT(str->isAtom()); JSFlatString* flat = str->ensureFlat(nullptr); if (!flat) return nullptr; return flat->twoByteChars(nogc); } extern JS_PUBLIC_API(JSFlatString*) JS_FlattenString(JSContext* cx, JSString* str) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, str); JSFlatString* flat = str->ensureFlat(cx); if (!flat) return nullptr; return flat; } extern JS_PUBLIC_API(const Latin1Char*) JS_GetLatin1FlatStringChars(const JS::AutoCheckCannotGC& nogc, JSFlatString* str) { return str->latin1Chars(nogc); } extern JS_PUBLIC_API(const char16_t*) JS_GetTwoByteFlatStringChars(const JS::AutoCheckCannotGC& nogc, JSFlatString* str) { return str->twoByteChars(nogc); } JS_PUBLIC_API(bool) JS_CompareStrings(JSContext* cx, JSString* str1, JSString* str2, int32_t* result) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return CompareStrings(cx, str1, str2, result); } JS_PUBLIC_API(bool) JS_StringEqualsAscii(JSContext* cx, JSString* str, const char* asciiBytes, bool* match) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); JSLinearString* linearStr = str->ensureLinear(cx); if (!linearStr) return false; *match = StringEqualsAscii(linearStr, asciiBytes); return true; } JS_PUBLIC_API(bool) JS_FlatStringEqualsAscii(JSFlatString* str, const char* asciiBytes) { return StringEqualsAscii(str, asciiBytes); } JS_PUBLIC_API(size_t) JS_PutEscapedFlatString(char* buffer, size_t size, JSFlatString* str, char quote) { return PutEscapedString(buffer, size, str, quote); } JS_PUBLIC_API(size_t) JS_PutEscapedString(JSContext* cx, char* buffer, size_t size, JSString* str, char quote) { AssertHeapIsIdle(cx); JSLinearString* linearStr = str->ensureLinear(cx); if (!linearStr) return size_t(-1); return PutEscapedString(buffer, size, linearStr, quote); } JS_PUBLIC_API(bool) JS_FileEscapedString(FILE* fp, JSString* str, char quote) { JSLinearString* linearStr = str->ensureLinear(nullptr); return linearStr && FileEscapedString(fp, linearStr, quote); } JS_PUBLIC_API(JSString*) JS_NewDependentString(JSContext* cx, HandleString str, size_t start, size_t length) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return NewDependentString(cx, str, start, length); } JS_PUBLIC_API(JSString*) JS_ConcatStrings(JSContext* cx, HandleString left, HandleString right) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return ConcatStrings(cx, left, right); } JS_PUBLIC_API(bool) JS_DecodeBytes(JSContext* cx, const char* src, size_t srclen, char16_t* dst, size_t* dstlenp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); if (!dst) { *dstlenp = srclen; return true; } size_t dstlen = *dstlenp; if (srclen > dstlen) { CopyAndInflateChars(dst, src, dstlen); AutoSuppressGC suppress(cx); JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BUFFER_TOO_SMALL); return false; } CopyAndInflateChars(dst, src, srclen); *dstlenp = srclen; return true; } static char* EncodeLatin1(ExclusiveContext* cx, JSString* str) { JSLinearString* linear = str->ensureLinear(cx); if (!linear) return nullptr; JS::AutoCheckCannotGC nogc; if (linear->hasTwoByteChars()) return JS::LossyTwoByteCharsToNewLatin1CharsZ(cx, linear->twoByteRange(nogc)).c_str(); size_t len = str->length(); Latin1Char* buf = cx->pod_malloc(len + 1); if (!buf) { ReportOutOfMemory(cx); return nullptr; } mozilla::PodCopy(buf, linear->latin1Chars(nogc), len); buf[len] = '\0'; return reinterpret_cast(buf); } JS_PUBLIC_API(char*) JS_EncodeString(JSContext* cx, JSString* str) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return EncodeLatin1(cx, str); } JS_PUBLIC_API(char*) JS_EncodeStringToUTF8(JSContext* cx, HandleString str) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return StringToNewUTF8CharsZ(cx, *str).release(); } JS_PUBLIC_API(size_t) JS_GetStringEncodingLength(JSContext* cx, JSString* str) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); if (!str->ensureLinear(cx)) return size_t(-1); return str->length(); } JS_PUBLIC_API(size_t) JS_EncodeStringToBuffer(JSContext* cx, JSString* str, char* buffer, size_t length) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); /* * FIXME bug 612141 - fix DeflateStringToBuffer interface so the result * would allow to distinguish between insufficient buffer and encoding * error. */ size_t writtenLength = length; JSLinearString* linear = str->ensureLinear(cx); if (!linear) return size_t(-1); bool res; if (linear->hasLatin1Chars()) { JS::AutoCheckCannotGC nogc; res = DeflateStringToBuffer(nullptr, linear->latin1Chars(nogc), linear->length(), buffer, &writtenLength); } else { JS::AutoCheckCannotGC nogc; res = DeflateStringToBuffer(nullptr, linear->twoByteChars(nogc), linear->length(), buffer, &writtenLength); } if (res) { MOZ_ASSERT(writtenLength <= length); return writtenLength; } MOZ_ASSERT(writtenLength <= length); size_t necessaryLength = str->length(); if (necessaryLength == size_t(-1)) return size_t(-1); MOZ_ASSERT(writtenLength == length); // C strings are NOT encoded. return necessaryLength; } JS_PUBLIC_API(JS::Symbol*) JS::NewSymbol(JSContext* cx, HandleString description) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); if (description) assertSameCompartment(cx, description); return Symbol::new_(cx, SymbolCode::UniqueSymbol, description); } JS_PUBLIC_API(JS::Symbol*) JS::GetSymbolFor(JSContext* cx, HandleString key) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, key); return Symbol::for_(cx, key); } JS_PUBLIC_API(JSString*) JS::GetSymbolDescription(HandleSymbol symbol) { return symbol->description(); } JS_PUBLIC_API(JS::SymbolCode) JS::GetSymbolCode(Handle symbol) { return symbol->code(); } JS_PUBLIC_API(JS::Symbol*) JS::GetWellKnownSymbol(JSContext* cx, JS::SymbolCode which) { return cx->wellKnownSymbols().get(uint32_t(which)); } #ifdef DEBUG static bool PropertySpecNameIsDigits(const char* s) { if (JS::PropertySpecNameIsSymbol(s)) return false; if (!*s) return false; for (; *s; s++) { if (*s < '0' || *s > '9') return false; } return true; } #endif // DEBUG JS_PUBLIC_API(bool) JS::PropertySpecNameEqualsId(const char* name, HandleId id) { if (JS::PropertySpecNameIsSymbol(name)) { if (!JSID_IS_SYMBOL(id)) return false; Symbol* sym = JSID_TO_SYMBOL(id); return sym->isWellKnownSymbol() && sym->code() == PropertySpecNameToSymbolCode(name); } MOZ_ASSERT(!PropertySpecNameIsDigits(name)); return JSID_IS_ATOM(id) && JS_FlatStringEqualsAscii(JSID_TO_ATOM(id), name); } JS_PUBLIC_API(bool) JS_Stringify(JSContext* cx, MutableHandleValue vp, HandleObject replacer, HandleValue space, JSONWriteCallback callback, void* data) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, replacer, space); StringBuffer sb(cx); if (!sb.ensureTwoByteChars()) return false; if (!Stringify(cx, vp, replacer, space, sb, StringifyBehavior::Normal)) return false; if (sb.empty() && !sb.append(cx->names().null)) return false; return callback(sb.rawTwoByteBegin(), sb.length(), data); } JS_PUBLIC_API(bool) JS::ToJSONMaybeSafely(JSContext* cx, JS::HandleObject input, JSONWriteCallback callback, void* data) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, input); StringBuffer sb(cx); if (!sb.ensureTwoByteChars()) return false; RootedValue inputValue(cx, ObjectValue(*input)); if (!Stringify(cx, &inputValue, nullptr, NullHandleValue, sb, StringifyBehavior::RestrictedSafe)) return false; if (sb.empty() && !sb.append(cx->names().null)) return false; return callback(sb.rawTwoByteBegin(), sb.length(), data); } JS_PUBLIC_API(bool) JS_ParseJSON(JSContext* cx, const char16_t* chars, uint32_t len, MutableHandleValue vp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return ParseJSONWithReviver(cx, mozilla::Range(chars, len), NullHandleValue, vp); } JS_PUBLIC_API(bool) JS_ParseJSON(JSContext* cx, HandleString str, MutableHandleValue vp) { return JS_ParseJSONWithReviver(cx, str, NullHandleValue, vp); } JS_PUBLIC_API(bool) JS_ParseJSONWithReviver(JSContext* cx, const char16_t* chars, uint32_t len, HandleValue reviver, MutableHandleValue vp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return ParseJSONWithReviver(cx, mozilla::Range(chars, len), reviver, vp); } JS_PUBLIC_API(bool) JS_ParseJSONWithReviver(JSContext* cx, HandleString str, HandleValue reviver, MutableHandleValue vp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, str); AutoStableStringChars stableChars(cx); if (!stableChars.init(cx, str)) return false; return stableChars.isLatin1() ? ParseJSONWithReviver(cx, stableChars.latin1Range(), reviver, vp) : ParseJSONWithReviver(cx, stableChars.twoByteRange(), reviver, vp); } /************************************************************************/ JS_PUBLIC_API(void) JS_ReportErrorASCII(JSContext* cx, const char* format, ...) { va_list ap; AssertHeapIsIdle(cx); va_start(ap, format); ReportErrorVA(cx, JSREPORT_ERROR, format, ArgumentsAreASCII, ap); va_end(ap); } JS_PUBLIC_API(void) JS_ReportErrorLatin1(JSContext* cx, const char* format, ...) { va_list ap; AssertHeapIsIdle(cx); va_start(ap, format); ReportErrorVA(cx, JSREPORT_ERROR, format, ArgumentsAreLatin1, ap); va_end(ap); } JS_PUBLIC_API(void) JS_ReportErrorUTF8(JSContext* cx, const char* format, ...) { va_list ap; AssertHeapIsIdle(cx); va_start(ap, format); ReportErrorVA(cx, JSREPORT_ERROR, format, ArgumentsAreUTF8, ap); va_end(ap); } JS_PUBLIC_API(void) JS_ReportErrorNumberASCII(JSContext* cx, JSErrorCallback errorCallback, void* userRef, const unsigned errorNumber, ...) { va_list ap; va_start(ap, errorNumber); JS_ReportErrorNumberASCIIVA(cx, errorCallback, userRef, errorNumber, ap); va_end(ap); } JS_PUBLIC_API(void) JS_ReportErrorNumberASCIIVA(JSContext* cx, JSErrorCallback errorCallback, void* userRef, const unsigned errorNumber, va_list ap) { AssertHeapIsIdle(cx); ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef, errorNumber, ArgumentsAreASCII, ap); } JS_PUBLIC_API(void) JS_ReportErrorNumberLatin1(JSContext* cx, JSErrorCallback errorCallback, void* userRef, const unsigned errorNumber, ...) { va_list ap; va_start(ap, errorNumber); JS_ReportErrorNumberLatin1VA(cx, errorCallback, userRef, errorNumber, ap); va_end(ap); } JS_PUBLIC_API(void) JS_ReportErrorNumberLatin1VA(JSContext* cx, JSErrorCallback errorCallback, void* userRef, const unsigned errorNumber, va_list ap) { AssertHeapIsIdle(cx); ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef, errorNumber, ArgumentsAreLatin1, ap); } JS_PUBLIC_API(void) JS_ReportErrorNumberUTF8(JSContext* cx, JSErrorCallback errorCallback, void* userRef, const unsigned errorNumber, ...) { va_list ap; va_start(ap, errorNumber); JS_ReportErrorNumberUTF8VA(cx, errorCallback, userRef, errorNumber, ap); va_end(ap); } JS_PUBLIC_API(void) JS_ReportErrorNumberUTF8VA(JSContext* cx, JSErrorCallback errorCallback, void* userRef, const unsigned errorNumber, va_list ap) { AssertHeapIsIdle(cx); ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef, errorNumber, ArgumentsAreUTF8, ap); } JS_PUBLIC_API(void) JS_ReportErrorNumberUC(JSContext* cx, JSErrorCallback errorCallback, void* userRef, const unsigned errorNumber, ...) { va_list ap; AssertHeapIsIdle(cx); va_start(ap, errorNumber); ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef, errorNumber, ArgumentsAreUnicode, ap); va_end(ap); } JS_PUBLIC_API(void) JS_ReportErrorNumberUCArray(JSContext* cx, JSErrorCallback errorCallback, void* userRef, const unsigned errorNumber, const char16_t** args) { AssertHeapIsIdle(cx); ReportErrorNumberUCArray(cx, JSREPORT_ERROR, errorCallback, userRef, errorNumber, args); } JS_PUBLIC_API(bool) JS_ReportWarningASCII(JSContext* cx, const char* format, ...) { va_list ap; bool ok; AssertHeapIsIdle(cx); va_start(ap, format); ok = ReportErrorVA(cx, JSREPORT_WARNING, format, ArgumentsAreASCII, ap); va_end(ap); return ok; } JS_PUBLIC_API(bool) JS_ReportWarningLatin1(JSContext* cx, const char* format, ...) { va_list ap; bool ok; AssertHeapIsIdle(cx); va_start(ap, format); ok = ReportErrorVA(cx, JSREPORT_WARNING, format, ArgumentsAreLatin1, ap); va_end(ap); return ok; } JS_PUBLIC_API(bool) JS_ReportWarningUTF8(JSContext* cx, const char* format, ...) { va_list ap; bool ok; AssertHeapIsIdle(cx); va_start(ap, format); ok = ReportErrorVA(cx, JSREPORT_WARNING, format, ArgumentsAreUTF8, ap); va_end(ap); return ok; } JS_PUBLIC_API(bool) JS_ReportErrorFlagsAndNumberASCII(JSContext* cx, unsigned flags, JSErrorCallback errorCallback, void* userRef, const unsigned errorNumber, ...) { va_list ap; bool ok; AssertHeapIsIdle(cx); va_start(ap, errorNumber); ok = ReportErrorNumberVA(cx, flags, errorCallback, userRef, errorNumber, ArgumentsAreASCII, ap); va_end(ap); return ok; } JS_PUBLIC_API(bool) JS_ReportErrorFlagsAndNumberLatin1(JSContext* cx, unsigned flags, JSErrorCallback errorCallback, void* userRef, const unsigned errorNumber, ...) { va_list ap; bool ok; AssertHeapIsIdle(cx); va_start(ap, errorNumber); ok = ReportErrorNumberVA(cx, flags, errorCallback, userRef, errorNumber, ArgumentsAreLatin1, ap); va_end(ap); return ok; } JS_PUBLIC_API(bool) JS_ReportErrorFlagsAndNumberUTF8(JSContext* cx, unsigned flags, JSErrorCallback errorCallback, void* userRef, const unsigned errorNumber, ...) { va_list ap; bool ok; AssertHeapIsIdle(cx); va_start(ap, errorNumber); ok = ReportErrorNumberVA(cx, flags, errorCallback, userRef, errorNumber, ArgumentsAreUTF8, ap); va_end(ap); return ok; } JS_PUBLIC_API(bool) JS_ReportErrorFlagsAndNumberUC(JSContext* cx, unsigned flags, JSErrorCallback errorCallback, void* userRef, const unsigned errorNumber, ...) { va_list ap; bool ok; AssertHeapIsIdle(cx); va_start(ap, errorNumber); ok = ReportErrorNumberVA(cx, flags, errorCallback, userRef, errorNumber, ArgumentsAreUnicode, ap); va_end(ap); return ok; } JS_PUBLIC_API(void) JS_ReportOutOfMemory(JSContext* cx) { ReportOutOfMemory(cx); } JS_PUBLIC_API(void) JS_ReportAllocationOverflow(JSContext* cx) { ReportAllocationOverflow(cx); } JS_PUBLIC_API(JS::WarningReporter) JS::GetWarningReporter(JSContext* cx) { return cx->warningReporter; } JS_PUBLIC_API(JS::WarningReporter) JS::SetWarningReporter(JSContext* cx, JS::WarningReporter reporter) { WarningReporter older = cx->warningReporter; cx->warningReporter = reporter; return older; } /************************************************************************/ /* * Dates. */ JS_PUBLIC_API(JSObject*) JS_NewDateObject(JSContext* cx, int year, int mon, int mday, int hour, int min, int sec) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return NewDateObject(cx, year, mon, mday, hour, min, sec); } JS_PUBLIC_API(JSObject*) JS::NewDateObject(JSContext* cx, JS::ClippedTime time) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return NewDateObjectMsec(cx, time); } JS_PUBLIC_API(bool) JS_ObjectIsDate(JSContext* cx, HandleObject obj, bool* isDate) { assertSameCompartment(cx, obj); ESClass cls; if (!GetBuiltinClass(cx, obj, &cls)) return false; *isDate = cls == ESClass::Date; return true; } /************************************************************************/ /* * Regular Expressions. */ JS_PUBLIC_API(JSObject*) JS_NewRegExpObject(JSContext* cx, const char* bytes, size_t length, unsigned flags) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); ScopedJSFreePtr chars(InflateString(cx, bytes, &length)); if (!chars) return nullptr; RegExpObject* reobj = RegExpObject::create(cx, chars, length, RegExpFlag(flags), nullptr, cx->tempLifoAlloc()); return reobj; } JS_PUBLIC_API(JSObject*) JS_NewUCRegExpObject(JSContext* cx, const char16_t* chars, size_t length, unsigned flags) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); return RegExpObject::create(cx, chars, length, RegExpFlag(flags), nullptr, cx->tempLifoAlloc()); } JS_PUBLIC_API(bool) JS_SetRegExpInput(JSContext* cx, HandleObject obj, HandleString input) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, input); Handle global = obj.as(); RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global); if (!res) return false; res->reset(cx, input); return true; } JS_PUBLIC_API(bool) JS_ClearRegExpStatics(JSContext* cx, HandleObject obj) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); MOZ_ASSERT(obj); Handle global = obj.as(); RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global); if (!res) return false; res->clear(); return true; } JS_PUBLIC_API(bool) JS_ExecuteRegExp(JSContext* cx, HandleObject obj, HandleObject reobj, char16_t* chars, size_t length, size_t* indexp, bool test, MutableHandleValue rval) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); Handle global = obj.as(); RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global); if (!res) return false; RootedLinearString input(cx, NewStringCopyN(cx, chars, length)); if (!input) return false; return ExecuteRegExpLegacy(cx, res, reobj.as(), input, indexp, test, rval); } JS_PUBLIC_API(bool) JS_ExecuteRegExpNoStatics(JSContext* cx, HandleObject obj, char16_t* chars, size_t length, size_t* indexp, bool test, MutableHandleValue rval) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); RootedLinearString input(cx, NewStringCopyN(cx, chars, length)); if (!input) return false; return ExecuteRegExpLegacy(cx, nullptr, obj.as(), input, indexp, test, rval); } JS_PUBLIC_API(bool) JS_ObjectIsRegExp(JSContext* cx, HandleObject obj, bool* isRegExp) { assertSameCompartment(cx, obj); ESClass cls; if (!GetBuiltinClass(cx, obj, &cls)) return false; *isRegExp = cls == ESClass::RegExp; return true; } JS_PUBLIC_API(unsigned) JS_GetRegExpFlags(JSContext* cx, HandleObject obj) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); RegExpGuard shared(cx); if (!RegExpToShared(cx, obj, &shared)) return false; return shared.re()->getFlags(); } JS_PUBLIC_API(JSString*) JS_GetRegExpSource(JSContext* cx, HandleObject obj) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); RegExpGuard shared(cx); if (!RegExpToShared(cx, obj, &shared)) return nullptr; return shared.re()->getSource(); } /************************************************************************/ JS_PUBLIC_API(bool) JS_SetDefaultLocale(JSContext* cx, const char* locale) { AssertHeapIsIdle(cx); return cx->setDefaultLocale(locale); } JS_PUBLIC_API(UniqueChars) JS_GetDefaultLocale(JSContext* cx) { AssertHeapIsIdle(cx); if (const char* locale = cx->getDefaultLocale()) return UniqueChars(JS_strdup(cx, locale)); return nullptr; } JS_PUBLIC_API(void) JS_ResetDefaultLocale(JSContext* cx) { AssertHeapIsIdle(cx); cx->resetDefaultLocale(); } JS_PUBLIC_API(void) JS_SetLocaleCallbacks(JSContext* cx, const JSLocaleCallbacks* callbacks) { AssertHeapIsIdle(cx); cx->localeCallbacks = callbacks; } JS_PUBLIC_API(const JSLocaleCallbacks*) JS_GetLocaleCallbacks(JSContext* cx) { /* This function can be called by a finalizer. */ return cx->localeCallbacks; } /************************************************************************/ JS_PUBLIC_API(bool) JS_IsExceptionPending(JSContext* cx) { /* This function can be called by a finalizer. */ return (bool) cx->isExceptionPending(); } JS_PUBLIC_API(bool) JS_GetPendingException(JSContext* cx, MutableHandleValue vp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); if (!cx->isExceptionPending()) return false; return cx->getPendingException(vp); } JS_PUBLIC_API(void) JS_SetPendingException(JSContext* cx, HandleValue value) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, value); cx->setPendingException(value); } JS_PUBLIC_API(void) JS_ClearPendingException(JSContext* cx) { AssertHeapIsIdle(cx); cx->clearPendingException(); } JS::AutoSaveExceptionState::AutoSaveExceptionState(JSContext* cx) : context(cx), wasPropagatingForcedReturn(cx->propagatingForcedReturn_), wasOverRecursed(cx->overRecursed_), wasThrowing(cx->throwing), exceptionValue(cx) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); if (wasPropagatingForcedReturn) cx->clearPropagatingForcedReturn(); if (wasOverRecursed) cx->overRecursed_ = false; if (wasThrowing) { exceptionValue = cx->unwrappedException_; cx->clearPendingException(); } } void JS::AutoSaveExceptionState::restore() { context->propagatingForcedReturn_ = wasPropagatingForcedReturn; context->overRecursed_ = wasOverRecursed; context->throwing = wasThrowing; context->unwrappedException_ = exceptionValue; drop(); } JS::AutoSaveExceptionState::~AutoSaveExceptionState() { if (!context->isExceptionPending()) { if (wasPropagatingForcedReturn) context->setPropagatingForcedReturn(); if (wasThrowing) { context->overRecursed_ = wasOverRecursed; context->throwing = true; context->unwrappedException_ = exceptionValue; } } } struct JSExceptionState { explicit JSExceptionState(JSContext* cx) : exception(cx) {} bool throwing; PersistentRootedValue exception; }; JS_PUBLIC_API(JSExceptionState*) JS_SaveExceptionState(JSContext* cx) { JSExceptionState* state; AssertHeapIsIdle(cx); CHECK_REQUEST(cx); state = cx->new_(cx); if (state) state->throwing = JS_GetPendingException(cx, &state->exception); return state; } JS_PUBLIC_API(void) JS_RestoreExceptionState(JSContext* cx, JSExceptionState* state) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); if (state) { if (state->throwing) JS_SetPendingException(cx, state->exception); else JS_ClearPendingException(cx); JS_DropExceptionState(cx, state); } } JS_PUBLIC_API(void) JS_DropExceptionState(JSContext* cx, JSExceptionState* state) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); js_delete(state); } JS_PUBLIC_API(JSErrorReport*) JS_ErrorFromException(JSContext* cx, HandleObject obj) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); return ErrorFromException(cx, obj); } void JSErrorReport::initBorrowedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg, size_t tokenOffsetArg) { MOZ_ASSERT(linebufArg); MOZ_ASSERT(tokenOffsetArg <= linebufLengthArg); MOZ_ASSERT(linebufArg[linebufLengthArg] == '\0'); linebuf_ = linebufArg; linebufLength_ = linebufLengthArg; tokenOffset_ = tokenOffsetArg; } void JSErrorReport::freeLinebuf() { if (ownsLinebuf_ && linebuf_) { js_free((void*)linebuf_); ownsLinebuf_ = false; } linebuf_ = nullptr; } JSString* JSErrorBase::newMessageString(JSContext* cx) { if (!message_) return cx->runtime()->emptyString; return JS_NewStringCopyUTF8Z(cx, message_); } void JSErrorBase::freeMessage() { if (ownsMessage_) { js_free((void*)message_.get()); ownsMessage_ = false; } message_ = JS::ConstUTF8CharsZ(); } JSErrorNotes::JSErrorNotes() : notes_() {} JSErrorNotes::~JSErrorNotes() { } static UniquePtr CreateErrorNoteVA(JSContext* cx, const char* filename, unsigned lineno, unsigned column, JSErrorCallback errorCallback, void* userRef, const unsigned errorNumber, ErrorArgumentsType argumentsType, va_list ap) { auto note = MakeUnique(); if (!note) return nullptr; note->errorNumber = errorNumber; note->filename = filename; note->lineno = lineno; note->column = column; if (!ExpandErrorArgumentsVA(cx, errorCallback, userRef, errorNumber, nullptr, argumentsType, note.get(), ap)) { return nullptr; } return note; } bool JSErrorNotes::addNoteASCII(JSContext* cx, const char* filename, unsigned lineno, unsigned column, JSErrorCallback errorCallback, void* userRef, const unsigned errorNumber, ...) { va_list ap; va_start(ap, errorNumber); auto note = CreateErrorNoteVA(cx, filename, lineno, column, errorCallback, userRef, errorNumber, ArgumentsAreASCII, ap); va_end(ap); if (!note) return false; if (!notes_.append(Move(note))) return false; return true; } bool JSErrorNotes::addNoteLatin1(JSContext* cx, const char* filename, unsigned lineno, unsigned column, JSErrorCallback errorCallback, void* userRef, const unsigned errorNumber, ...) { va_list ap; va_start(ap, errorNumber); auto note = CreateErrorNoteVA(cx, filename, lineno, column, errorCallback, userRef, errorNumber, ArgumentsAreLatin1, ap); va_end(ap); if (!note) return false; if (!notes_.append(Move(note))) return false; return true; } bool JSErrorNotes::addNoteUTF8(JSContext* cx, const char* filename, unsigned lineno, unsigned column, JSErrorCallback errorCallback, void* userRef, const unsigned errorNumber, ...) { va_list ap; va_start(ap, errorNumber); auto note = CreateErrorNoteVA(cx, filename, lineno, column, errorCallback, userRef, errorNumber, ArgumentsAreUTF8, ap); va_end(ap); if (!note) return false; if (!notes_.append(Move(note))) return false; return true; } size_t JSErrorNotes::length() { return notes_.length(); } UniquePtr JSErrorNotes::copy(JSContext* cx) { auto copiedNotes = MakeUnique(); if (!copiedNotes) return nullptr; for (auto&& note : *this) { js::UniquePtr copied(CopyErrorNote(cx, note.get())); if (!copied) return nullptr; if (!copiedNotes->notes_.append(Move(copied))) return nullptr; } return copiedNotes; } JSErrorNotes::iterator JSErrorNotes::begin() { return iterator(notes_.begin()); } JSErrorNotes::iterator JSErrorNotes::end() { return iterator(notes_.end()); } JS_PUBLIC_API(bool) JS_ThrowStopIteration(JSContext* cx) { AssertHeapIsIdle(cx); return ThrowStopIteration(cx); } JS_PUBLIC_API(bool) JS_IsStopIteration(const Value& v) { return v.isObject() && v.toObject().is(); } extern MOZ_NEVER_INLINE JS_PUBLIC_API(void) JS_AbortIfWrongThread(JSContext* cx) { if (!CurrentThreadCanAccessRuntime(cx)) MOZ_CRASH(); if (!js::TlsPerThreadData.get()->associatedWith(cx)) MOZ_CRASH(); } JS_PUBLIC_API(void) JS_SetParallelParsingEnabled(JSContext* cx, bool enabled) { cx->setParallelParsingEnabled(enabled); } JS_PUBLIC_API(void) JS_SetOffthreadIonCompilationEnabled(JSContext* cx, bool enabled) { cx->setOffthreadIonCompilationEnabled(enabled); } JS_PUBLIC_API(void) JS_SetGlobalJitCompilerOption(JSContext* cx, JSJitCompilerOption opt, uint32_t value) { JSRuntime* rt = cx->runtime(); switch (opt) { case JSJITCOMPILER_BASELINE_WARMUP_TRIGGER: if (value == uint32_t(-1)) { jit::DefaultJitOptions defaultValues; value = defaultValues.baselineWarmUpThreshold; } jit::JitOptions.baselineWarmUpThreshold = value; break; case JSJITCOMPILER_ION_WARMUP_TRIGGER: if (value == uint32_t(-1)) { jit::JitOptions.resetCompilerWarmUpThreshold(); break; } jit::JitOptions.setCompilerWarmUpThreshold(value); if (value == 0) jit::JitOptions.setEagerCompilation(); break; case JSJITCOMPILER_ION_GVN_ENABLE: if (value == 0) { jit::JitOptions.enableGvn(false); JitSpew(js::jit::JitSpew_IonScripts, "Disable ion's GVN"); } else { jit::JitOptions.enableGvn(true); JitSpew(js::jit::JitSpew_IonScripts, "Enable ion's GVN"); } break; case JSJITCOMPILER_ION_FORCE_IC: if (value == 0) { jit::JitOptions.forceInlineCaches = false; JitSpew(js::jit::JitSpew_IonScripts, "IonBuilder: Enable non-IC optimizations."); } else { jit::JitOptions.forceInlineCaches = true; JitSpew(js::jit::JitSpew_IonScripts, "IonBuilder: Disable non-IC optimizations."); } break; case JSJITCOMPILER_ION_CHECK_RANGE_ANALYSIS: if (value == 0) { jit::JitOptions.checkRangeAnalysis = false; JitSpew(js::jit::JitSpew_IonScripts, "IonBuilder: Enable range analysis checks."); } else { jit::JitOptions.checkRangeAnalysis = true; JitSpew(js::jit::JitSpew_IonScripts, "IonBuilder: Disable range analysis checks."); } break; case JSJITCOMPILER_ION_ENABLE: if (value == 1) { JS::ContextOptionsRef(cx).setIon(true); JitSpew(js::jit::JitSpew_IonScripts, "Enable ion"); } else if (value == 0) { JS::ContextOptionsRef(cx).setIon(false); JitSpew(js::jit::JitSpew_IonScripts, "Disable ion"); } break; case JSJITCOMPILER_BASELINE_ENABLE: if (value == 1) { JS::ContextOptionsRef(cx).setBaseline(true); ReleaseAllJITCode(rt->defaultFreeOp()); JitSpew(js::jit::JitSpew_BaselineScripts, "Enable baseline"); } else if (value == 0) { JS::ContextOptionsRef(cx).setBaseline(false); ReleaseAllJITCode(rt->defaultFreeOp()); JitSpew(js::jit::JitSpew_BaselineScripts, "Disable baseline"); } break; case JSJITCOMPILER_OFFTHREAD_COMPILATION_ENABLE: if (value == 1) { rt->setOffthreadIonCompilationEnabled(true); JitSpew(js::jit::JitSpew_IonScripts, "Enable offthread compilation"); } else if (value == 0) { rt->setOffthreadIonCompilationEnabled(false); JitSpew(js::jit::JitSpew_IonScripts, "Disable offthread compilation"); } break; case JSJITCOMPILER_JUMP_THRESHOLD: if (value == uint32_t(-1)) { jit::DefaultJitOptions defaultValues; value = defaultValues.jumpThreshold; } jit::JitOptions.jumpThreshold = value; break; case JSJITCOMPILER_ASMJS_ATOMICS_ENABLE: jit::JitOptions.asmJSAtomicsEnable = !!value; break; case JSJITCOMPILER_WASM_TEST_MODE: jit::JitOptions.wasmTestMode = !!value; break; case JSJITCOMPILER_WASM_FOLD_OFFSETS: jit::JitOptions.wasmFoldOffsets = !!value; break; case JSJITCOMPILER_ION_INTERRUPT_WITHOUT_SIGNAL: jit::JitOptions.ionInterruptWithoutSignals = !!value; break; default: break; } } JS_PUBLIC_API(bool) JS_GetGlobalJitCompilerOption(JSContext* cx, JSJitCompilerOption opt, uint32_t* valueOut) { MOZ_ASSERT(valueOut); #ifndef JS_CODEGEN_NONE JSRuntime* rt = cx->runtime(); switch (opt) { case JSJITCOMPILER_BASELINE_WARMUP_TRIGGER: *valueOut = jit::JitOptions.baselineWarmUpThreshold; break; case JSJITCOMPILER_ION_WARMUP_TRIGGER: *valueOut = jit::JitOptions.forcedDefaultIonWarmUpThreshold.isSome() ? jit::JitOptions.forcedDefaultIonWarmUpThreshold.ref() : jit::OptimizationInfo::CompilerWarmupThreshold; break; case JSJITCOMPILER_ION_FORCE_IC: *valueOut = jit::JitOptions.forceInlineCaches; break; case JSJITCOMPILER_ION_CHECK_RANGE_ANALYSIS: *valueOut = jit::JitOptions.checkRangeAnalysis; break; case JSJITCOMPILER_ION_ENABLE: *valueOut = JS::ContextOptionsRef(cx).ion(); break; case JSJITCOMPILER_BASELINE_ENABLE: *valueOut = JS::ContextOptionsRef(cx).baseline(); break; case JSJITCOMPILER_OFFTHREAD_COMPILATION_ENABLE: *valueOut = rt->canUseOffthreadIonCompilation(); break; case JSJITCOMPILER_ASMJS_ATOMICS_ENABLE: *valueOut = jit::JitOptions.asmJSAtomicsEnable ? 1 : 0; break; case JSJITCOMPILER_WASM_TEST_MODE: *valueOut = jit::JitOptions.wasmTestMode ? 1 : 0; break; case JSJITCOMPILER_WASM_FOLD_OFFSETS: *valueOut = jit::JitOptions.wasmFoldOffsets ? 1 : 0; break; case JSJITCOMPILER_ION_INTERRUPT_WITHOUT_SIGNAL: *valueOut = jit::JitOptions.ionInterruptWithoutSignals ? 1 : 0; break; default: return false; } #else *valueOut = 0; #endif return true; } /************************************************************************/ #if !defined(STATIC_EXPORTABLE_JS_API) && !defined(STATIC_JS_API) && defined(XP_WIN) #include "jswin.h" /* * Initialization routine for the JS DLL. */ BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved) { return TRUE; } #endif JS_PUBLIC_API(bool) JS_IndexToId(JSContext* cx, uint32_t index, MutableHandleId id) { return IndexToId(cx, index, id); } JS_PUBLIC_API(bool) JS_CharsToId(JSContext* cx, JS::TwoByteChars chars, MutableHandleId idp) { RootedAtom atom(cx, AtomizeChars(cx, chars.begin().get(), chars.length())); if (!atom) return false; #ifdef DEBUG uint32_t dummy; MOZ_ASSERT(!atom->isIndex(&dummy), "API misuse: |chars| must not encode an index"); #endif idp.set(AtomToId(atom)); return true; } JS_PUBLIC_API(bool) JS_IsIdentifier(JSContext* cx, HandleString str, bool* isIdentifier) { assertSameCompartment(cx, str); JSLinearString* linearStr = str->ensureLinear(cx); if (!linearStr) return false; *isIdentifier = js::frontend::IsIdentifier(linearStr); return true; } JS_PUBLIC_API(bool) JS_IsIdentifier(const char16_t* chars, size_t length) { return js::frontend::IsIdentifier(chars, length); } namespace JS { void AutoFilename::reset() { if (ss_) { ss_->decref(); ss_ = nullptr; } if (filename_.is()) filename_.as() = nullptr; else filename_.as().reset(); } void AutoFilename::setScriptSource(js::ScriptSource* p) { MOZ_ASSERT(!ss_); MOZ_ASSERT(!get()); ss_ = p; if (p) { p->incref(); setUnowned(p->filename()); } } void AutoFilename::setUnowned(const char* filename) { MOZ_ASSERT(!get()); filename_.as() = filename ? filename : ""; } void AutoFilename::setOwned(UniqueChars&& filename) { MOZ_ASSERT(!get()); filename_ = AsVariant(Move(filename)); } const char* AutoFilename::get() const { if (filename_.is()) return filename_.as(); return filename_.as().get(); } JS_PUBLIC_API(bool) DescribeScriptedCaller(JSContext* cx, AutoFilename* filename, unsigned* lineno, unsigned* column) { if (filename) filename->reset(); if (lineno) *lineno = 0; if (column) *column = 0; if (!cx->compartment()) return false; NonBuiltinFrameIter i(cx, cx->compartment()->principals()); if (i.done()) return false; // If the caller is hidden, the embedding wants us to return false here so // that it can check its own stack (see HideScriptedCaller). if (i.activation()->scriptedCallerIsHidden()) return false; if (filename) { if (i.isWasm()) { // For Wasm, copy out the filename, there is no script source. UniqueChars copy = DuplicateString(i.filename() ? i.filename() : ""); if (!copy) filename->setUnowned("out of memory"); else filename->setOwned(Move(copy)); } else { // All other frames have a script source to read the filename from. filename->setScriptSource(i.scriptSource()); } } if (lineno) *lineno = i.computeLine(column); else if (column) i.computeLine(column); return true; } // Fast path to get the activation to use for GetScriptedCallerGlobal. If this // returns false, the fast path didn't work out and the caller has to use the // (much slower) NonBuiltinFrameIter path. // // The optimization here is that we skip Ion-inlined frames and only look at // 'outer' frames. That's fine: each activation is tied to a single compartment, // so if an activation contains at least one non-self-hosted frame, we can use // the activation's global for GetScriptedCallerGlobal. If, however, all 'outer' // frames are self-hosted, it's possible Ion inlined a non-self-hosted script, // so we must return false and use the slower path. static bool GetScriptedCallerActivationFast(JSContext* cx, Activation** activation) { ActivationIterator activationIter(cx->runtime()); if (activationIter.done()) { *activation = nullptr; return true; } *activation = activationIter.activation(); if (activationIter->isJit()) { for (jit::JitFrameIterator iter(activationIter); !iter.done(); ++iter) { if (iter.isScripted() && !iter.script()->selfHosted()) return true; } } else if (activationIter->isInterpreter()) { for (InterpreterFrameIterator iter((*activation)->asInterpreter()); !iter.done(); ++iter) { if (!iter.frame()->script()->selfHosted()) return true; } } return false; } JS_PUBLIC_API(JSObject*) GetScriptedCallerGlobal(JSContext* cx) { Activation* activation; if (GetScriptedCallerActivationFast(cx, &activation)) { if (!activation) return nullptr; } else { NonBuiltinFrameIter i(cx); if (i.done()) return nullptr; activation = i.activation(); } // If the caller is hidden, the embedding wants us to return null here so // that it can check its own stack (see HideScriptedCaller). if (activation->scriptedCallerIsHidden()) return nullptr; GlobalObject* global = activation->compartment()->maybeGlobal(); // Noone should be running code in the atoms compartment or running code in // a compartment without any live objects, so there should definitely be a // live global. MOZ_ASSERT(global); return global; } JS_PUBLIC_API(void) HideScriptedCaller(JSContext* cx) { MOZ_ASSERT(cx); // If there's no accessible activation on the stack, we'll return null from // DescribeScriptedCaller anyway, so there's no need to annotate anything. Activation* act = cx->runtime()->activation(); if (!act) return; act->hideScriptedCaller(); } JS_PUBLIC_API(void) UnhideScriptedCaller(JSContext* cx) { Activation* act = cx->runtime()->activation(); if (!act) return; act->unhideScriptedCaller(); } } /* namespace JS */ AutoGCRooter::AutoGCRooter(JSContext* cx, ptrdiff_t tag) : AutoGCRooter(JS::RootingContext::get(cx), tag) {} AutoGCRooter::AutoGCRooter(JS::RootingContext* cx, ptrdiff_t tag) : down(cx->roots.autoGCRooters_), tag_(tag), stackTop(&cx->roots.autoGCRooters_) { MOZ_ASSERT(this != *stackTop); *stackTop = this; } #ifdef JS_DEBUG JS_PUBLIC_API(void) JS::detail::AssertArgumentsAreSane(JSContext* cx, HandleValue value) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, value); } #endif /* JS_DEBUG */ JS_PUBLIC_API(JS::TranscodeResult) JS::EncodeScript(JSContext* cx, TranscodeBuffer& buffer, HandleScript scriptArg) { XDREncoder encoder(cx, buffer, buffer.length()); RootedScript script(cx, scriptArg); if (!encoder.codeScript(&script)) buffer.clearAndFree(); MOZ_ASSERT(!buffer.empty() == (encoder.resultCode() == TranscodeResult_Ok)); return encoder.resultCode(); } JS_PUBLIC_API(JS::TranscodeResult) JS::EncodeInterpretedFunction(JSContext* cx, TranscodeBuffer& buffer, HandleObject funobjArg) { XDREncoder encoder(cx, buffer, buffer.length()); RootedFunction funobj(cx, &funobjArg->as()); if (!encoder.codeFunction(&funobj)) buffer.clearAndFree(); MOZ_ASSERT(!buffer.empty() == (encoder.resultCode() == TranscodeResult_Ok)); return encoder.resultCode(); } JS_PUBLIC_API(JS::TranscodeResult) JS::DecodeScript(JSContext* cx, TranscodeBuffer& buffer, JS::MutableHandleScript scriptp, size_t cursorIndex) { XDRDecoder decoder(cx, buffer, cursorIndex); decoder.codeScript(scriptp); MOZ_ASSERT(bool(scriptp) == (decoder.resultCode() == TranscodeResult_Ok)); return decoder.resultCode(); } JS_PUBLIC_API(JS::TranscodeResult) JS::DecodeInterpretedFunction(JSContext* cx, TranscodeBuffer& buffer, JS::MutableHandleFunction funp, size_t cursorIndex) { XDRDecoder decoder(cx, buffer, cursorIndex); decoder.codeFunction(funp); MOZ_ASSERT(bool(funp) == (decoder.resultCode() == TranscodeResult_Ok)); return decoder.resultCode(); } JS_PUBLIC_API(void) JS::SetBuildIdOp(JSContext* cx, JS::BuildIdOp buildIdOp) { cx->runtime()->buildIdOp = buildIdOp; } JS_PUBLIC_API(void) JS::SetAsmJSCacheOps(JSContext* cx, const JS::AsmJSCacheOps* ops) { cx->runtime()->asmJSCacheOps = *ops; } bool JS::IsWasmModuleObject(HandleObject obj) { JSObject* unwrapped = CheckedUnwrap(obj); if (!unwrapped) return false; return unwrapped->is(); } JS_PUBLIC_API(RefPtr) JS::GetWasmModule(HandleObject obj) { MOZ_ASSERT(JS::IsWasmModuleObject(obj)); return &CheckedUnwrap(obj)->as().module(); } JS_PUBLIC_API(bool) JS::CompiledWasmModuleAssumptionsMatch(PRFileDesc* compiled, JS::BuildIdCharVector&& buildId) { return wasm::CompiledModuleAssumptionsMatch(compiled, Move(buildId)); } JS_PUBLIC_API(RefPtr) JS::DeserializeWasmModule(PRFileDesc* bytecode, PRFileDesc* maybeCompiled, JS::BuildIdCharVector&& buildId, UniqueChars file, unsigned line, unsigned column) { return wasm::DeserializeModule(bytecode, maybeCompiled, Move(buildId), Move(file), line, column); } char* JSAutoByteString::encodeLatin1(ExclusiveContext* cx, JSString* str) { mBytes = EncodeLatin1(cx, str); return mBytes; } JS_PUBLIC_API(void) JS::SetLargeAllocationFailureCallback(JSContext* cx, JS::LargeAllocationFailureCallback lafc, void* data) { cx->largeAllocationFailureCallback = lafc; cx->largeAllocationFailureCallbackData = data; } JS_PUBLIC_API(void) JS::SetOutOfMemoryCallback(JSContext* cx, OutOfMemoryCallback cb, void* data) { cx->oomCallback = cb; cx->oomCallbackData = data; } JS::FirstSubsumedFrame::FirstSubsumedFrame(JSContext* cx, bool ignoreSelfHostedFrames /* = true */) : JS::FirstSubsumedFrame(cx, cx->compartment()->principals(), ignoreSelfHostedFrames) { } JS_PUBLIC_API(bool) JS::CaptureCurrentStack(JSContext* cx, JS::MutableHandleObject stackp, JS::StackCapture&& capture /* = JS::StackCapture(JS::AllFrames()) */) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); MOZ_RELEASE_ASSERT(cx->compartment()); JSCompartment* compartment = cx->compartment(); Rooted frame(cx); if (!compartment->savedStacks().saveCurrentStack(cx, &frame, mozilla::Move(capture))) return false; stackp.set(frame.get()); return true; } JS_PUBLIC_API(bool) JS::CopyAsyncStack(JSContext* cx, JS::HandleObject asyncStack, JS::HandleString asyncCause, JS::MutableHandleObject stackp, unsigned maxFrameCount) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); MOZ_RELEASE_ASSERT(cx->compartment()); js::AssertObjectIsSavedFrameOrWrapper(cx, asyncStack); JSCompartment* compartment = cx->compartment(); Rooted frame(cx); if (!compartment->savedStacks().copyAsyncStack(cx, asyncStack, asyncCause, &frame, maxFrameCount)) return false; stackp.set(frame.get()); return true; } JS_PUBLIC_API(Zone*) JS::GetObjectZone(JSObject* obj) { return obj->zone(); } JS_PUBLIC_API(JS::TraceKind) JS::GCThingTraceKind(void* thing) { MOZ_ASSERT(thing); return static_cast(thing)->getTraceKind(); } JS_PUBLIC_API(void) js::SetStackFormat(JSContext* cx, js::StackFormat format) { cx->setStackFormat(format); } JS_PUBLIC_API(js::StackFormat) js::GetStackFormat(JSContext* cx) { return cx->stackFormat(); }