diff options
Diffstat (limited to '')
-rw-r--r-- | js/src/vm/SelfHosting.cpp | 3397 |
1 files changed, 3397 insertions, 0 deletions
diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp new file mode 100644 index 0000000000..63a51666be --- /dev/null +++ b/js/src/vm/SelfHosting.cpp @@ -0,0 +1,3397 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "vm/SelfHosting.h" + +#include "mozilla/Casting.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/Maybe.h" +#include "mozilla/Utf8.h" // mozilla::Utf8Unit + +#include <algorithm> +#include <iterator> + +#include "jsdate.h" +#include "jsfriendapi.h" +#include "jsmath.h" +#include "jsnum.h" +#include "selfhosted.out.h" + +#include "builtin/Array.h" +#include "builtin/BigInt.h" +#ifdef JS_HAS_INTL_API +# include "builtin/intl/Collator.h" +# include "builtin/intl/DateTimeFormat.h" +# include "builtin/intl/DisplayNames.h" +# include "builtin/intl/IntlObject.h" +# include "builtin/intl/ListFormat.h" +# include "builtin/intl/Locale.h" +# include "builtin/intl/NumberFormat.h" +# include "builtin/intl/PluralRules.h" +# include "builtin/intl/RelativeTimeFormat.h" +#endif +#include "builtin/MapObject.h" +#include "builtin/ModuleObject.h" +#include "builtin/Object.h" +#include "builtin/Promise.h" +#include "builtin/Reflect.h" +#include "builtin/RegExp.h" +#include "builtin/SelfHostingDefines.h" +#include "builtin/String.h" +#include "builtin/WeakMapObject.h" +#include "frontend/BytecodeCompilation.h" // CompileGlobalScriptToStencil +#include "frontend/CompilationInfo.h" // js::frontend::CompilationStencil +#include "frontend/ParserAtom.h" // js::frontend::ParserAtom +#include "gc/Marking.h" +#include "gc/Policy.h" +#include "jit/AtomicOperations.h" +#include "jit/InlinableNatives.h" +#include "js/CharacterEncoding.h" +#include "js/CompilationAndEvaluation.h" +#include "js/Conversions.h" +#include "js/Date.h" +#include "js/ErrorReport.h" // JS::PrintError +#include "js/Exception.h" +#include "js/experimental/TypedData.h" // JS_GetArrayBufferViewType +#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* +#include "js/Modules.h" // JS::GetModulePrivate +#include "js/PropertySpec.h" +#include "js/ScalarType.h" // js::Scalar::Type +#include "js/SourceText.h" // JS::SourceText +#include "js/StableStringChars.h" +#include "js/Transcoding.h" +#include "js/Warnings.h" // JS::{,Set}WarningReporter +#include "js/Wrapper.h" +#include "util/StringBuffer.h" +#include "vm/ArgumentsObject.h" +#include "vm/AsyncFunction.h" +#include "vm/AsyncIteration.h" +#include "vm/BigIntType.h" +#include "vm/BytecodeIterator.h" +#include "vm/BytecodeLocation.h" +#include "vm/Compression.h" +#include "vm/DateObject.h" +#include "vm/FrameIter.h" // js::ScriptFrameIter +#include "vm/FunctionFlags.h" // js::FunctionFlags +#include "vm/GeneratorObject.h" +#include "vm/Interpreter.h" +#include "vm/Iteration.h" +#include "vm/JSContext.h" +#include "vm/JSFunction.h" +#include "vm/JSObject.h" +#include "vm/PIC.h" +#include "vm/PlainObject.h" // js::PlainObject +#include "vm/Printer.h" +#include "vm/Realm.h" +#include "vm/RegExpObject.h" +#include "vm/StringType.h" +#include "vm/ToSource.h" // js::ValueToSource +#include "vm/TypedArrayObject.h" +#include "vm/Uint8Clamped.h" +#include "vm/WrapperObject.h" + +#include "gc/GC-inl.h" +#include "vm/BooleanObject-inl.h" +#include "vm/BytecodeIterator-inl.h" +#include "vm/BytecodeLocation-inl.h" +#include "vm/Compartment-inl.h" +#include "vm/JSAtom-inl.h" +#include "vm/JSFunction-inl.h" +#include "vm/JSObject-inl.h" +#include "vm/JSScript-inl.h" +#include "vm/NativeObject-inl.h" +#include "vm/NumberObject-inl.h" +#include "vm/StringObject-inl.h" +#include "vm/TypedArrayObject-inl.h" + +using namespace js; +using namespace js::selfhosted; + +using JS::AutoCheckCannotGC; +using JS::AutoStableStringChars; +using JS::CompileOptions; +using mozilla::Maybe; + +static void selfHosting_WarningReporter(JSContext* cx, JSErrorReport* report) { + MOZ_ASSERT(report->isWarning()); + + JS::PrintError(cx, stderr, report, true); +} + +static bool intrinsic_ToObject(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + JSObject* obj = ToObject(cx, args[0]); + if (!obj) { + return false; + } + args.rval().setObject(*obj); + return true; +} + +static bool intrinsic_IsObject(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + Value val = args[0]; + bool isObject = val.isObject(); + args.rval().setBoolean(isObject); + return true; +} + +static bool intrinsic_IsArray(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + RootedValue val(cx, args[0]); + if (val.isObject()) { + RootedObject obj(cx, &val.toObject()); + bool isArray = false; + if (!IsArray(cx, obj, &isArray)) { + return false; + } + args.rval().setBoolean(isArray); + } else { + args.rval().setBoolean(false); + } + return true; +} + +static bool intrinsic_IsCrossRealmArrayConstructor(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + bool result = false; + if (!IsCrossRealmArrayConstructor(cx, &args[0].toObject(), &result)) { + return false; + } + args.rval().setBoolean(result); + return true; +} + +static bool intrinsic_ToLength(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + + // Inline fast path for the common case. + if (args[0].isInt32()) { + int32_t i = args[0].toInt32(); + args.rval().setInt32(i < 0 ? 0 : i); + return true; + } + + uint64_t length = 0; + if (!ToLength(cx, args[0], &length)) { + return false; + } + + args.rval().setNumber(double(length)); + return true; +} + +static bool intrinsic_ToInteger(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + double result; + if (!ToInteger(cx, args[0], &result)) { + return false; + } + args.rval().setNumber(result); + return true; +} + +static bool intrinsic_ToSource(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + JSString* str = ValueToSource(cx, args[0]); + if (!str) { + return false; + } + args.rval().setString(str); + return true; +} + +static bool intrinsic_ToPropertyKey(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + RootedId id(cx); + if (!ToPropertyKey(cx, args[0], &id)) { + return false; + } + + args.rval().set(IdToValue(id)); + return true; +} + +static bool intrinsic_IsCallable(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + args.rval().setBoolean(IsCallable(args[0])); + return true; +} + +static bool intrinsic_IsConstructor(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + args.rval().setBoolean(IsConstructor(args[0])); + return true; +} + +template <typename T> +static bool intrinsic_IsInstanceOfBuiltin(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + args.rval().setBoolean(args[0].toObject().is<T>()); + return true; +} + +template <typename T> +static bool intrinsic_GuardToBuiltin(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + if (args[0].toObject().is<T>()) { + args.rval().setObject(args[0].toObject()); + return true; + } + args.rval().setNull(); + return true; +} + +template <typename T> +static bool intrinsic_IsWrappedInstanceOfBuiltin(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + JSObject* obj = &args[0].toObject(); + if (!obj->is<WrapperObject>()) { + args.rval().setBoolean(false); + return true; + } + + JSObject* unwrapped = CheckedUnwrapDynamic(obj, cx); + if (!unwrapped) { + ReportAccessDenied(cx); + return false; + } + + args.rval().setBoolean(unwrapped->is<T>()); + return true; +} + +template <typename T> +static bool intrinsic_IsPossiblyWrappedInstanceOfBuiltin(JSContext* cx, + unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + JSObject* obj = CheckedUnwrapDynamic(&args[0].toObject(), cx); + if (!obj) { + ReportAccessDenied(cx); + return false; + } + + args.rval().setBoolean(obj->is<T>()); + return true; +} + +static bool intrinsic_SubstringKernel(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args[0].isString()); + MOZ_RELEASE_ASSERT(args[1].isInt32()); + MOZ_RELEASE_ASSERT(args[2].isInt32()); + + RootedString str(cx, args[0].toString()); + int32_t begin = args[1].toInt32(); + int32_t length = args[2].toInt32(); + + JSString* substr = SubstringKernel(cx, str, begin, length); + if (!substr) { + return false; + } + + args.rval().setString(substr); + return true; +} + +static void ThrowErrorWithType(JSContext* cx, JSExnType type, + const CallArgs& args) { + MOZ_RELEASE_ASSERT(args[0].isInt32()); + uint32_t errorNumber = args[0].toInt32(); + +#ifdef DEBUG + const JSErrorFormatString* efs = GetErrorMessage(nullptr, errorNumber); + MOZ_ASSERT(efs->argCount == args.length() - 1); + MOZ_ASSERT(efs->exnType == type, + "error-throwing intrinsic and error number are inconsistent"); +#endif + + UniqueChars errorArgs[3]; + for (unsigned i = 1; i < 4 && i < args.length(); i++) { + HandleValue val = args[i]; + if (val.isInt32() || val.isString()) { + JSString* str = ToString<CanGC>(cx, val); + if (!str) { + return; + } + errorArgs[i - 1] = QuoteString(cx, str); + } else { + errorArgs[i - 1] = + DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, nullptr); + } + if (!errorArgs[i - 1]) { + return; + } + } + + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber, + errorArgs[0].get(), errorArgs[1].get(), + errorArgs[2].get()); +} + +static bool intrinsic_ThrowRangeError(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() >= 1); + + ThrowErrorWithType(cx, JSEXN_RANGEERR, args); + return false; +} + +static bool intrinsic_ThrowTypeError(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() >= 1); + + ThrowErrorWithType(cx, JSEXN_TYPEERR, args); + return false; +} + +static bool intrinsic_ThrowSyntaxError(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() >= 1); + + ThrowErrorWithType(cx, JSEXN_SYNTAXERR, args); + return false; +} + +static bool intrinsic_ThrowAggregateError(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() >= 1); + + ThrowErrorWithType(cx, JSEXN_AGGREGATEERR, args); + return false; +} + +static bool intrinsic_ThrowInternalError(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() >= 1); + + ThrowErrorWithType(cx, JSEXN_INTERNALERR, args); + return false; +} + +static bool intrinsic_GetErrorMessage(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_RELEASE_ASSERT(args[0].isInt32()); + + const JSErrorFormatString* errorString = + GetErrorMessage(nullptr, args[0].toInt32()); + MOZ_ASSERT(errorString); + + MOZ_ASSERT(errorString->argCount == 0); + RootedString message(cx, JS_NewStringCopyZ(cx, errorString->format)); + if (!message) { + return false; + } + + args.rval().setString(message); + return true; +} + +static bool intrinsic_CreateModuleSyntaxError(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 4); + MOZ_ASSERT(args[0].isObject()); + MOZ_RELEASE_ASSERT(args[1].isInt32()); + MOZ_RELEASE_ASSERT(args[2].isInt32()); + MOZ_ASSERT(args[3].isString()); + + RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>()); + RootedString filename(cx, + JS_NewStringCopyZ(cx, module->script()->filename())); + if (!filename) { + return false; + } + + RootedString message(cx, args[3].toString()); + + RootedValue error(cx); + if (!JS::CreateError(cx, JSEXN_SYNTAXERR, nullptr, filename, + args[1].toInt32(), args[2].toInt32(), nullptr, message, + &error)) { + return false; + } + + args.rval().set(error); + return true; +} + +/** + * Handles an assertion failure in self-hosted code just like an assertion + * failure in C++ code. Information about the failure can be provided in + * args[0]. + */ +static bool intrinsic_AssertionFailed(JSContext* cx, unsigned argc, Value* vp) { +#ifdef DEBUG + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() > 0) { + // try to dump the informative string + JSString* str = ToString<CanGC>(cx, args[0]); + if (str) { + js::Fprinter out(stderr); + out.put("Self-hosted JavaScript assertion info: "); + str->dumpCharsNoNewline(out); + out.putChar('\n'); + } + } +#endif + MOZ_ASSERT(false); + return false; +} + +/** + * Dumps a message to stderr, after stringifying it. Doesn't append a newline. + */ +static bool intrinsic_DumpMessage(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); +#ifdef DEBUG + if (args.length() > 0) { + // try to dump the informative string + js::Fprinter out(stderr); + JSString* str = ToString<CanGC>(cx, args[0]); + if (str) { + str->dumpCharsNoNewline(out); + out.putChar('\n'); + } else { + cx->recoverFromOutOfMemory(); + } + } +#endif + args.rval().setUndefined(); + return true; +} + +static bool intrinsic_MakeConstructible(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(args[0].isObject()); + MOZ_ASSERT(args[0].toObject().is<JSFunction>()); + MOZ_ASSERT(args[0].toObject().as<JSFunction>().isSelfHostedBuiltin()); + MOZ_ASSERT(args[1].isObjectOrNull()); + + // Normal .prototype properties aren't enumerable. But for this to clone + // correctly, it must be enumerable. + RootedObject ctor(cx, &args[0].toObject()); + if (!DefineDataProperty( + cx, ctor, cx->names().prototype, args[1], + JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT)) { + return false; + } + + ctor->as<JSFunction>().setIsConstructor(); + args.rval().setUndefined(); + return true; +} + +static bool intrinsic_FinishBoundFunctionInit(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 3); + MOZ_ASSERT(IsCallable(args[1])); + MOZ_RELEASE_ASSERT(args[2].isInt32()); + + RootedFunction bound(cx, &args[0].toObject().as<JSFunction>()); + RootedObject targetObj(cx, &args[1].toObject()); + int32_t argCount = args[2].toInt32(); + + args.rval().setUndefined(); + return JSFunction::finishBoundFunctionInit(cx, bound, targetObj, argCount); +} + +/* + * Used to decompile values in the nearest non-builtin stack frame, falling + * back to decompiling in the current frame. Helpful for printing higher-order + * function arguments. + * + * The user must supply the argument number of the value in question; it + * _cannot_ be automatically determined. + */ +static bool intrinsic_DecompileArg(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + MOZ_RELEASE_ASSERT(args[0].isInt32()); + + HandleValue value = args[1]; + JSString* str = DecompileArgument(cx, args[0].toInt32(), value); + if (!str) { + return false; + } + args.rval().setString(str); + return true; +} + +static bool intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + // When DefineDataProperty is called with 3 arguments, it's compiled to + // JSOp::InitElem in the bytecode emitter so we shouldn't get here. + MOZ_ASSERT(args.length() == 4); + MOZ_ASSERT(args[0].isObject()); + MOZ_RELEASE_ASSERT(args[3].isInt32()); + + RootedObject obj(cx, &args[0].toObject()); + RootedId id(cx); + if (!ToPropertyKey(cx, args[1], &id)) { + return false; + } + RootedValue value(cx, args[2]); + + unsigned attrs = 0; + unsigned attributes = args[3].toInt32(); + + MOZ_ASSERT(bool(attributes & ATTR_ENUMERABLE) != + bool(attributes & ATTR_NONENUMERABLE), + "_DefineDataProperty must receive either ATTR_ENUMERABLE xor " + "ATTR_NONENUMERABLE"); + if (attributes & ATTR_ENUMERABLE) { + attrs |= JSPROP_ENUMERATE; + } + + MOZ_ASSERT(bool(attributes & ATTR_CONFIGURABLE) != + bool(attributes & ATTR_NONCONFIGURABLE), + "_DefineDataProperty must receive either ATTR_CONFIGURABLE xor " + "ATTR_NONCONFIGURABLE"); + if (attributes & ATTR_NONCONFIGURABLE) { + attrs |= JSPROP_PERMANENT; + } + + MOZ_ASSERT( + bool(attributes & ATTR_WRITABLE) != bool(attributes & ATTR_NONWRITABLE), + "_DefineDataProperty must receive either ATTR_WRITABLE xor " + "ATTR_NONWRITABLE"); + if (attributes & ATTR_NONWRITABLE) { + attrs |= JSPROP_READONLY; + } + + Rooted<PropertyDescriptor> desc(cx); + desc.setDataDescriptor(value, attrs); + if (!DefineProperty(cx, obj, id, desc)) { + return false; + } + + args.rval().setUndefined(); + return true; +} + +static bool intrinsic_DefineProperty(JSContext* cx, unsigned argc, Value* vp) { + // _DefineProperty(object, propertyKey, attributes, + // valueOrGetter, setter, strict) + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 6); + MOZ_ASSERT(args[0].isObject()); + MOZ_ASSERT(args[1].isString() || args[1].isNumber() || args[1].isSymbol()); + MOZ_RELEASE_ASSERT(args[2].isInt32()); + MOZ_ASSERT(args[5].isBoolean()); + + RootedObject obj(cx, &args[0].toObject()); + RootedId id(cx); + if (!PrimitiveValueToId<CanGC>(cx, args[1], &id)) { + return false; + } + + Rooted<PropertyDescriptor> desc(cx); + + unsigned attributes = args[2].toInt32(); + unsigned attrs = 0; + if (attributes & ATTR_ENUMERABLE) { + attrs |= JSPROP_ENUMERATE; + } else if (!(attributes & ATTR_NONENUMERABLE)) { + attrs |= JSPROP_IGNORE_ENUMERATE; + } + + if (attributes & ATTR_NONCONFIGURABLE) { + attrs |= JSPROP_PERMANENT; + } else if (!(attributes & ATTR_CONFIGURABLE)) { + attrs |= JSPROP_IGNORE_PERMANENT; + } + + if (attributes & ATTR_NONWRITABLE) { + attrs |= JSPROP_READONLY; + } else if (!(attributes & ATTR_WRITABLE)) { + attrs |= JSPROP_IGNORE_READONLY; + } + + // When args[4] is |null|, the data descriptor has a value component. + if ((attributes & DATA_DESCRIPTOR_KIND) && args[4].isNull()) { + desc.value().set(args[3]); + } else { + attrs |= JSPROP_IGNORE_VALUE; + } + + if (attributes & ACCESSOR_DESCRIPTOR_KIND) { + Value getter = args[3]; + MOZ_ASSERT(getter.isObject() || getter.isNullOrUndefined()); + if (getter.isObject()) { + desc.setGetterObject(&getter.toObject()); + } + if (!getter.isNull()) { + attrs |= JSPROP_GETTER; + } + + Value setter = args[4]; + MOZ_ASSERT(setter.isObject() || setter.isNullOrUndefined()); + if (setter.isObject()) { + desc.setSetterObject(&setter.toObject()); + } + if (!setter.isNull()) { + attrs |= JSPROP_SETTER; + } + + // By convention, these bits are not used on accessor descriptors. + attrs &= ~(JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE); + } + + desc.setAttributes(attrs); + desc.assertValid(); + + ObjectOpResult result; + if (!DefineProperty(cx, obj, id, desc, result)) { + return false; + } + + bool strict = args[5].toBoolean(); + if (strict && !result.ok()) { + // We need to tell our caller Object.defineProperty, + // that this operation failed, without actually throwing + // for web-compatibility reasons. + if (result.failureCode() == JSMSG_CANT_DEFINE_WINDOW_NC) { + args.rval().setBoolean(false); + return true; + } + + return result.reportError(cx, obj, id); + } + + args.rval().setBoolean(result.ok()); + return true; +} + +static bool intrinsic_ObjectHasPrototype(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + + // Self-hosted code calls this intrinsic with builtin prototypes. These are + // always native objects. + auto* obj = &args[0].toObject().as<NativeObject>(); + auto* proto = &args[1].toObject().as<NativeObject>(); + + JSObject* actualProto = obj->staticPrototype(); + args.rval().setBoolean(actualProto == proto); + return true; +} + +static bool intrinsic_UnsafeSetReservedSlot(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 3); + MOZ_ASSERT(args[0].isObject()); + MOZ_RELEASE_ASSERT(args[1].isInt32()); + MOZ_ASSERT(args[1].toInt32() >= 0); + + uint32_t slot = uint32_t(args[1].toInt32()); + args[0].toObject().as<NativeObject>().setReservedSlot(slot, args[2]); + args.rval().setUndefined(); + return true; +} + +static bool intrinsic_UnsafeGetReservedSlot(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(args[0].isObject()); + MOZ_RELEASE_ASSERT(args[1].isInt32()); + MOZ_ASSERT(args[1].toInt32() >= 0); + + uint32_t slot = uint32_t(args[1].toInt32()); + args.rval().set(args[0].toObject().as<NativeObject>().getReservedSlot(slot)); + return true; +} + +static bool intrinsic_UnsafeGetObjectFromReservedSlot(JSContext* cx, + unsigned argc, + Value* vp) { + if (!intrinsic_UnsafeGetReservedSlot(cx, argc, vp)) { + return false; + } + MOZ_ASSERT(vp->isObject()); + return true; +} + +static bool intrinsic_UnsafeGetInt32FromReservedSlot(JSContext* cx, + unsigned argc, Value* vp) { + if (!intrinsic_UnsafeGetReservedSlot(cx, argc, vp)) { + return false; + } + MOZ_ASSERT(vp->isInt32()); + return true; +} + +static bool intrinsic_UnsafeGetStringFromReservedSlot(JSContext* cx, + unsigned argc, + Value* vp) { + if (!intrinsic_UnsafeGetReservedSlot(cx, argc, vp)) { + return false; + } + MOZ_ASSERT(vp->isString()); + return true; +} + +static bool intrinsic_UnsafeGetBooleanFromReservedSlot(JSContext* cx, + unsigned argc, + Value* vp) { + if (!intrinsic_UnsafeGetReservedSlot(cx, argc, vp)) { + return false; + } + MOZ_ASSERT(vp->isBoolean()); + return true; +} + +static bool intrinsic_ThisTimeValue(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isInt32()); + + const char* name = nullptr; + + int32_t method = args[0].toInt32(); + if (method == DATE_METHOD_LOCALE_TIME_STRING) { + name = "toLocaleTimeString"; + } else if (method == DATE_METHOD_LOCALE_DATE_STRING) { + name = "toLocaleDateString"; + } else { + MOZ_ASSERT(method == DATE_METHOD_LOCALE_STRING); + name = "toLocaleString"; + } + + auto* unwrapped = UnwrapAndTypeCheckThis<DateObject>(cx, args, name); + if (!unwrapped) { + return false; + } + + args.rval().set(unwrapped->UTCTime()); + return true; +} + +static bool intrinsic_IsPackedArray(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + args.rval().setBoolean(IsPackedArray(&args[0].toObject())); + return true; +} + +bool js::intrinsic_NewArrayIterator(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + JSObject* obj = NewArrayIterator(cx); + if (!obj) { + return false; + } + + args.rval().setObject(*obj); + return true; +} + +static bool intrinsic_ArrayIteratorPrototypeOptimizable(JSContext* cx, + unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + ForOfPIC::Chain* stubChain = ForOfPIC::getOrCreate(cx); + if (!stubChain) { + return false; + } + + bool optimized; + if (!stubChain->tryOptimizeArrayIteratorNext(cx, &optimized)) { + return false; + } + args.rval().setBoolean(optimized); + return true; +} + +static bool intrinsic_GetNextMapEntryForIterator(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(args[0].toObject().is<MapIteratorObject>()); + MOZ_ASSERT(args[1].isObject()); + + MapIteratorObject* mapIterator = &args[0].toObject().as<MapIteratorObject>(); + ArrayObject* result = &args[1].toObject().as<ArrayObject>(); + + args.rval().setBoolean(MapIteratorObject::next(mapIterator, result)); + return true; +} + +static bool intrinsic_CreateMapIterationResultPair(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + JSObject* result = MapIteratorObject::createResultPair(cx); + if (!result) { + return false; + } + + args.rval().setObject(*result); + return true; +} + +static bool intrinsic_GetNextSetEntryForIterator(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(args[0].toObject().is<SetIteratorObject>()); + MOZ_ASSERT(args[1].isObject()); + + SetIteratorObject* setIterator = &args[0].toObject().as<SetIteratorObject>(); + ArrayObject* result = &args[1].toObject().as<ArrayObject>(); + + args.rval().setBoolean(SetIteratorObject::next(setIterator, result)); + return true; +} + +static bool intrinsic_CreateSetIterationResult(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + JSObject* result = SetIteratorObject::createResult(cx); + if (!result) { + return false; + } + + args.rval().setObject(*result); + return true; +} + +bool js::intrinsic_NewStringIterator(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + JSObject* obj = NewStringIterator(cx); + if (!obj) { + return false; + } + + args.rval().setObject(*obj); + return true; +} + +bool js::intrinsic_NewRegExpStringIterator(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + JSObject* obj = NewRegExpStringIterator(cx); + if (!obj) { + return false; + } + + args.rval().setObject(*obj); + return true; +} + +static js::PropertyName* GetUnclonedSelfHostedFunctionName(JSFunction* fun) { + if (!fun->isExtended()) { + return nullptr; + } + Value name = fun->getExtendedSlot(ORIGINAL_FUNCTION_NAME_SLOT); + if (!name.isString()) { + return nullptr; + } + return name.toString()->asAtom().asPropertyName(); +} + +js::PropertyName* js::GetClonedSelfHostedFunctionName(const JSFunction* fun) { + if (!fun->isExtended()) { + return nullptr; + } + Value name = fun->getExtendedSlot(LAZY_FUNCTION_NAME_SLOT); + if (!name.isString()) { + return nullptr; + } + return name.toString()->asAtom().asPropertyName(); +} + +js::PropertyName* js::GetClonedSelfHostedFunctionNameOffMainThread( + JSFunction* fun) { + Value name = fun->getExtendedSlotOffMainThread(LAZY_FUNCTION_NAME_SLOT); + if (!name.isString()) { + return nullptr; + } + return name.toString()->asAtom().asPropertyName(); +} + +bool js::IsExtendedUnclonedSelfHostedFunctionName(JSAtom* name) { + if (name->length() < 2) { + return false; + } + return name->latin1OrTwoByteChar(0) == '$'; +} +bool js::IsExtendedUnclonedSelfHostedFunctionName( + const frontend::ParserAtom* id) { + if (id->length() < 2) { + return false; + } + + char16_t ch = + id->hasLatin1Chars() ? id->latin1Chars()[0] : id->twoByteChars()[0]; + return ch == '$'; +} + +static void SetUnclonedSelfHostedFunctionName(JSFunction* fun, JSAtom* name) { + fun->setExtendedSlot(ORIGINAL_FUNCTION_NAME_SLOT, StringValue(name)); +} + +static void SetClonedSelfHostedFunctionName(JSFunction* fun, JSAtom* name) { + fun->setExtendedSlot(LAZY_FUNCTION_NAME_SLOT, StringValue(name)); +} + +static bool intrinsic_SetCanonicalName(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + + RootedFunction fun(cx, &args[0].toObject().as<JSFunction>()); + MOZ_ASSERT(fun->isSelfHostedBuiltin()); + MOZ_ASSERT(fun->isExtended()); + MOZ_ASSERT(IsExtendedUnclonedSelfHostedFunctionName(fun->explicitName())); + JSAtom* atom = AtomizeString(cx, args[1].toString()); + if (!atom) { + return false; + } + + // _SetCanonicalName can only be called on top-level function declarations. + MOZ_ASSERT(fun->kind() == FunctionFlags::NormalFunction); + MOZ_ASSERT(!fun->isLambda()); + + // It's an error to call _SetCanonicalName multiple times. + MOZ_ASSERT(!GetUnclonedSelfHostedFunctionName(fun)); + + // Set the lazy function name so we can later retrieve the script from the + // self-hosting global. + SetUnclonedSelfHostedFunctionName(fun, fun->explicitName()); + fun->setAtom(atom); + + args.rval().setUndefined(); + return true; +} + +static bool intrinsic_SetIsInlinableLargeFunction(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + + RootedFunction fun(cx, &args[0].toObject().as<JSFunction>()); + MOZ_ASSERT(fun->isSelfHostedBuiltin()); + + // _SetIsInlinableLargeFunction can only be called on top-level function + // declarations. + MOZ_ASSERT(fun->kind() == FunctionFlags::NormalFunction); + MOZ_ASSERT(!fun->isLambda()); + + fun->baseScript()->setIsInlinableLargeFunction(); + + args.rval().setUndefined(); + return true; +} + +static bool intrinsic_GeneratorObjectIsClosed(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + GeneratorObject* genObj = &args[0].toObject().as<GeneratorObject>(); + args.rval().setBoolean(genObj->isClosed()); + return true; +} + +static bool intrinsic_IsSuspendedGenerator(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + + if (!args[0].isObject() || !args[0].toObject().is<GeneratorObject>()) { + args.rval().setBoolean(false); + return true; + } + + GeneratorObject& genObj = args[0].toObject().as<GeneratorObject>(); + args.rval().setBoolean(!genObj.isClosed() && genObj.isSuspended()); + return true; +} + +static bool intrinsic_GeneratorIsRunning(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + GeneratorObject* genObj = &args[0].toObject().as<GeneratorObject>(); + args.rval().setBoolean(genObj->isRunning()); + return true; +} + +static bool intrinsic_GeneratorSetClosed(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + GeneratorObject* genObj = &args[0].toObject().as<GeneratorObject>(); + genObj->setClosed(); + return true; +} + +template <typename T> +static bool intrinsic_ArrayBufferByteLength(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + MOZ_ASSERT(args[0].toObject().is<T>()); + + size_t byteLength = args[0].toObject().as<T>().byteLength().get(); + args.rval().setNumber(byteLength); + return true; +} + +template <typename T> +static bool intrinsic_PossiblyWrappedArrayBufferByteLength(JSContext* cx, + unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + + T* obj = args[0].toObject().maybeUnwrapAs<T>(); + if (!obj) { + ReportAccessDenied(cx); + return false; + } + + size_t byteLength = obj->byteLength().get(); + args.rval().setNumber(byteLength); + return true; +} + +static void AssertNonNegativeInteger(const Value& v) { + MOZ_ASSERT(v.isNumber()); + MOZ_ASSERT(v.toNumber() >= 0); + MOZ_ASSERT(v.toNumber() < DOUBLE_INTEGRAL_PRECISION_LIMIT); + MOZ_ASSERT(JS::ToInteger(v.toNumber()) == v.toNumber()); +} + +template <typename T> +static bool intrinsic_ArrayBufferCopyData(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 6); + AssertNonNegativeInteger(args[1]); + AssertNonNegativeInteger(args[3]); + AssertNonNegativeInteger(args[4]); + + bool isWrapped = args[5].toBoolean(); + Rooted<T*> toBuffer(cx); + if (!isWrapped) { + toBuffer = &args[0].toObject().as<T>(); + } else { + JSObject* wrapped = &args[0].toObject(); + MOZ_ASSERT(wrapped->is<WrapperObject>()); + toBuffer = wrapped->maybeUnwrapAs<T>(); + if (!toBuffer) { + ReportAccessDenied(cx); + return false; + } + } + size_t toIndex = size_t(args[1].toNumber()); + Rooted<T*> fromBuffer(cx, &args[2].toObject().as<T>()); + size_t fromIndex = size_t(args[3].toNumber()); + size_t count = size_t(args[4].toNumber()); + + T::copyData(toBuffer, toIndex, fromBuffer, fromIndex, count); + + args.rval().setUndefined(); + return true; +} + +// Arguments must both be SharedArrayBuffer or wrapped SharedArrayBuffer. +static bool intrinsic_SharedArrayBuffersMemorySame(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + + auto* lhs = args[0].toObject().maybeUnwrapAs<SharedArrayBufferObject>(); + if (!lhs) { + ReportAccessDenied(cx); + return false; + } + auto* rhs = args[1].toObject().maybeUnwrapAs<SharedArrayBufferObject>(); + if (!rhs) { + ReportAccessDenied(cx); + return false; + } + + args.rval().setBoolean(lhs->rawBufferObject() == rhs->rawBufferObject()); + return true; +} + +static bool intrinsic_GetTypedArrayKind(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + static_assert(TYPEDARRAY_KIND_INT8 == Scalar::Type::Int8, + "TYPEDARRAY_KIND_INT8 doesn't match the scalar type"); + static_assert(TYPEDARRAY_KIND_UINT8 == Scalar::Type::Uint8, + "TYPEDARRAY_KIND_UINT8 doesn't match the scalar type"); + static_assert(TYPEDARRAY_KIND_INT16 == Scalar::Type::Int16, + "TYPEDARRAY_KIND_INT16 doesn't match the scalar type"); + static_assert(TYPEDARRAY_KIND_UINT16 == Scalar::Type::Uint16, + "TYPEDARRAY_KIND_UINT16 doesn't match the scalar type"); + static_assert(TYPEDARRAY_KIND_INT32 == Scalar::Type::Int32, + "TYPEDARRAY_KIND_INT32 doesn't match the scalar type"); + static_assert(TYPEDARRAY_KIND_UINT32 == Scalar::Type::Uint32, + "TYPEDARRAY_KIND_UINT32 doesn't match the scalar type"); + static_assert(TYPEDARRAY_KIND_FLOAT32 == Scalar::Type::Float32, + "TYPEDARRAY_KIND_FLOAT32 doesn't match the scalar type"); + static_assert(TYPEDARRAY_KIND_FLOAT64 == Scalar::Type::Float64, + "TYPEDARRAY_KIND_FLOAT64 doesn't match the scalar type"); + static_assert(TYPEDARRAY_KIND_UINT8CLAMPED == Scalar::Type::Uint8Clamped, + "TYPEDARRAY_KIND_UINT8CLAMPED doesn't match the scalar type"); + static_assert(TYPEDARRAY_KIND_BIGINT64 == Scalar::Type::BigInt64, + "TYPEDARRAY_KIND_BIGINT64 doesn't match the scalar type"); + static_assert(TYPEDARRAY_KIND_BIGUINT64 == Scalar::Type::BigUint64, + "TYPEDARRAY_KIND_BIGUINT64 doesn't match the scalar type"); + + JSObject* obj = &args[0].toObject(); + Scalar::Type type = JS_GetArrayBufferViewType(obj); + + args.rval().setInt32(static_cast<int32_t>(type)); + return true; +} + +static bool intrinsic_IsTypedArrayConstructor(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + args.rval().setBoolean(js::IsTypedArrayConstructor(&args[0].toObject())); + return true; +} + +static bool intrinsic_TypedArrayBuffer(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(TypedArrayObject::is(args[0])); + + Rooted<TypedArrayObject*> tarray(cx, + &args[0].toObject().as<TypedArrayObject>()); + if (!TypedArrayObject::ensureHasBuffer(cx, tarray)) { + return false; + } + + args.rval().set(tarray->bufferValue()); + return true; +} + +static bool intrinsic_TypedArrayByteOffset(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(TypedArrayObject::is(args[0])); + + auto* tarr = &args[0].toObject().as<TypedArrayObject>(); + args.rval().set(tarr->byteOffsetValue()); + return true; +} + +static bool intrinsic_TypedArrayElementShift(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(TypedArrayObject::is(args[0])); + + unsigned shift = + TypedArrayShift(args[0].toObject().as<TypedArrayObject>().type()); + MOZ_ASSERT(shift == 0 || shift == 1 || shift == 2 || shift == 3); + + args.rval().setInt32(mozilla::AssertedCast<int32_t>(shift)); + return true; +} + +// Return the value of [[ArrayLength]] internal slot of the TypedArray +static bool intrinsic_TypedArrayLength(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(TypedArrayObject::is(args[0])); + + auto* tarr = &args[0].toObject().as<TypedArrayObject>(); + args.rval().set(tarr->lengthValue()); + return true; +} + +static bool intrinsic_PossiblyWrappedTypedArrayLength(JSContext* cx, + unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + TypedArrayObject* obj = args[0].toObject().maybeUnwrapAs<TypedArrayObject>(); + if (!obj) { + ReportAccessDenied(cx); + return false; + } + + args.rval().set(obj->lengthValue()); + return true; +} + +static bool intrinsic_PossiblyWrappedTypedArrayHasDetachedBuffer(JSContext* cx, + unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + TypedArrayObject* obj = args[0].toObject().maybeUnwrapAs<TypedArrayObject>(); + if (!obj) { + ReportAccessDenied(cx); + return false; + } + + bool detached = obj->hasDetachedBuffer(); + args.rval().setBoolean(detached); + return true; +} + +// Extract the TypedArrayObject* underlying |obj| and return it. This method, +// in a TOTALLY UNSAFE manner, completely violates the normal compartment +// boundaries, returning an object not necessarily in the current compartment +// or in |obj|'s compartment. +// +// All callers of this method are expected to sigil this TypedArrayObject*, and +// all values and information derived from it, with an "unsafe" prefix, to +// indicate the extreme caution required when dealing with such values. +// +// If calling code discipline ever fails to be maintained, it's gonna have a +// bad time. +static TypedArrayObject* DangerouslyUnwrapTypedArray(JSContext* cx, + JSObject* obj) { + // An unwrapped pointer to an object potentially on the other side of a + // compartment boundary! Isn't this such fun? + TypedArrayObject* unwrapped = obj->maybeUnwrapAs<TypedArrayObject>(); + if (!unwrapped) { + ReportAccessDenied(cx); + return nullptr; + } + + // Be super-duper careful using this, as we've just punched through + // the compartment boundary, and things like buffer() on this aren't + // same-compartment with anything else in the calling method. + return unwrapped; +} + +// The specification requires us to perform bitwise copying when |sourceType| +// and |targetType| are the same (ES2017, §22.2.3.24, step 15). Additionally, +// as an optimization, we can also perform bitwise copying when |sourceType| +// and |targetType| have compatible bit-level representations. +static bool IsTypedArrayBitwiseSlice(Scalar::Type sourceType, + Scalar::Type targetType) { + switch (sourceType) { + case Scalar::Int8: + return targetType == Scalar::Int8 || targetType == Scalar::Uint8; + + case Scalar::Uint8: + case Scalar::Uint8Clamped: + return targetType == Scalar::Int8 || targetType == Scalar::Uint8 || + targetType == Scalar::Uint8Clamped; + + case Scalar::Int16: + case Scalar::Uint16: + return targetType == Scalar::Int16 || targetType == Scalar::Uint16; + + case Scalar::Int32: + case Scalar::Uint32: + return targetType == Scalar::Int32 || targetType == Scalar::Uint32; + + case Scalar::Float32: + return targetType == Scalar::Float32; + + case Scalar::Float64: + return targetType == Scalar::Float64; + + case Scalar::BigInt64: + case Scalar::BigUint64: + return targetType == Scalar::BigInt64 || targetType == Scalar::BigUint64; + + default: + MOZ_CRASH("IsTypedArrayBitwiseSlice with a bogus typed array type"); + } +} + +static bool intrinsic_TypedArrayBitwiseSlice(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 4); + MOZ_ASSERT(args[0].isObject()); + MOZ_ASSERT(args[1].isObject()); + AssertNonNegativeInteger(args[2]); + AssertNonNegativeInteger(args[3]); + + Rooted<TypedArrayObject*> source(cx, + &args[0].toObject().as<TypedArrayObject>()); + MOZ_ASSERT(!source->hasDetachedBuffer()); + + // As directed by |DangerouslyUnwrapTypedArray|, sigil this pointer and all + // variables derived from it to counsel extreme caution here. + Rooted<TypedArrayObject*> unsafeTypedArrayCrossCompartment(cx); + unsafeTypedArrayCrossCompartment = + DangerouslyUnwrapTypedArray(cx, &args[1].toObject()); + if (!unsafeTypedArrayCrossCompartment) { + return false; + } + MOZ_ASSERT(!unsafeTypedArrayCrossCompartment->hasDetachedBuffer()); + + Scalar::Type sourceType = source->type(); + if (!IsTypedArrayBitwiseSlice(sourceType, + unsafeTypedArrayCrossCompartment->type())) { + args.rval().setBoolean(false); + return true; + } + + size_t sourceOffset = size_t(args[2].toNumber()); + size_t count = size_t(args[3].toNumber()); + + MOZ_ASSERT(count > 0 && count <= source->length().get()); + MOZ_ASSERT(sourceOffset <= source->length().get() - count); + MOZ_ASSERT(count <= unsafeTypedArrayCrossCompartment->length().get()); + + size_t elementSize = TypedArrayElemSize(sourceType); + MOZ_ASSERT(elementSize == + TypedArrayElemSize(unsafeTypedArrayCrossCompartment->type())); + + SharedMem<uint8_t*> sourceData = + source->dataPointerEither().cast<uint8_t*>() + sourceOffset * elementSize; + + SharedMem<uint8_t*> unsafeTargetDataCrossCompartment = + unsafeTypedArrayCrossCompartment->dataPointerEither().cast<uint8_t*>(); + + size_t byteLength = count * elementSize; + + // The same-type case requires exact copying preserving the bit-level + // encoding of the source data, so use memcpy if possible. If source and + // target are the same buffer, we can't use memcpy (or memmove), because + // the specification requires sequential copying of the values. This case + // is only possible if a @@species constructor created a specifically + // crafted typed array. It won't happen in normal code and hence doesn't + // need to be optimized. + if (!TypedArrayObject::sameBuffer(source, unsafeTypedArrayCrossCompartment)) { + if (source->isSharedMemory() || + unsafeTypedArrayCrossCompartment->isSharedMemory()) { + jit::AtomicOperations::memcpySafeWhenRacy( + unsafeTargetDataCrossCompartment, sourceData, byteLength); + } else { + memcpy(unsafeTargetDataCrossCompartment.unwrapUnshared(), + sourceData.unwrapUnshared(), byteLength); + } + } else { + using namespace jit; + + for (; byteLength > 0; byteLength--) { + AtomicOperations::storeSafeWhenRacy( + unsafeTargetDataCrossCompartment++, + AtomicOperations::loadSafeWhenRacy(sourceData++)); + } + } + + args.rval().setBoolean(true); + return true; +} + +static bool intrinsic_TypedArrayInitFromPackedArray(JSContext* cx, + unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(args[0].isObject()); + MOZ_ASSERT(args[1].isObject()); + + Rooted<TypedArrayObject*> target(cx, + &args[0].toObject().as<TypedArrayObject>()); + MOZ_ASSERT(!target->hasDetachedBuffer()); + MOZ_ASSERT(!target->isSharedMemory()); + + RootedArrayObject source(cx, &args[1].toObject().as<ArrayObject>()); + MOZ_ASSERT(IsPackedArray(source)); + MOZ_ASSERT(source->length() == target->length().get()); + + switch (target->type()) { +#define INIT_TYPED_ARRAY(T, N) \ + case Scalar::N: { \ + if (!ElementSpecific<T, UnsharedOps>::initFromIterablePackedArray( \ + cx, target, source)) { \ + return false; \ + } \ + break; \ + } + JS_FOR_EACH_TYPED_ARRAY(INIT_TYPED_ARRAY) +#undef INIT_TYPED_ARRAY + + default: + MOZ_CRASH( + "TypedArrayInitFromPackedArray with a typed array with bogus type"); + } + + args.rval().setUndefined(); + return true; +} + +static bool intrinsic_RegExpCreate(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + MOZ_ASSERT(args.length() == 1 || args.length() == 2); + MOZ_ASSERT_IF(args.length() == 2, + args[1].isString() || args[1].isUndefined()); + MOZ_ASSERT(!args.isConstructing()); + + return RegExpCreate(cx, args[0], args.get(1), args.rval()); +} + +static bool intrinsic_RegExpGetSubstitution(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 6); + + RootedArrayObject matchResult(cx, &args[0].toObject().as<ArrayObject>()); + + RootedLinearString string(cx, args[1].toString()->ensureLinear(cx)); + if (!string) { + return false; + } + + int32_t position = int32_t(args[2].toNumber()); + MOZ_ASSERT(position >= 0); + + RootedLinearString replacement(cx, args[3].toString()->ensureLinear(cx)); + if (!replacement) { + return false; + } + + int32_t firstDollarIndex = int32_t(args[4].toNumber()); + MOZ_ASSERT(firstDollarIndex >= 0); + + RootedValue namedCaptures(cx, args[5]); + MOZ_ASSERT(namedCaptures.isUndefined() || namedCaptures.isObject()); + + return RegExpGetSubstitution(cx, matchResult, string, size_t(position), + replacement, size_t(firstDollarIndex), + namedCaptures, args.rval()); +} + +static bool intrinsic_StringReplaceString(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 3); + + RootedString string(cx, args[0].toString()); + RootedString pattern(cx, args[1].toString()); + RootedString replacement(cx, args[2].toString()); + JSString* result = str_replace_string_raw(cx, string, pattern, replacement); + if (!result) { + return false; + } + + args.rval().setString(result); + return true; +} + +static bool intrinsic_StringReplaceAllString(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 3); + + RootedString string(cx, args[0].toString()); + RootedString pattern(cx, args[1].toString()); + RootedString replacement(cx, args[2].toString()); + JSString* result = + str_replaceAll_string_raw(cx, string, pattern, replacement); + if (!result) { + return false; + } + + args.rval().setString(result); + return true; +} + +static bool intrinsic_StringSplitString(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + + RootedString string(cx, args[0].toString()); + RootedString sep(cx, args[1].toString()); + + JSObject* aobj = StringSplitString(cx, string, sep, INT32_MAX); + if (!aobj) { + return false; + } + + args.rval().setObject(*aobj); + return true; +} + +static bool intrinsic_StringSplitStringLimit(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 3); + + RootedString string(cx, args[0].toString()); + RootedString sep(cx, args[1].toString()); + + // args[2] should be already in UInt32 range, but it could be double typed, + // because of Ion optimization. + uint32_t limit = uint32_t(args[2].toNumber()); + MOZ_ASSERT(limit > 0, + "Zero limit case is already handled in self-hosted code."); + + JSObject* aobj = StringSplitString(cx, string, sep, limit); + if (!aobj) { + return false; + } + + args.rval().setObject(*aobj); + return true; +} + +bool CallSelfHostedNonGenericMethod(JSContext* cx, const CallArgs& args) { + // This function is called when a self-hosted method is invoked on a + // wrapper object, like a CrossCompartmentWrapper. The last argument is + // the name of the self-hosted function. The other arguments are the + // arguments to pass to this function. + + MOZ_ASSERT(args.length() > 0); + RootedPropertyName name( + cx, args[args.length() - 1].toString()->asAtom().asPropertyName()); + + InvokeArgs args2(cx); + if (!args2.init(cx, args.length() - 1)) { + return false; + } + + for (size_t i = 0; i < args.length() - 1; i++) { + args2[i].set(args[i]); + } + + return CallSelfHostedFunction(cx, name, args.thisv(), args2, args.rval()); +} + +#ifdef DEBUG +bool js::CallSelfHostedFunction(JSContext* cx, const char* name, + HandleValue thisv, const AnyInvokeArgs& args, + MutableHandleValue rval) { + JSAtom* funAtom = Atomize(cx, name, strlen(name)); + if (!funAtom) { + return false; + } + RootedPropertyName funName(cx, funAtom->asPropertyName()); + return CallSelfHostedFunction(cx, funName, thisv, args, rval); +} +#endif + +bool js::CallSelfHostedFunction(JSContext* cx, HandlePropertyName name, + HandleValue thisv, const AnyInvokeArgs& args, + MutableHandleValue rval) { + RootedValue fun(cx); + if (!GlobalObject::getIntrinsicValue(cx, cx->global(), name, &fun)) { + return false; + } + MOZ_ASSERT(fun.toObject().is<JSFunction>()); + + return Call(cx, fun, thisv, args, rval); +} + +template <typename T> +bool Is(HandleValue v) { + return v.isObject() && v.toObject().is<T>(); +} + +template <IsAcceptableThis Test> +static bool CallNonGenericSelfhostedMethod(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod<Test, CallSelfHostedNonGenericMethod>(cx, args); +} + +bool js::IsCallSelfHostedNonGenericMethod(NativeImpl impl) { + return impl == CallSelfHostedNonGenericMethod; +} + +bool js::ReportIncompatibleSelfHostedMethod(JSContext* cx, + const CallArgs& args) { + // The contract for this function is the same as + // CallSelfHostedNonGenericMethod. The normal ReportIncompatible function + // doesn't work for selfhosted functions, because they always call the + // different CallXXXMethodIfWrapped methods, which would be reported as the + // called function instead. + + // Lookup the selfhosted method that was invoked. But skip over + // internal self-hosted function frames, because those are never the + // actual self-hosted callee from external code. We can't just skip + // self-hosted things until we find a non-self-hosted one because of cases + // like array.sort(somethingSelfHosted), where we want to report the error + // in the somethingSelfHosted, not in the sort() call. + + static const char* const internalNames[] = { + "IsTypedArrayEnsuringArrayBuffer", + "UnwrapAndCallRegExpBuiltinExec", + "RegExpBuiltinExec", + "RegExpExec", + "RegExpSearchSlowPath", + "RegExpReplaceSlowPath", + "RegExpMatchSlowPath", + }; + + ScriptFrameIter iter(cx); + MOZ_ASSERT(iter.isFunctionFrame()); + + while (!iter.done()) { + MOZ_ASSERT(iter.callee(cx)->isSelfHostedOrIntrinsic() && + !iter.callee(cx)->isBoundFunction()); + UniqueChars funNameBytes; + const char* funName = + GetFunctionNameBytes(cx, iter.callee(cx), &funNameBytes); + if (!funName) { + return false; + } + if (std::all_of( + std::begin(internalNames), std::end(internalNames), + [funName](auto* name) { return strcmp(funName, name) != 0; })) { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, + JSMSG_INCOMPATIBLE_METHOD, funName, "method", + InformalValueTypeName(args.thisv())); + return false; + } + ++iter; + } + + MOZ_ASSERT_UNREACHABLE("How did we not find a useful self-hosted frame?"); + return false; +} + +#ifdef JS_HAS_INTL_API +/** + * Returns the default locale as a well-formed, but not necessarily + * canonicalized, BCP-47 language tag. + */ +static bool intrinsic_RuntimeDefaultLocale(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + const char* locale = cx->runtime()->getDefaultLocale(); + if (!locale) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_DEFAULT_LOCALE_ERROR); + return false; + } + + JSString* jslocale = NewStringCopyZ<CanGC>(cx, locale); + if (!jslocale) { + return false; + } + + args.rval().setString(jslocale); + return true; +} + +static bool intrinsic_IsRuntimeDefaultLocale(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isString() || args[0].isUndefined()); + + // |undefined| is the default value when the Intl runtime caches haven't + // yet been initialized. Handle it the same way as a cache miss. + if (args[0].isUndefined()) { + args.rval().setBoolean(false); + return true; + } + + const char* locale = cx->runtime()->getDefaultLocale(); + if (!locale) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_DEFAULT_LOCALE_ERROR); + return false; + } + + JSLinearString* str = args[0].toString()->ensureLinear(cx); + if (!str) { + return false; + } + + bool equals = StringEqualsAscii(str, locale); + args.rval().setBoolean(equals); + return true; +} +#endif // JS_HAS_INTL_API + +static bool intrinsic_ThrowArgTypeNotObject(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(args[0].isNumber()); + MOZ_ASSERT(!args[1].isObject()); + if (args[0].toNumber() == NOT_OBJECT_KIND_DESCRIPTOR) { + ReportNotObject(cx, JSMSG_OBJECT_REQUIRED_PROP_DESC, args[1]); + } else { + MOZ_CRASH("unexpected kind"); + } + + return false; +} + +static bool intrinsic_ConstructFunction(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 3); + MOZ_ASSERT(IsConstructor(args[0])); + MOZ_ASSERT(IsConstructor(args[1])); + MOZ_ASSERT(args[2].toObject().is<ArrayObject>()); + + RootedArrayObject argsList(cx, &args[2].toObject().as<ArrayObject>()); + uint32_t len = argsList->length(); + ConstructArgs constructArgs(cx); + if (!constructArgs.init(cx, len)) { + return false; + } + for (uint32_t index = 0; index < len; index++) { + constructArgs[index].set(argsList->getDenseElement(index)); + } + + RootedObject res(cx); + if (!Construct(cx, args[0], constructArgs, args[1], &res)) { + return false; + } + + args.rval().setObject(*res); + return true; +} + +static bool intrinsic_IsConstructing(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + ScriptFrameIter iter(cx); + bool isConstructing = iter.isConstructing(); + args.rval().setBoolean(isConstructing); + return true; +} + +static bool intrinsic_ConstructorForTypedArray(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + auto* object = UnwrapAndDowncastValue<TypedArrayObject>(cx, args[0]); + if (!object) { + return false; + } + + JSProtoKey protoKey = StandardProtoKeyOrNull(object); + MOZ_ASSERT(protoKey); + + // While it may seem like an invariant that in any compartment, + // seeing a typed array object implies that the TypedArray constructor + // for that type is initialized on the compartment's global, this is not + // the case. When we construct a typed array given a cross-compartment + // ArrayBuffer, we put the constructed TypedArray in the same compartment + // as the ArrayBuffer. Since we use the prototype from the initial + // compartment, and never call the constructor in the ArrayBuffer's + // compartment from script, we are not guaranteed to have initialized + // the constructor. + JSObject* ctor = GlobalObject::getOrCreateConstructor(cx, protoKey); + if (!ctor) { + return false; + } + + args.rval().setObject(*ctor); + return true; +} + +static bool intrinsic_HostResolveImportedModule(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>()); + RootedString specifier(cx, args[1].toString()); + + RootedValue referencingPrivate(cx, JS::GetModulePrivate(module)); + RootedObject result(cx, + CallModuleResolveHook(cx, referencingPrivate, specifier)); + if (!result) { + return false; + } + + if (!result->is<ModuleObject>()) { + JS_ReportErrorASCII(cx, "Module resolve hook did not return Module object"); + return false; + } + + args.rval().setObject(*result); + return true; +} + +static bool intrinsic_CreateImportBinding(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 4); + RootedModuleEnvironmentObject environment( + cx, &args[0].toObject().as<ModuleEnvironmentObject>()); + RootedAtom importedName(cx, &args[1].toString()->asAtom()); + RootedModuleObject module(cx, &args[2].toObject().as<ModuleObject>()); + RootedAtom localName(cx, &args[3].toString()->asAtom()); + if (!environment->createImportBinding(cx, importedName, module, localName)) { + return false; + } + + args.rval().setUndefined(); + return true; +} + +static bool intrinsic_CreateNamespaceBinding(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 3); + RootedModuleEnvironmentObject environment( + cx, &args[0].toObject().as<ModuleEnvironmentObject>()); + RootedId name(cx, AtomToId(&args[1].toString()->asAtom())); + MOZ_ASSERT(args[2].toObject().is<ModuleNamespaceObject>()); + // The property already exists in the evironment but is not writable, so set + // the slot directly. + RootedShape shape(cx, environment->lookup(cx, name)); + MOZ_ASSERT(shape); + environment->setSlot(shape->slot(), args[2]); + args.rval().setUndefined(); + return true; +} + +static bool intrinsic_EnsureModuleEnvironmentNamespace(JSContext* cx, + unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>()); + MOZ_ASSERT(args[1].toObject().is<ModuleNamespaceObject>()); + RootedModuleEnvironmentObject environment(cx, &module->initialEnvironment()); + // The property already exists in the evironment but is not writable, so set + // the slot directly. + RootedShape shape(cx, environment->lookup(cx, cx->names().starNamespaceStar)); + MOZ_ASSERT(shape); + environment->setSlot(shape->slot(), args[1]); + args.rval().setUndefined(); + return true; +} + +static bool intrinsic_InstantiateModuleFunctionDeclarations(JSContext* cx, + unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>()); + args.rval().setUndefined(); + return ModuleObject::instantiateFunctionDeclarations(cx, module); +} + +static bool intrinsic_ExecuteModule(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>()); + return ModuleObject::execute(cx, module, args.rval()); +} + +static bool intrinsic_IsTopLevelAwaitEnabled(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + bool topLevelAwait = cx->options().topLevelAwait(); + args.rval().setBoolean(topLevelAwait); + return true; +} + +static bool intrinsic_GetAsyncCycleRoot(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>()); + JSObject* result = js::GetAsyncCycleRoot(module); + if (!result) { + return false; + } + args.rval().setObject(*result); + return true; +} + +static bool intrinsic_AppendAsyncParentModule(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + RootedModuleObject self(cx, &args[0].toObject().as<ModuleObject>()); + RootedModuleObject parent(cx, &args[1].toObject().as<ModuleObject>()); + return ModuleObject::appendAsyncParentModule(cx, self, parent); +} + +static bool intrinsic_CreateTopLevelCapability(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + RootedModuleObject self(cx, &args[0].toObject().as<ModuleObject>()); + PromiseObject* result = ModuleObject::createTopLevelCapability(cx, self); + if (!result) { + return false; + } + args.rval().setObject(*result); + return true; +} + +static bool intrinsic_ModuleTopLevelCapabilityResolve(JSContext* cx, + unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>()); + ModuleObject::topLevelCapabilityResolve(cx, module); + args.rval().setUndefined(); + return true; +} + +static bool intrinsic_ModuleTopLevelCapabilityReject(JSContext* cx, + unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>()); + HandleValue error = args[1]; + ModuleObject::topLevelCapabilityReject(cx, module, error); + args.rval().setUndefined(); + return true; +} + +static bool intrinsic_NewModuleNamespace(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>()); + RootedObject exports(cx, &args[1].toObject()); + JSObject* namespace_ = ModuleObject::createNamespace(cx, module, exports); + if (!namespace_) { + return false; + } + + args.rval().setObject(*namespace_); + return true; +} + +static bool intrinsic_AddModuleNamespaceBinding(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 4); + RootedModuleNamespaceObject namespace_( + cx, &args[0].toObject().as<ModuleNamespaceObject>()); + RootedAtom exportedName(cx, &args[1].toString()->asAtom()); + RootedModuleObject targetModule(cx, &args[2].toObject().as<ModuleObject>()); + RootedAtom localName(cx, &args[3].toString()->asAtom()); + if (!namespace_->addBinding(cx, exportedName, targetModule, localName)) { + return false; + } + + args.rval().setUndefined(); + return true; +} + +static bool intrinsic_ModuleNamespaceExports(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + RootedModuleNamespaceObject namespace_( + cx, &args[0].toObject().as<ModuleNamespaceObject>()); + args.rval().setObject(namespace_->exports()); + return true; +} + +static bool intrinsic_PromiseResolve(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + + RootedObject constructor(cx, &args[0].toObject()); + JSObject* promise = js::PromiseResolve(cx, constructor, args[1]); + if (!promise) { + return false; + } + + args.rval().setObject(*promise); + return true; +} + +static bool intrinsic_CopyDataPropertiesOrGetOwnKeys(JSContext* cx, + unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 3); + MOZ_ASSERT(args[0].isObject()); + MOZ_ASSERT(args[1].isObject()); + MOZ_ASSERT(args[2].isObjectOrNull()); + + RootedObject target(cx, &args[0].toObject()); + RootedObject from(cx, &args[1].toObject()); + RootedObject excludedItems(cx, args[2].toObjectOrNull()); + + if (from->isNative() && target->is<PlainObject>() && + (!excludedItems || excludedItems->is<PlainObject>())) { + bool optimized; + if (!CopyDataPropertiesNative( + cx, target.as<PlainObject>(), from.as<NativeObject>(), + (excludedItems ? excludedItems.as<PlainObject>() : nullptr), + &optimized)) { + return false; + } + + if (optimized) { + args.rval().setNull(); + return true; + } + } + + return GetOwnPropertyKeys( + cx, from, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, args.rval()); +} + +static bool intrinsic_ToBigInt(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + BigInt* res = ToBigInt(cx, args[0]); + if (!res) { + return false; + } + args.rval().setBigInt(res); + return true; +} + +static bool intrinsic_NewWrapForValidIterator(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + JSObject* obj = NewWrapForValidIterator(cx); + if (!obj) { + return false; + } + + args.rval().setObject(*obj); + return true; +} + +static bool intrinsic_NewPrivateName(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + + RootedString desc(cx, args[0].toString()); + auto* symbol = JS::Symbol::new_(cx, JS::SymbolCode::PrivateNameSymbol, desc); + if (!symbol) { + return false; + } + args.rval().setSymbol(symbol); + return true; +} + +static bool intrinsic_NewIteratorHelper(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + JSObject* obj = NewIteratorHelper(cx); + if (!obj) { + return false; + } + + args.rval().setObject(*obj); + return true; +} + +static bool intrinsic_NewAsyncIteratorHelper(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + JSObject* obj = NewAsyncIteratorHelper(cx); + if (!obj) { + return false; + } + + args.rval().setObject(*obj); + return true; +} + +static bool intrinsic_NoPrivateGetter(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_PRIVATE_SETTER_ONLY); + + args.rval().setUndefined(); + return false; +} + +// The self-hosting global isn't initialized with the normal set of builtins. +// Instead, individual C++-implemented functions that're required by +// self-hosted code are defined as global functions. Accessing these +// functions via a content compartment's builtins would be unsafe, because +// content script might have changed the builtins' prototypes' members. +// Installing the whole set of builtins in the self-hosting compartment, OTOH, +// would be wasteful: it increases memory usage and initialization time for +// self-hosting compartment. +// +// Additionally, a set of C++-implemented helper functions is defined on the +// self-hosting global. +static const JSFunctionSpec intrinsic_functions[] = { + JS_INLINABLE_FN("std_Array", array_construct, 1, 0, Array), + JS_INLINABLE_FN("std_Array_push", array_push, 1, 0, ArrayPush), + JS_INLINABLE_FN("std_Array_pop", array_pop, 0, 0, ArrayPop), + JS_FN("ArrayNativeSort", intrinsic_ArrayNativeSort, 1, 0), + + JS_FN("std_BigInt_valueOf", BigIntObject::valueOf, 0, 0), + + JS_FN("std_Date_now", date_now, 0, 0), + JS_FN("std_Date_valueOf", date_valueOf, 0, 0), + + JS_FN("std_Function_apply", fun_apply, 2, 0), + + JS_INLINABLE_FN("std_Math_floor", math_floor, 1, 0, MathFloor), + JS_INLINABLE_FN("std_Math_max", math_max, 2, 0, MathMax), + JS_INLINABLE_FN("std_Math_min", math_min, 2, 0, MathMin), + JS_INLINABLE_FN("std_Math_abs", math_abs, 1, 0, MathAbs), + JS_INLINABLE_FN("std_Math_trunc", math_trunc, 1, 0, MathTrunc), + + JS_FN("std_Map_entries", MapObject::entries, 0, 0), + + JS_INLINABLE_FN("std_Object_create", obj_create, 2, 0, ObjectCreate), + JS_FN("std_Object_propertyIsEnumerable", obj_propertyIsEnumerable, 1, 0), + JS_FN("std_Object_toString", obj_toString, 0, 0), + JS_FN("std_Object_setProto", obj_setProto, 1, 0), + + JS_INLINABLE_FN("std_Reflect_getPrototypeOf", Reflect_getPrototypeOf, 1, 0, + ReflectGetPrototypeOf), + JS_FN("std_Reflect_isExtensible", Reflect_isExtensible, 1, 0), + JS_FN("std_Reflect_ownKeys", Reflect_ownKeys, 1, 0), + + JS_FN("std_Set_values", SetObject::values, 0, 0), + + JS_INLINABLE_FN("std_String_fromCharCode", str_fromCharCode, 1, 0, + StringFromCharCode), + JS_INLINABLE_FN("std_String_fromCodePoint", str_fromCodePoint, 1, 0, + StringFromCodePoint), + JS_INLINABLE_FN("std_String_charCodeAt", str_charCodeAt, 1, 0, + StringCharCodeAt), + JS_FN("std_String_includes", str_includes, 1, 0), + JS_FN("std_String_indexOf", str_indexOf, 1, 0), + JS_FN("std_String_startsWith", str_startsWith, 1, 0), + JS_FN("std_String_endsWith", str_endsWith, 1, 0), + + JS_FN("std_TypedArray_buffer", js::TypedArray_bufferGetter, 1, 0), + + // Helper funtions after this point. + JS_INLINABLE_FN("ToObject", intrinsic_ToObject, 1, 0, IntrinsicToObject), + JS_INLINABLE_FN("IsObject", intrinsic_IsObject, 1, 0, IntrinsicIsObject), + JS_INLINABLE_FN("IsArray", intrinsic_IsArray, 1, 0, ArrayIsArray), + JS_INLINABLE_FN("IsCrossRealmArrayConstructor", + intrinsic_IsCrossRealmArrayConstructor, 1, 0, + IntrinsicIsCrossRealmArrayConstructor), + JS_INLINABLE_FN("ToInteger", intrinsic_ToInteger, 1, 0, IntrinsicToInteger), + JS_INLINABLE_FN("ToLength", intrinsic_ToLength, 1, 0, IntrinsicToLength), + JS_FN("ToSource", intrinsic_ToSource, 1, 0), + JS_FN("ToPropertyKey", intrinsic_ToPropertyKey, 1, 0), + JS_INLINABLE_FN("IsCallable", intrinsic_IsCallable, 1, 0, + IntrinsicIsCallable), + JS_INLINABLE_FN("IsConstructor", intrinsic_IsConstructor, 1, 0, + IntrinsicIsConstructor), + JS_FN("MakeConstructible", intrinsic_MakeConstructible, 2, 0), + JS_FN("_ConstructFunction", intrinsic_ConstructFunction, 2, 0), + JS_FN("ThrowRangeError", intrinsic_ThrowRangeError, 4, 0), + JS_FN("ThrowTypeError", intrinsic_ThrowTypeError, 4, 0), + JS_FN("ThrowSyntaxError", intrinsic_ThrowSyntaxError, 4, 0), + JS_FN("ThrowAggregateError", intrinsic_ThrowAggregateError, 4, 0), + JS_FN("ThrowInternalError", intrinsic_ThrowInternalError, 4, 0), + JS_FN("GetErrorMessage", intrinsic_GetErrorMessage, 1, 0), + JS_FN("CreateModuleSyntaxError", intrinsic_CreateModuleSyntaxError, 4, 0), + JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1, 0), + JS_FN("DumpMessage", intrinsic_DumpMessage, 1, 0), + JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1, + 0), + JS_FN("DecompileArg", intrinsic_DecompileArg, 2, 0), + JS_INLINABLE_FN("_FinishBoundFunctionInit", + intrinsic_FinishBoundFunctionInit, 3, 0, + IntrinsicFinishBoundFunctionInit), + JS_FN("_DefineDataProperty", intrinsic_DefineDataProperty, 4, 0), + JS_FN("_DefineProperty", intrinsic_DefineProperty, 6, 0), + JS_FN("CopyDataPropertiesOrGetOwnKeys", + intrinsic_CopyDataPropertiesOrGetOwnKeys, 3, 0), + JS_INLINABLE_FN("SameValue", js::obj_is, 2, 0, ObjectIs), + + JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing, 0, 0, + IntrinsicIsConstructing), + JS_INLINABLE_FN("SubstringKernel", intrinsic_SubstringKernel, 3, 0, + IntrinsicSubstringKernel), + JS_INLINABLE_FN("ObjectHasPrototype", intrinsic_ObjectHasPrototype, 2, 0, + IntrinsicObjectHasPrototype), + JS_INLINABLE_FN("UnsafeSetReservedSlot", intrinsic_UnsafeSetReservedSlot, 3, + 0, IntrinsicUnsafeSetReservedSlot), + JS_INLINABLE_FN("UnsafeGetReservedSlot", intrinsic_UnsafeGetReservedSlot, 2, + 0, IntrinsicUnsafeGetReservedSlot), + JS_INLINABLE_FN("UnsafeGetObjectFromReservedSlot", + intrinsic_UnsafeGetObjectFromReservedSlot, 2, 0, + IntrinsicUnsafeGetObjectFromReservedSlot), + JS_INLINABLE_FN("UnsafeGetInt32FromReservedSlot", + intrinsic_UnsafeGetInt32FromReservedSlot, 2, 0, + IntrinsicUnsafeGetInt32FromReservedSlot), + JS_INLINABLE_FN("UnsafeGetStringFromReservedSlot", + intrinsic_UnsafeGetStringFromReservedSlot, 2, 0, + IntrinsicUnsafeGetStringFromReservedSlot), + JS_INLINABLE_FN("UnsafeGetBooleanFromReservedSlot", + intrinsic_UnsafeGetBooleanFromReservedSlot, 2, 0, + IntrinsicUnsafeGetBooleanFromReservedSlot), + + JS_FN("ThisNumberValueForToLocaleString", ThisNumberValueForToLocaleString, + 0, 0), + + JS_FN("ThisTimeValue", intrinsic_ThisTimeValue, 1, 0), + + JS_INLINABLE_FN("IsPackedArray", intrinsic_IsPackedArray, 1, 0, + IntrinsicIsPackedArray), + + JS_INLINABLE_FN("NewArrayIterator", intrinsic_NewArrayIterator, 0, 0, + IntrinsicNewArrayIterator), + JS_INLINABLE_FN("ArrayIteratorPrototypeOptimizable", + intrinsic_ArrayIteratorPrototypeOptimizable, 0, 0, + IntrinsicArrayIteratorPrototypeOptimizable), + + JS_FN("CallArrayIteratorMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<ArrayIteratorObject>>, 2, 0), + + JS_FN("_SetCanonicalName", intrinsic_SetCanonicalName, 2, 0), + JS_FN("_SetIsInlinableLargeFunction", intrinsic_SetIsInlinableLargeFunction, + 1, 0), + + JS_INLINABLE_FN("GuardToArrayIterator", + intrinsic_GuardToBuiltin<ArrayIteratorObject>, 1, 0, + IntrinsicGuardToArrayIterator), + JS_INLINABLE_FN("GuardToMapIterator", + intrinsic_GuardToBuiltin<MapIteratorObject>, 1, 0, + IntrinsicGuardToMapIterator), + JS_INLINABLE_FN("GuardToSetIterator", + intrinsic_GuardToBuiltin<SetIteratorObject>, 1, 0, + IntrinsicGuardToSetIterator), + JS_INLINABLE_FN("GuardToStringIterator", + intrinsic_GuardToBuiltin<StringIteratorObject>, 1, 0, + IntrinsicGuardToStringIterator), + JS_INLINABLE_FN("GuardToRegExpStringIterator", + intrinsic_GuardToBuiltin<RegExpStringIteratorObject>, 1, 0, + IntrinsicGuardToRegExpStringIterator), + + JS_FN("_CreateMapIterationResultPair", + intrinsic_CreateMapIterationResultPair, 0, 0), + JS_INLINABLE_FN("_GetNextMapEntryForIterator", + intrinsic_GetNextMapEntryForIterator, 2, 0, + IntrinsicGetNextMapEntryForIterator), + JS_FN("CallMapIteratorMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<MapIteratorObject>>, 2, 0), + + JS_FN("_CreateSetIterationResult", intrinsic_CreateSetIterationResult, 0, + 0), + JS_INLINABLE_FN("_GetNextSetEntryForIterator", + intrinsic_GetNextSetEntryForIterator, 2, 0, + IntrinsicGetNextSetEntryForIterator), + JS_FN("CallSetIteratorMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<SetIteratorObject>>, 2, 0), + + JS_INLINABLE_FN("NewStringIterator", intrinsic_NewStringIterator, 0, 0, + IntrinsicNewStringIterator), + JS_FN("CallStringIteratorMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<StringIteratorObject>>, 2, 0), + + JS_INLINABLE_FN("NewRegExpStringIterator", + intrinsic_NewRegExpStringIterator, 0, 0, + IntrinsicNewRegExpStringIterator), + JS_FN("CallRegExpStringIteratorMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<RegExpStringIteratorObject>>, 2, 0), + + JS_FN("IsGeneratorObject", intrinsic_IsInstanceOfBuiltin<GeneratorObject>, + 1, 0), + JS_FN("GeneratorObjectIsClosed", intrinsic_GeneratorObjectIsClosed, 1, 0), + JS_INLINABLE_FN("IsSuspendedGenerator", intrinsic_IsSuspendedGenerator, 1, + 0, IntrinsicIsSuspendedGenerator), + + JS_FN("GeneratorIsRunning", intrinsic_GeneratorIsRunning, 1, 0), + JS_FN("GeneratorSetClosed", intrinsic_GeneratorSetClosed, 1, 0), + + JS_FN("IsAsyncFunctionGeneratorObject", + intrinsic_IsInstanceOfBuiltin<AsyncFunctionGeneratorObject>, 1, 0), + JS_FN("IsAsyncGeneratorObject", + intrinsic_IsInstanceOfBuiltin<AsyncGeneratorObject>, 1, 0), + + JS_INLINABLE_FN("GuardToArrayBuffer", + intrinsic_GuardToBuiltin<ArrayBufferObject>, 1, 0, + IntrinsicGuardToArrayBuffer), + JS_INLINABLE_FN("GuardToSharedArrayBuffer", + intrinsic_GuardToBuiltin<SharedArrayBufferObject>, 1, 0, + IntrinsicGuardToSharedArrayBuffer), + JS_FN("IsWrappedArrayBuffer", + intrinsic_IsWrappedInstanceOfBuiltin<ArrayBufferObject>, 1, 0), + JS_FN("IsWrappedSharedArrayBuffer", + intrinsic_IsWrappedInstanceOfBuiltin<SharedArrayBufferObject>, 1, 0), + + JS_INLINABLE_FN("ArrayBufferByteLength", + intrinsic_ArrayBufferByteLength<ArrayBufferObject>, 1, 0, + IntrinsicArrayBufferByteLength), + JS_INLINABLE_FN( + "PossiblyWrappedArrayBufferByteLength", + intrinsic_PossiblyWrappedArrayBufferByteLength<ArrayBufferObject>, 1, 0, + IntrinsicPossiblyWrappedArrayBufferByteLength), + JS_FN("ArrayBufferCopyData", + intrinsic_ArrayBufferCopyData<ArrayBufferObject>, 6, 0), + + JS_FN("SharedArrayBufferByteLength", + intrinsic_ArrayBufferByteLength<SharedArrayBufferObject>, 1, 0), + JS_FN( + "PossiblyWrappedSharedArrayBufferByteLength", + intrinsic_PossiblyWrappedArrayBufferByteLength<SharedArrayBufferObject>, + 1, 0), + JS_FN("SharedArrayBufferCopyData", + intrinsic_ArrayBufferCopyData<SharedArrayBufferObject>, 6, 0), + JS_FN("SharedArrayBuffersMemorySame", + intrinsic_SharedArrayBuffersMemorySame, 2, 0), + + JS_FN("GetTypedArrayKind", intrinsic_GetTypedArrayKind, 1, 0), + JS_INLINABLE_FN("IsTypedArray", + intrinsic_IsInstanceOfBuiltin<TypedArrayObject>, 1, 0, + IntrinsicIsTypedArray), + JS_INLINABLE_FN( + "IsPossiblyWrappedTypedArray", + intrinsic_IsPossiblyWrappedInstanceOfBuiltin<TypedArrayObject>, 1, 0, + IntrinsicIsPossiblyWrappedTypedArray), + JS_INLINABLE_FN("IsTypedArrayConstructor", + intrinsic_IsTypedArrayConstructor, 1, 0, + IntrinsicIsTypedArrayConstructor), + + JS_FN("TypedArrayBuffer", intrinsic_TypedArrayBuffer, 1, 0), + JS_INLINABLE_FN("TypedArrayByteOffset", intrinsic_TypedArrayByteOffset, 1, + 0, IntrinsicTypedArrayByteOffset), + JS_INLINABLE_FN("TypedArrayElementShift", intrinsic_TypedArrayElementShift, + 1, 0, IntrinsicTypedArrayElementShift), + + JS_INLINABLE_FN("TypedArrayLength", intrinsic_TypedArrayLength, 1, 0, + IntrinsicTypedArrayLength), + JS_INLINABLE_FN("PossiblyWrappedTypedArrayLength", + intrinsic_PossiblyWrappedTypedArrayLength, 1, 0, + IntrinsicPossiblyWrappedTypedArrayLength), + JS_FN("PossiblyWrappedTypedArrayHasDetachedBuffer", + intrinsic_PossiblyWrappedTypedArrayHasDetachedBuffer, 1, 0), + + JS_FN("TypedArrayBitwiseSlice", intrinsic_TypedArrayBitwiseSlice, 4, 0), + JS_FN("TypedArrayInitFromPackedArray", + intrinsic_TypedArrayInitFromPackedArray, 2, 0), + + JS_FN("CallArrayBufferMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<ArrayBufferObject>>, 2, 0), + JS_FN("CallSharedArrayBufferMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<SharedArrayBufferObject>>, 2, 0), + JS_FN("CallTypedArrayMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<TypedArrayObject>>, 2, 0), + + JS_FN("CallGeneratorMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<GeneratorObject>>, 2, 0), + + JS_INLINABLE_FN("GuardToMapObject", intrinsic_GuardToBuiltin<MapObject>, 1, + 0, IntrinsicGuardToMapObject), + JS_FN("CallMapMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<MapObject>>, 2, 0), + + JS_INLINABLE_FN("GuardToSetObject", intrinsic_GuardToBuiltin<SetObject>, 1, + 0, IntrinsicGuardToSetObject), + JS_FN("CallSetMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<SetObject>>, 2, 0), + +#ifdef JS_HAS_INTL_API + // See builtin/intl/*.h for descriptions of the intl_* functions. + JS_FN("intl_availableCalendars", intl_availableCalendars, 1, 0), + JS_FN("intl_availableCollations", intl_availableCollations, 1, 0), +# if DEBUG || MOZ_SYSTEM_ICU + JS_FN("intl_availableMeasurementUnits", intl_availableMeasurementUnits, 0, + 0), +# endif + JS_FN("intl_BestAvailableLocale", intl_BestAvailableLocale, 3, 0), + JS_FN("intl_canonicalizeTimeZone", intl_canonicalizeTimeZone, 1, 0), + JS_FN("intl_Collator", intl_Collator, 2, 0), + JS_FN("intl_CompareStrings", intl_CompareStrings, 3, 0), + JS_FN("intl_DateTimeFormat", intl_DateTimeFormat, 2, 0), + JS_FN("intl_defaultCalendar", intl_defaultCalendar, 1, 0), + JS_FN("intl_supportedLocaleOrFallback", intl_supportedLocaleOrFallback, 1, + 0), + JS_FN("intl_defaultTimeZone", intl_defaultTimeZone, 0, 0), + JS_FN("intl_defaultTimeZoneOffset", intl_defaultTimeZoneOffset, 0, 0), + JS_FN("intl_isDefaultTimeZone", intl_isDefaultTimeZone, 1, 0), + JS_FN("intl_FormatDateTime", intl_FormatDateTime, 2, 0), + JS_FN("intl_FormatDateTimeRange", intl_FormatDateTimeRange, 4, 0), + JS_FN("intl_FormatNumber", intl_FormatNumber, 2, 0), + JS_FN("intl_GetCalendarInfo", intl_GetCalendarInfo, 1, 0), + JS_FN("intl_GetLocaleInfo", intl_GetLocaleInfo, 1, 0), + JS_FN("intl_ComputeDisplayNames", intl_ComputeDisplayNames, 3, 0), + JS_FN("intl_ComputeDisplayName", intl_ComputeDisplayName, 6, 0), + JS_FN("intl_isUpperCaseFirst", intl_isUpperCaseFirst, 1, 0), + JS_FN("intl_IsValidTimeZoneName", intl_IsValidTimeZoneName, 1, 0), + JS_FN("intl_NumberFormat", intl_NumberFormat, 2, 0), + JS_FN("intl_numberingSystem", intl_numberingSystem, 1, 0), + JS_FN("intl_patternForSkeleton", intl_patternForSkeleton, 3, 0), + JS_FN("intl_patternForStyle", intl_patternForStyle, 6, 0), + JS_FN("intl_skeletonForPattern", intl_skeletonForPattern, 1, 0), + JS_FN("intl_GetPluralCategories", intl_GetPluralCategories, 1, 0), + JS_FN("intl_SelectPluralRule", intl_SelectPluralRule, 2, 0), + JS_FN("intl_FormatRelativeTime", intl_FormatRelativeTime, 4, 0), + JS_FN("intl_FormatList", intl_FormatList, 3, 0), + JS_FN("intl_toLocaleLowerCase", intl_toLocaleLowerCase, 2, 0), + JS_FN("intl_toLocaleUpperCase", intl_toLocaleUpperCase, 2, 0), + JS_FN("intl_ValidateAndCanonicalizeLanguageTag", + intl_ValidateAndCanonicalizeLanguageTag, 2, 0), + JS_FN("intl_TryValidateAndCanonicalizeLanguageTag", + intl_TryValidateAndCanonicalizeLanguageTag, 1, 0), + JS_FN("intl_ValidateAndCanonicalizeUnicodeExtensionType", + intl_ValidateAndCanonicalizeUnicodeExtensionType, 3, 0), + + JS_INLINABLE_FN("GuardToCollator", intrinsic_GuardToBuiltin<CollatorObject>, + 1, 0, IntlGuardToCollator), + JS_INLINABLE_FN("GuardToDateTimeFormat", + intrinsic_GuardToBuiltin<DateTimeFormatObject>, 1, 0, + IntlGuardToDateTimeFormat), + JS_INLINABLE_FN("GuardToDisplayNames", + intrinsic_GuardToBuiltin<DisplayNamesObject>, 1, 0, + IntlGuardToDisplayNames), + JS_INLINABLE_FN("GuardToListFormat", + intrinsic_GuardToBuiltin<ListFormatObject>, 1, 0, + IntlGuardToListFormat), + JS_INLINABLE_FN("GuardToNumberFormat", + intrinsic_GuardToBuiltin<NumberFormatObject>, 1, 0, + IntlGuardToNumberFormat), + JS_INLINABLE_FN("GuardToPluralRules", + intrinsic_GuardToBuiltin<PluralRulesObject>, 1, 0, + IntlGuardToPluralRules), + JS_INLINABLE_FN("GuardToRelativeTimeFormat", + intrinsic_GuardToBuiltin<RelativeTimeFormatObject>, 1, 0, + IntlGuardToRelativeTimeFormat), + + JS_FN("IsWrappedDateTimeFormat", + intrinsic_IsWrappedInstanceOfBuiltin<DateTimeFormatObject>, 1, 0), + JS_FN("IsWrappedNumberFormat", + intrinsic_IsWrappedInstanceOfBuiltin<NumberFormatObject>, 1, 0), + + JS_FN("CallCollatorMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<CollatorObject>>, 2, 0), + JS_FN("CallDateTimeFormatMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<DateTimeFormatObject>>, 2, 0), + JS_FN("CallDisplayNamesMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<DisplayNamesObject>>, 2, 0), + JS_FN("CallListFormatMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<ListFormatObject>>, 2, 0), + JS_FN("CallNumberFormatMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<NumberFormatObject>>, 2, 0), + JS_FN("CallPluralRulesMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<PluralRulesObject>>, 2, 0), + JS_FN("CallRelativeTimeFormatMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<RelativeTimeFormatObject>>, 2, 0), + + JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0, 0), + JS_FN("IsRuntimeDefaultLocale", intrinsic_IsRuntimeDefaultLocale, 1, 0), +#endif // JS_HAS_INTL_API + + JS_FN("GetOwnPropertyDescriptorToArray", GetOwnPropertyDescriptorToArray, 2, + 0), + + JS_INLINABLE_FN("IsRegExpObject", + intrinsic_IsInstanceOfBuiltin<RegExpObject>, 1, 0, + IsRegExpObject), + JS_INLINABLE_FN("IsPossiblyWrappedRegExpObject", + intrinsic_IsPossiblyWrappedInstanceOfBuiltin<RegExpObject>, + 1, 0, IsPossiblyWrappedRegExpObject), + JS_FN("CallRegExpMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<RegExpObject>>, 2, 0), + JS_INLINABLE_FN("RegExpMatcher", RegExpMatcher, 3, 0, RegExpMatcher), + JS_INLINABLE_FN("RegExpSearcher", RegExpSearcher, 3, 0, RegExpSearcher), + JS_INLINABLE_FN("RegExpTester", RegExpTester, 3, 0, RegExpTester), + JS_FN("RegExpCreate", intrinsic_RegExpCreate, 2, 0), + JS_INLINABLE_FN("RegExpPrototypeOptimizable", RegExpPrototypeOptimizable, 1, + 0, RegExpPrototypeOptimizable), + JS_INLINABLE_FN("RegExpInstanceOptimizable", RegExpInstanceOptimizable, 1, + 0, RegExpInstanceOptimizable), + JS_FN("RegExpGetSubstitution", intrinsic_RegExpGetSubstitution, 5, 0), + JS_FN("GetElemBaseForLambda", intrinsic_GetElemBaseForLambda, 1, 0), + JS_FN("GetStringDataProperty", intrinsic_GetStringDataProperty, 2, 0), + JS_INLINABLE_FN("GetFirstDollarIndex", GetFirstDollarIndex, 1, 0, + GetFirstDollarIndex), + + JS_FN("FlatStringMatch", FlatStringMatch, 2, 0), + JS_FN("FlatStringSearch", FlatStringSearch, 2, 0), + JS_INLINABLE_FN("StringReplaceString", intrinsic_StringReplaceString, 3, 0, + IntrinsicStringReplaceString), + JS_FN("StringReplaceAllString", intrinsic_StringReplaceAllString, 3, 0), + JS_INLINABLE_FN("StringSplitString", intrinsic_StringSplitString, 2, 0, + IntrinsicStringSplitString), + JS_FN("StringSplitStringLimit", intrinsic_StringSplitStringLimit, 3, 0), + JS_FN("ThrowArgTypeNotObject", intrinsic_ThrowArgTypeNotObject, 2, 0), + + // See builtin/RegExp.h for descriptions of the regexp_* functions. + JS_FN("regexp_construct_raw_flags", regexp_construct_raw_flags, 2, 0), + + JS_FN("IsModule", intrinsic_IsInstanceOfBuiltin<ModuleObject>, 1, 0), + JS_FN("CallModuleMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<ModuleObject>>, 2, 0), + JS_FN("HostResolveImportedModule", intrinsic_HostResolveImportedModule, 2, + 0), + JS_FN("IsModuleEnvironment", + intrinsic_IsInstanceOfBuiltin<ModuleEnvironmentObject>, 1, 0), + JS_FN("CreateImportBinding", intrinsic_CreateImportBinding, 4, 0), + JS_FN("CreateNamespaceBinding", intrinsic_CreateNamespaceBinding, 3, 0), + JS_FN("EnsureModuleEnvironmentNamespace", + intrinsic_EnsureModuleEnvironmentNamespace, 1, 0), + JS_FN("InstantiateModuleFunctionDeclarations", + intrinsic_InstantiateModuleFunctionDeclarations, 1, 0), + JS_FN("ExecuteModule", intrinsic_ExecuteModule, 1, 0), + JS_FN("IsTopLevelAwaitEnabled", intrinsic_IsTopLevelAwaitEnabled, 0, 0), + JS_FN("GetAsyncCycleRoot", intrinsic_GetAsyncCycleRoot, 1, 0), + JS_FN("AppendAsyncParentModule", intrinsic_AppendAsyncParentModule, 2, 0), + JS_FN("CreateTopLevelCapability", intrinsic_CreateTopLevelCapability, 1, 0), + JS_FN("ModuleTopLevelCapabilityResolve", + intrinsic_ModuleTopLevelCapabilityResolve, 1, 0), + JS_FN("ModuleTopLevelCapabilityReject", + intrinsic_ModuleTopLevelCapabilityReject, 2, 0), + JS_FN("NewModuleNamespace", intrinsic_NewModuleNamespace, 2, 0), + JS_FN("AddModuleNamespaceBinding", intrinsic_AddModuleNamespaceBinding, 4, + 0), + JS_FN("ModuleNamespaceExports", intrinsic_ModuleNamespaceExports, 1, 0), + + JS_FN("PromiseResolve", intrinsic_PromiseResolve, 2, 0), + + JS_FN("ToBigInt", intrinsic_ToBigInt, 1, 0), + + JS_FN("NewWrapForValidIterator", intrinsic_NewWrapForValidIterator, 0, 0), + JS_INLINABLE_FN("GuardToWrapForValidIterator", + intrinsic_GuardToBuiltin<WrapForValidIteratorObject>, 1, 0, + IntrinsicGuardToWrapForValidIterator), + JS_FN("CallWrapForValidIteratorMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<WrapForValidIteratorObject>>, 2, 0), + JS_FN("NewIteratorHelper", intrinsic_NewIteratorHelper, 0, 0), + JS_INLINABLE_FN("GuardToIteratorHelper", + intrinsic_GuardToBuiltin<IteratorHelperObject>, 1, 0, + IntrinsicGuardToIteratorHelper), + JS_FN("CallIteratorHelperMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<IteratorHelperObject>>, 2, 0), + JS_FN("NewAsyncIteratorHelper", intrinsic_NewAsyncIteratorHelper, 0, 0), + JS_INLINABLE_FN("GuardToAsyncIteratorHelper", + intrinsic_GuardToBuiltin<AsyncIteratorHelperObject>, 1, 0, + IntrinsicGuardToAsyncIteratorHelper), + JS_FN("CallAsyncIteratorHelperMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<AsyncIteratorHelperObject>>, 2, 0), + JS_FN("IntrinsicAsyncGeneratorNext", AsyncGeneratorNext, 1, 0), + JS_FN("IntrinsicAsyncGeneratorReturn", AsyncGeneratorReturn, 1, 0), + JS_FN("IntrinsicAsyncGeneratorThrow", AsyncGeneratorThrow, 1, 0), + + JS_FN("NewPrivateName", intrinsic_NewPrivateName, 1, 0), + JS_FN("NoPrivateGetter", intrinsic_NoPrivateGetter, 1, 0), + + JS_FS_END}; + +void js::FillSelfHostingCompileOptions(CompileOptions& options) { + /* + * In self-hosting mode, scripts use JSOp::GetIntrinsic instead of + * JSOp::GetName or JSOp::GetGName to access unbound variables. + * JSOp::GetIntrinsic does a name lookup on a special object, whose + * properties are filled in lazily upon first access for a given global. + * + * As that object is inaccessible to client code, the lookups are + * guaranteed to return the original objects, ensuring safe implementation + * of self-hosted builtins. + * + * Additionally, the special syntax callFunction(fun, receiver, ...args) + * is supported, for which bytecode is emitted that invokes |fun| with + * |receiver| as the this-object and ...args as the arguments. + */ + options.setIntroductionType("self-hosted"); + options.setFileAndLine("self-hosted", 1); + options.setSkipFilenameValidation(true); + options.setSelfHostingMode(true); + options.setForceFullParse(); + options.setForceStrictMode(); + options.setIsRunOnce(true); +} + +GlobalObject* JSRuntime::createSelfHostingGlobal(JSContext* cx) { + MOZ_ASSERT(!cx->isExceptionPending()); + MOZ_ASSERT(!cx->realm()); + + JS::RealmOptions options; + options.creationOptions().setNewCompartmentInSelfHostingZone(); + // Debugging the selfHosted zone is not supported because CCWs are not + // allowed in that zone. + options.creationOptions().setInvisibleToDebugger(true); + options.behaviors().setDiscardSource(true); + + Realm* realm = NewRealm(cx, nullptr, options); + if (!realm) { + return nullptr; + } + + static const JSClassOps shgClassOps = { + nullptr, // addProperty + nullptr, // delProperty + nullptr, // enumerate + nullptr, // newEnumerate + nullptr, // resolve + nullptr, // mayResolve + nullptr, // finalize + nullptr, // call + nullptr, // hasInstance + nullptr, // construct + JS_GlobalObjectTraceHook, // trace + }; + + static const JSClass shgClass = {"self-hosting-global", JSCLASS_GLOBAL_FLAGS, + &shgClassOps}; + + AutoRealmUnchecked ar(cx, realm); + Rooted<GlobalObject*> shg(cx, GlobalObject::createInternal(cx, &shgClass)); + if (!shg) { + return nullptr; + } + + cx->runtime()->selfHostingGlobal_ = shg; + MOZ_ASSERT(realm->zone()->isSelfHostingZone()); + realm->setIsSelfHostingRealm(); + + if (!GlobalObject::initSelfHostingBuiltins(cx, shg, intrinsic_functions)) { + return nullptr; + } + + JS_FireOnNewGlobalObject(cx, shg); + + return shg; +} + +static void MaybePrintAndClearPendingException(JSContext* cx, FILE* file) { + if (!cx->isExceptionPending()) { + return; + } + + AutoClearPendingException acpe(cx); + + JS::ExceptionStack exnStack(cx); + if (!JS::StealPendingExceptionStack(cx, &exnStack)) { + fprintf(file, "error getting pending exception\n"); + return; + } + + JS::ErrorReportBuilder report(cx); + if (!report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) { + fprintf(file, "out of memory initializing JS::ErrorReportBuilder\n"); + return; + } + + MOZ_ASSERT(!report.report()->isWarning()); + JS::PrintError(cx, file, report, true); +} + +class MOZ_STACK_CLASS AutoSelfHostingErrorReporter { + JSContext* cx_; + JS::WarningReporter oldReporter_; + + public: + explicit AutoSelfHostingErrorReporter(JSContext* cx) : cx_(cx) { + oldReporter_ = JS::SetWarningReporter(cx_, selfHosting_WarningReporter); + } + ~AutoSelfHostingErrorReporter() { + JS::SetWarningReporter(cx_, oldReporter_); + + // Exceptions in self-hosted code will usually be printed to stderr in + // ErrorToException, but not all exceptions are handled there. For + // instance, ReportOutOfMemory will throw the "out of memory" string + // without going through ErrorToException. We handle these other + // exceptions here. + MaybePrintAndClearPendingException(cx_, stderr); + } +}; + +static bool VerifyGlobalNames(JSContext* cx, Handle<GlobalObject*> shg) { +#ifdef DEBUG + RootedId id(cx); + bool nameMissing = false; + + for (auto base = cx->zone()->cellIter<BaseScript>(); + !base.done() && !nameMissing; base.next()) { + if (!base->hasBytecode()) { + continue; + } + JSScript* script = base->asJSScript(); + + for (BytecodeLocation loc : AllBytecodesIterable(script)) { + JSOp op = loc.getOp(); + + if (op == JSOp::GetIntrinsic) { + PropertyName* name = loc.getPropertyName(script); + id = NameToId(name); + + if (!shg->lookupPure(id)) { + // cellIter disallows GCs, but error reporting wants to + // have them, so we need to move it out of the loop. + nameMissing = true; + break; + } + } + } + } + + if (nameMissing) { + return Throw(cx, id, JSMSG_NO_SUCH_SELF_HOSTED_PROP); + } +#endif // DEBUG + + return true; +} + +bool JSRuntime::initSelfHostingFromXDR( + JSContext* cx, const CompileOptions& options, + frontend::CompilationStencilSet& stencilSet, + MutableHandle<JSScript*> scriptOut) { + MOZ_ASSERT(selfHostingGlobal_); + MOZ_ASSERT(selfHostedXDR.length() > 0); + scriptOut.set(nullptr); + + // Deserialize the stencil from XDR. + JS::TranscodeRange xdrRange(selfHostedXDR); + bool decodeOk = false; + if (!stencilSet.deserializeStencils(cx, xdrRange, &decodeOk)) { + return false; + } + // If XDR decode failed, it's not a propagated error. + // Fall back to regular text parse. + if (!decodeOk) { + return true; + } + + // Instantiate the stencil. + Rooted<frontend::CompilationGCOutput> output(cx); + if (!frontend::CompilationStencil::instantiateStencils(cx, stencilSet, + output.get())) { + return false; + } + + scriptOut.set(output.get().script); + return true; +} + +bool JSRuntime::initSelfHosting(JSContext* cx) { + MOZ_ASSERT(!selfHostingGlobal_); + + if (cx->runtime()->parentRuntime) { + selfHostingGlobal_ = cx->runtime()->parentRuntime->selfHostingGlobal_; + return true; + } + + Rooted<GlobalObject*> shg(cx, JSRuntime::createSelfHostingGlobal(cx)); + if (!shg) { + return false; + } + + JSAutoRealm ar(cx, shg); + + /* + * Set a temporary error reporter printing to stderr because it is too + * early in the startup process for any other reporter to be registered + * and we don't want errors in self-hosted code to be silently swallowed. + * + * This class also overrides the warning reporter to print warnings to + * stderr. See selfHosting_WarningReporter. + */ + AutoSelfHostingErrorReporter errorReporter(cx); + + // Variables used to instantiate scripts. + CompileOptions options(cx); + FillSelfHostingCompileOptions(options); + + RootedScript script(cx); + + // Try initializing from Stencil XDR. + if (selfHostedXDR.length() > 0) { + // Initialize the compilation info that houses the stencil. + Rooted<frontend::CompilationStencilSet> stencilSet( + cx, frontend::CompilationStencilSet(cx, options)); + if (!stencilSet.get().input.initForSelfHostingGlobal(cx)) { + return false; + } + + if (!initSelfHostingFromXDR(cx, options, stencilSet.get(), &script)) { + return false; + } + } + + // If script wasn't generated, it means XDR was either not provided or that it + // failed the decoding phase. Parse from text as before. + if (!script) { + Rooted<frontend::CompilationStencil> stencil( + cx, frontend::CompilationStencil(cx, options)); + if (!stencil.get().input.initForSelfHostingGlobal(cx)) { + return false; + } + + uint32_t srcLen = GetRawScriptsSize(); + const unsigned char* compressed = compressedSources; + uint32_t compressedLen = GetCompressedSize(); + auto src = cx->make_pod_array<char>(srcLen); + if (!src) { + return false; + } + if (!DecompressString(compressed, compressedLen, + reinterpret_cast<unsigned char*>(src.get()), + srcLen)) { + return false; + } + + JS::SourceText<mozilla::Utf8Unit> srcBuf; + if (!srcBuf.init(cx, std::move(src), srcLen)) { + return false; + } + + if (!frontend::CompileGlobalScriptToStencil(cx, stencil.get(), srcBuf, + ScopeKind::Global)) { + return false; + } + + // Serialize the stencil to XDR. + if (selfHostedXDRWriter) { + JS::TranscodeBuffer xdrBuffer; + if (!stencil.get().serializeStencils(cx, xdrBuffer)) { + return false; + } + + if (!selfHostedXDRWriter(cx, xdrBuffer)) { + return false; + } + } + + // Instantiate the stencil. + Rooted<frontend::CompilationGCOutput> output(cx); + if (!frontend::CompilationStencil::instantiateStencils(cx, stencil.get(), + output.get())) { + return false; + } + + script.set(output.get().script); + } + + MOZ_ASSERT(script); + RootedValue rval(cx); + if (!JS_ExecuteScript(cx, script, &rval)) { + return false; + } + + if (!VerifyGlobalNames(cx, shg)) { + return false; + } + + // Garbage collect the self hosting zone once when it is created. It should + // not be modified after this point. Drop top-level script reference before we + // do this collection. + script.set(nullptr); + cx->runtime()->gc.freezeSelfHostingZone(); + + return true; +} + +void JSRuntime::finishSelfHosting() { selfHostingGlobal_ = nullptr; } + +void JSRuntime::traceSelfHostingGlobal(JSTracer* trc) { + if (selfHostingGlobal_ && !parentRuntime) { + TraceRoot(trc, const_cast<NativeObject**>(&selfHostingGlobal_.ref()), + "self-hosting global"); + } +} + +GeneratorKind JSRuntime::getSelfHostedFunctionGeneratorKind(JSAtom* name) { + JSFunction* fun = getUnclonedSelfHostedFunction(name->asPropertyName()); + return fun->generatorKind(); +} + +static bool CloneValue(JSContext* cx, HandleValue selfHostedValue, + MutableHandleValue vp); + +static void GetUnclonedValue(NativeObject* selfHostedObject, + const JS::PropertyKey& id, Value* vp) { + if (JSID_IS_INT(id)) { + size_t index = JSID_TO_INT(id); + if (index < selfHostedObject->getDenseInitializedLength() && + !selfHostedObject->getDenseElement(index).isMagic(JS_ELEMENTS_HOLE)) { + *vp = selfHostedObject->getDenseElement(JSID_TO_INT(id)); + return; + } + } + + // Since all atoms used by self-hosting are marked as permanent, the only + // reason we'd see a non-permanent atom here is code looking for + // properties on the self hosted global which aren't present. + // Since we ensure that that can't happen during startup, encountering + // non-permanent atoms here should be impossible. + MOZ_ASSERT_IF(JSID_IS_STRING(id), JSID_TO_STRING(id)->isPermanentAtom()); + + Shape* shape = selfHostedObject->lookupPure(id); + MOZ_ASSERT(shape); + MOZ_ASSERT(shape->isDataProperty()); + *vp = selfHostedObject->getSlot(shape->slot()); +} + +static bool CloneProperties(JSContext* cx, HandleNativeObject selfHostedObject, + HandleObject clone) { + RootedIdVector ids(cx); + Vector<uint8_t, 16> attrs(cx); + + for (size_t i = 0; i < selfHostedObject->getDenseInitializedLength(); i++) { + if (!selfHostedObject->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) { + if (!ids.append(INT_TO_JSID(i))) { + return false; + } + if (!attrs.append(JSPROP_ENUMERATE)) { + return false; + } + } + } + + Rooted<ShapeVector> shapes(cx, ShapeVector(cx)); + for (Shape::Range<NoGC> range(selfHostedObject->lastProperty()); + !range.empty(); range.popFront()) { + Shape& shape = range.front(); + if (shape.enumerable() && !shapes.append(&shape)) { + return false; + } + } + + // Now our shapes are in last-to-first order, so.... + std::reverse(shapes.begin(), shapes.end()); + for (size_t i = 0; i < shapes.length(); ++i) { + MOZ_ASSERT(!shapes[i]->isAccessorShape(), + "Can't handle cloning accessors here yet."); + if (!ids.append(shapes[i]->propid())) { + return false; + } + uint8_t shapeAttrs = + shapes[i]->attributes() & + (JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY); + if (!attrs.append(shapeAttrs)) { + return false; + } + } + + RootedId id(cx); + RootedValue val(cx); + RootedValue selfHostedValue(cx); + for (uint32_t i = 0; i < ids.length(); i++) { + id = ids[i]; + GetUnclonedValue(selfHostedObject, id, selfHostedValue.address()); + if (!CloneValue(cx, selfHostedValue, &val) || + !JS_DefinePropertyById(cx, clone, id, val, attrs[i])) { + return false; + } + } + + return true; +} + +static JSString* CloneString(JSContext* cx, JSLinearString* selfHostedString) { + size_t len = selfHostedString->length(); + { + JS::AutoCheckCannotGC nogc; + JSString* clone; + if (selfHostedString->hasLatin1Chars()) { + clone = + NewStringCopyN<NoGC>(cx, selfHostedString->latin1Chars(nogc), len); + } else { + clone = NewStringCopyNDontDeflate<NoGC>( + cx, selfHostedString->twoByteChars(nogc), len); + } + if (clone) { + return clone; + } + } + + AutoStableStringChars chars(cx); + if (!chars.init(cx, selfHostedString)) { + return nullptr; + } + + return chars.isLatin1() + ? NewStringCopyN<CanGC>(cx, chars.latin1Range().begin().get(), len) + : NewStringCopyNDontDeflate<CanGC>( + cx, chars.twoByteRange().begin().get(), len); +} + +// Returns the ScriptSourceObject to use for cloned self-hosted scripts in the +// current realm. +static ScriptSourceObject* SelfHostingScriptSourceObject(JSContext* cx) { + if (ScriptSourceObject* sso = cx->realm()->selfHostingScriptSource) { + return sso; + } + + CompileOptions options(cx); + FillSelfHostingCompileOptions(options); + + ScriptSource* ss = cx->new_<ScriptSource>(); + if (!ss) { + return nullptr; + } + Rooted<ScriptSourceHolder> ssHolder(cx, ss); + + if (!ss->initFromOptions(cx, options)) { + return nullptr; + } + + RootedScriptSourceObject sourceObject(cx, ScriptSourceObject::create(cx, ss)); + if (!sourceObject) { + return nullptr; + } + + if (!ScriptSourceObject::initFromOptions(cx, sourceObject, options)) { + return nullptr; + } + + cx->realm()->selfHostingScriptSource.set(sourceObject); + return sourceObject; +} + +static JSObject* CloneObject(JSContext* cx, + HandleNativeObject selfHostedObject) { +#ifdef DEBUG + // Object hash identities are owned by the hashed object, which may be on a + // different thread than the clone target. In theory, these objects are all + // tenured and will not be compacted; however, we simply avoid the issue + // altogether by skipping the cycle-detection when off thread. + mozilla::Maybe<AutoCycleDetector> detect; + if (js::CurrentThreadCanAccessZone(selfHostedObject->zoneFromAnyThread())) { + detect.emplace(cx, selfHostedObject); + if (!detect->init()) { + return nullptr; + } + if (detect->foundCycle()) { + MOZ_CRASH("SelfHosted cloning cannot handle cyclic object graphs."); + } + } +#endif + + RootedObject clone(cx); + if (selfHostedObject->is<JSFunction>()) { + RootedFunction selfHostedFunction(cx, &selfHostedObject->as<JSFunction>()); + if (selfHostedFunction->isInterpreted()) { + bool hasName = selfHostedFunction->explicitName() != nullptr; + + // Arrow functions use the first extended slot for their lexical |this| + // value. And methods use the first extended slot for their home-object. + // We only expect to see normal functions here. + MOZ_ASSERT(selfHostedFunction->kind() == FunctionFlags::NormalFunction); + js::gc::AllocKind kind = hasName ? gc::AllocKind::FUNCTION_EXTENDED + : selfHostedFunction->getAllocKind(); + + Handle<GlobalObject*> global = cx->global(); + Rooted<LexicalEnvironmentObject*> globalLexical( + cx, &global->lexicalEnvironment()); + RootedScope emptyGlobalScope(cx, &global->emptyGlobalScope()); + Rooted<ScriptSourceObject*> sourceObject( + cx, SelfHostingScriptSourceObject(cx)); + if (!sourceObject) { + return nullptr; + } + MOZ_ASSERT( + !CanReuseScriptForClone(cx->realm(), selfHostedFunction, global)); + clone = CloneFunctionAndScript(cx, selfHostedFunction, globalLexical, + emptyGlobalScope, sourceObject, kind); + // To be able to re-lazify the cloned function, its name in the + // self-hosting compartment has to be stored on the clone. Re-lazification + // is only possible if this isn't a function expression. + if (clone && !selfHostedFunction->isLambda()) { + // If |_SetCanonicalName| was called on the function, the self-hosted + // name is stored in the extended slot. + JSAtom* name = GetUnclonedSelfHostedFunctionName(selfHostedFunction); + if (!name) { + name = selfHostedFunction->explicitName(); + } + SetClonedSelfHostedFunctionName(&clone->as<JSFunction>(), name); + } + } else { + clone = CloneSelfHostingIntrinsic(cx, selfHostedFunction); + } + } else if (selfHostedObject->is<RegExpObject>()) { + RegExpObject& reobj = selfHostedObject->as<RegExpObject>(); + RootedAtom source(cx, reobj.getSource()); + MOZ_ASSERT(source->isPermanentAtom()); + clone = RegExpObject::create(cx, source, reobj.getFlags(), TenuredObject); + } else if (selfHostedObject->is<DateObject>()) { + clone = + JS::NewDateObject(cx, selfHostedObject->as<DateObject>().clippedTime()); + } else if (selfHostedObject->is<BooleanObject>()) { + clone = BooleanObject::create( + cx, selfHostedObject->as<BooleanObject>().unbox()); + } else if (selfHostedObject->is<NumberObject>()) { + clone = + NumberObject::create(cx, selfHostedObject->as<NumberObject>().unbox()); + } else if (selfHostedObject->is<StringObject>()) { + JSString* selfHostedString = selfHostedObject->as<StringObject>().unbox(); + if (!selfHostedString->isLinear()) { + MOZ_CRASH(); + } + RootedString str(cx, CloneString(cx, &selfHostedString->asLinear())); + if (!str) { + return nullptr; + } + clone = StringObject::create(cx, str); + } else if (selfHostedObject->is<ArrayObject>()) { + clone = NewTenuredDenseEmptyArray(cx, nullptr); + } else { + MOZ_ASSERT(selfHostedObject->isNative()); + clone = NewObjectWithGivenProto( + cx, selfHostedObject->getClass(), nullptr, + selfHostedObject->asTenured().getAllocKind(), TenuredObject); + } + if (!clone) { + return nullptr; + } + + if (!CloneProperties(cx, selfHostedObject, clone)) { + return nullptr; + } + return clone; +} + +static bool CloneValue(JSContext* cx, HandleValue selfHostedValue, + MutableHandleValue vp) { + if (selfHostedValue.isObject()) { + RootedNativeObject selfHostedObject( + cx, &selfHostedValue.toObject().as<NativeObject>()); + JSObject* clone = CloneObject(cx, selfHostedObject); + if (!clone) { + return false; + } + vp.setObject(*clone); + } else if (selfHostedValue.isBoolean() || selfHostedValue.isNumber() || + selfHostedValue.isNullOrUndefined()) { + // Nothing to do here: these are represented inline in the value. + vp.set(selfHostedValue); + } else if (selfHostedValue.isString()) { + if (!selfHostedValue.toString()->isLinear()) { + MOZ_CRASH(); + } + JSLinearString* selfHostedString = &selfHostedValue.toString()->asLinear(); + JSString* clone = CloneString(cx, selfHostedString); + if (!clone) { + return false; + } + vp.setString(clone); + } else if (selfHostedValue.isSymbol()) { + // Well-known symbols are shared. + mozilla::DebugOnly<JS::Symbol*> sym = selfHostedValue.toSymbol(); + MOZ_ASSERT(sym->isWellKnownSymbol()); + MOZ_ASSERT(cx->wellKnownSymbols().get(sym->code()) == sym); + vp.set(selfHostedValue); + } else { + MOZ_CRASH("Self-hosting CloneValue can't clone given value."); + } + return true; +} + +bool JSRuntime::createLazySelfHostedFunctionClone( + JSContext* cx, HandlePropertyName selfHostedName, HandleAtom name, + unsigned nargs, NewObjectKind newKind, MutableHandleFunction fun) { + MOZ_ASSERT(newKind != GenericObject); + + RootedAtom funName(cx, name); + JSFunction* selfHostedFun = getUnclonedSelfHostedFunction(selfHostedName); + if (!selfHostedFun) { + return false; + } + + if (!selfHostedFun->isClassConstructor() && + !selfHostedFun->hasGuessedAtom() && + selfHostedFun->explicitName() != selfHostedName) { + MOZ_ASSERT(GetUnclonedSelfHostedFunctionName(selfHostedFun) == + selfHostedName); + funName = selfHostedFun->explicitName(); + } + + RootedObject proto(cx); + if (!GetFunctionPrototype(cx, selfHostedFun->generatorKind(), + selfHostedFun->asyncKind(), &proto)) { + return false; + } + + fun.set(NewScriptedFunction(cx, nargs, FunctionFlags::BASESCRIPT, funName, + proto, gc::AllocKind::FUNCTION_EXTENDED, + newKind)); + if (!fun) { + return false; + } + fun->setIsSelfHostedBuiltin(); + fun->initSelfHostedLazyScript(&cx->runtime()->selfHostedLazyScript.ref()); + SetClonedSelfHostedFunctionName(fun, selfHostedName); + return true; +} + +bool JSRuntime::cloneSelfHostedFunctionScript(JSContext* cx, + HandlePropertyName name, + HandleFunction targetFun) { + RootedFunction sourceFun(cx, getUnclonedSelfHostedFunction(name)); + if (!sourceFun) { + return false; + } + MOZ_ASSERT(targetFun->isExtended()); + MOZ_ASSERT(targetFun->hasSelfHostedLazyScript()); + + RootedScript sourceScript(cx, JSFunction::getOrCreateScript(cx, sourceFun)); + if (!sourceScript) { + return false; + } + + Rooted<ScriptSourceObject*> sourceObject(cx, + SelfHostingScriptSourceObject(cx)); + if (!sourceObject) { + return false; + } + + // Assert that there are no intervening scopes between the global scope + // and the self-hosted script. Toplevel lexicals are explicitly forbidden + // by the parser when parsing self-hosted code. The fact they have the + // global lexical scope on the scope chain is for uniformity and engine + // invariants. + MOZ_ASSERT(sourceScript->outermostScope()->enclosing()->kind() == + ScopeKind::Global); + RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope()); + if (!CloneScriptIntoFunction(cx, emptyGlobalScope, targetFun, sourceScript, + sourceObject)) { + return false; + } + + MOZ_ASSERT(targetFun->hasBytecode()); + RootedScript targetScript(cx, targetFun->nonLazyScript()); + + // Relazifiable self-hosted function may be relazified later into a + // SelfHostedLazyScript. It is important to note that this only applies to + // named self-hosted entry points (that use this clone method). Inner + // functions clones used by self-hosted are never relazified, even if they + // would be able to in normal script. + if (targetScript->isRelazifiable()) { + targetScript->setAllowRelazify(); + } + + MOZ_ASSERT(sourceFun->nargs() == targetFun->nargs()); + MOZ_ASSERT(sourceScript->hasRest() == targetScript->hasRest()); + MOZ_ASSERT(targetFun->strict(), "Self-hosted builtins must be strict"); + + // The target function might have been relazified after its flags changed. + targetFun->setFlags(targetFun->flags().toRaw() | sourceFun->flags().toRaw()); + return true; +} + +void JSRuntime::getUnclonedSelfHostedValue(PropertyName* name, Value* vp) { + JS::PropertyKey id = NameToId(name); + GetUnclonedValue(selfHostingGlobal_, id, vp); +} + +JSFunction* JSRuntime::getUnclonedSelfHostedFunction(PropertyName* name) { + Value selfHostedValue; + getUnclonedSelfHostedValue(name, &selfHostedValue); + return &selfHostedValue.toObject().as<JSFunction>(); +} + +bool JSRuntime::cloneSelfHostedValue(JSContext* cx, HandlePropertyName name, + MutableHandleValue vp) { + RootedValue selfHostedValue(cx); + getUnclonedSelfHostedValue(name, selfHostedValue.address()); + + /* + * We don't clone if we're operating in the self-hosting global, as that + * means we're currently executing the self-hosting script while + * initializing the runtime (see JSRuntime::initSelfHosting). + */ + if (cx->global() == selfHostingGlobal_) { + vp.set(selfHostedValue); + return true; + } + + return CloneValue(cx, selfHostedValue, vp); +} + +void JSRuntime::assertSelfHostedFunctionHasCanonicalName( + JSContext* cx, HandlePropertyName name) { +#ifdef DEBUG + JSFunction* selfHostedFun = getUnclonedSelfHostedFunction(name); + MOZ_ASSERT(selfHostedFun); + MOZ_ASSERT(GetUnclonedSelfHostedFunctionName(selfHostedFun) == name); +#endif +} + +bool js::IsSelfHostedFunctionWithName(JSFunction* fun, JSAtom* name) { + return fun->isSelfHostedBuiltin() && fun->isExtended() && + GetClonedSelfHostedFunctionName(fun) == name; +} + +static_assert( + JSString::MAX_LENGTH <= INT32_MAX, + "StringIteratorNext in builtin/String.js assumes the stored index " + "into the string is an Int32Value"); + +static_assert(JSString::MAX_LENGTH == MAX_STRING_LENGTH, + "JSString::MAX_LENGTH matches self-hosted constant for maximum " + "string length"); + +static_assert(ARGS_LENGTH_MAX == MAX_ARGS_LENGTH, + "ARGS_LENGTH_MAX matches self-hosted constant for maximum " + "arguments length"); |