diff options
Diffstat (limited to 'js/src/vm/SelfHosting.cpp')
-rw-r--r-- | js/src/vm/SelfHosting.cpp | 3109 |
1 files changed, 3109 insertions, 0 deletions
diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp new file mode 100644 index 000000000..6737e774c --- /dev/null +++ b/js/src/vm/SelfHosting.cpp @@ -0,0 +1,3109 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "vm/SelfHosting.h" + +#include "mozilla/ArrayUtils.h" +#include "mozilla/Casting.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/Maybe.h" + +#include "jsarray.h" +#include "jscntxt.h" +#include "jscompartment.h" +#include "jsdate.h" +#include "jsfriendapi.h" +#include "jsfun.h" +#include "jshashutil.h" +#include "jsstr.h" +#include "jsweakmap.h" +#include "jswrapper.h" +#include "selfhosted.out.h" + +#include "builtin/Intl.h" +#include "builtin/MapObject.h" +#include "builtin/ModuleObject.h" +#include "builtin/Object.h" +#include "builtin/Reflect.h" +#include "builtin/SelfHostingDefines.h" +#include "builtin/SIMD.h" +#include "builtin/TypedObject.h" +#include "builtin/WeakSetObject.h" +#include "gc/Marking.h" +#include "gc/Policy.h" +#include "jit/AtomicOperations.h" +#include "jit/InlinableNatives.h" +#include "js/CharacterEncoding.h" +#include "js/Date.h" +#include "vm/Compression.h" +#include "vm/GeneratorObject.h" +#include "vm/Interpreter.h" +#include "vm/RegExpObject.h" +#include "vm/String.h" +#include "vm/StringBuffer.h" +#include "vm/TypedArrayCommon.h" +#include "vm/WrapperObject.h" + +#include "jsatominlines.h" +#include "jsfuninlines.h" +#include "jsobjinlines.h" +#include "jsscriptinlines.h" + +#include "vm/BooleanObject-inl.h" +#include "vm/NativeObject-inl.h" +#include "vm/NumberObject-inl.h" +#include "vm/StringObject-inl.h" + +using namespace js; +using namespace js::selfhosted; + +using JS::AutoCheckCannotGC; +using mozilla::IsInRange; +using mozilla::Maybe; +using mozilla::PodMove; +using mozilla::Maybe; + +static void +selfHosting_WarningReporter(JSContext* cx, JSErrorReport* report) +{ + MOZ_ASSERT(report); + MOZ_ASSERT(JSREPORT_IS_WARNING(report->flags)); + + PrintError(cx, stderr, JS::ConstUTF8CharsZ(), report, true); +} + +static bool +intrinsic_ToObject(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + RootedValue val(cx, args[0]); + RootedObject obj(cx, ToObject(cx, val)); + 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_IsWrappedArrayConstructor(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + bool result = false; + if (!IsWrappedArrayConstructor(cx, args[0], &result)) + return false; + args.rval().setBoolean(result); + 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_ToString(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + RootedString str(cx); + str = ToString<CanGC>(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); + 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; +} + +/** + * Self-hosting intrinsic returning the original constructor for a builtin + * the name of which is the first and only argument. + * + * The return value is guaranteed to be the original constructor even if + * content code changed the named binding on the global object. + * + * This intrinsic shouldn't be called directly. Instead, the + * `GetBuiltinConstructor` and `GetBuiltinPrototype` helper functions in + * Utilities.js should be used, as they cache results, improving performance. + */ +static bool +intrinsic_GetBuiltinConstructor(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + RootedString str(cx, args[0].toString()); + JSAtom* atom; + if (str->isAtom()) { + atom = &str->asAtom(); + } else { + atom = AtomizeString(cx, str); + if (!atom) + return false; + } + RootedId id(cx, AtomToId(atom)); + JSProtoKey key = JS_IdToProtoKey(cx, id); + MOZ_ASSERT(key != JSProto_Null); + RootedObject ctor(cx); + if (!GetBuiltinConstructor(cx, key, &ctor)) + return false; + args.rval().setObject(*ctor); + return true; +} + +static bool +intrinsic_SubstringKernel(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args[0].isString()); + MOZ_ASSERT(args[1].isInt32()); + MOZ_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 bool +intrinsic_OwnPropertyKeys(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args[0].isObject()); + MOZ_ASSERT(args[1].isInt32()); + return GetOwnPropertyKeys(cx, args, args[1].toInt32()); +} + +static void +ThrowErrorWithType(JSContext* cx, JSExnType type, const CallArgs& args) +{ + 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 + + JSAutoByteString errorArgs[3]; + for (unsigned i = 1; i < 4 && i < args.length(); i++) { + RootedValue val(cx, args[i]); + if (val.isInt32()) { + JSString* str = ToString<CanGC>(cx, val); + if (!str) + return; + errorArgs[i - 1].encodeLatin1(cx, str); + } else if (val.isString()) { + errorArgs[i - 1].encodeLatin1(cx, val.toString()); + } else { + UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, nullptr); + if (!bytes) + return; + errorArgs[i - 1].initBytes(bytes.release()); + } + if (!errorArgs[i - 1]) + return; + } + + JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, errorNumber, + errorArgs[0].ptr(), errorArgs[1].ptr(), errorArgs[2].ptr()); +} + +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_ThrowInternalError(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() >= 1); + + ThrowErrorWithType(cx, JSEXN_INTERNALERR, args); + return false; +} + +/** + * 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) { + fprintf(stderr, "Self-hosted JavaScript assertion info: "); + str->dumpCharsNoNewline(); + fputc('\n', stderr); + } + } +#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 + JSString* str = ToString<CanGC>(cx, args[0]); + if (str) { + str->dumpCharsNoNewline(); + fputc('\n', stderr); + } 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 (!DefineProperty(cx, ctor, cx->names().prototype, args[1], + nullptr, nullptr, + JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT)) + { + return false; + } + + ctor->as<JSFunction>().setIsConstructor(); + args.rval().setUndefined(); + return true; +} + +static bool +intrinsic_MakeDefaultConstructor(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].toObject().as<JSFunction>().isSelfHostedBuiltin()); + + RootedFunction ctor(cx, &args[0].toObject().as<JSFunction>()); + + ctor->nonLazyScript()->setIsDefaultClassConstructor(); + + // Because self-hosting code does not allow top-level lexicals, + // class constructors are class expressions in top-level vars. + // Because of this, we give them a guessed atom. Since they + // will always be cloned, and given an explicit atom, instead + // overrule that. + ctor->clearGuessedAtom(); + + args.rval().setUndefined(); + return true; +} + +/* + * Used to mark bound functions as such and make them constructible if the + * target is. Also assigns the prototype and sets the name and correct length. + */ +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_ASSERT(args[2].isNumber()); + + RootedFunction bound(cx, &args[0].toObject().as<JSFunction>()); + bound->setIsBoundFunction(); + RootedObject targetObj(cx, &args[1].toObject()); + MOZ_ASSERT(bound->getBoundFunctionTarget() == targetObj); + + // 9.4.1.3 BoundFunctionCreate, steps 1, 3-5, 8-12 (Already performed). + + // 9.4.1.3 BoundFunctionCreate, step 6. + if (targetObj->isConstructor()) + bound->setIsConstructor(); + + // 9.4.1.3 BoundFunctionCreate, step 2. + RootedObject proto(cx); + if (!GetPrototype(cx, targetObj, &proto)) + return false; + + // 9.4.1.3 BoundFunctionCreate, step 7. + if (bound->staticPrototype() != proto) { + if (!SetPrototype(cx, bound, proto)) + return false; + } + + double argCount = args[2].toNumber(); + double length = 0.0; + + // Try to avoid invoking the resolve hook. + if (targetObj->is<JSFunction>() && !targetObj->as<JSFunction>().hasResolvedLength()) { + RootedValue targetLength(cx); + if (!targetObj->as<JSFunction>().getUnresolvedLength(cx, &targetLength)) + return false; + + length = Max(0.0, targetLength.toNumber() - argCount); + } else { + // 19.2.3.2 Function.prototype.bind, step 5. + bool hasLength; + RootedId idRoot(cx, NameToId(cx->names().length)); + if (!HasOwnProperty(cx, targetObj, idRoot, &hasLength)) + return false; + + // 19.2.3.2 Function.prototype.bind, step 6. + if (hasLength) { + RootedValue targetLength(cx); + if (!GetProperty(cx, targetObj, targetObj, idRoot, &targetLength)) + return false; + + if (targetLength.isNumber()) + length = Max(0.0, JS::ToInteger(targetLength.toNumber()) - argCount); + } + + // 19.2.3.2 Function.prototype.bind, step 7 (implicit). + } + + // 19.2.3.2 Function.prototype.bind, step 8. + bound->setExtendedSlot(BOUND_FUN_LENGTH_SLOT, NumberValue(length)); + + // Try to avoid invoking the resolve hook. + JSAtom* name = nullptr; + if (targetObj->is<JSFunction>() && !targetObj->as<JSFunction>().hasResolvedName()) + name = targetObj->as<JSFunction>().getUnresolvedName(cx); + + RootedString rootedName(cx); + if (name) { + rootedName = name; + } else { + // 19.2.3.2 Function.prototype.bind, step 9. + RootedValue targetName(cx); + if (!GetProperty(cx, targetObj, targetObj, cx->names().name, &targetName)) + return false; + + // 19.2.3.2 Function.prototype.bind, step 10. + if (targetName.isString()) + rootedName = targetName.toString(); + } + + // 19.2.3.2 Function.prototype.bind, step 11 (Inlined SetFunctionName). + MOZ_ASSERT(!bound->hasGuessedAtom()); + if (rootedName && !rootedName->empty()) { + StringBuffer sb(cx); + if (!sb.append(cx->names().boundWithSpace) || !sb.append(rootedName)) + return false; + + RootedAtom nameAtom(cx, sb.finishAtom()); + if (!nameAtom) + return false; + + bound->setAtom(nameAtom); + } else { + bound->setAtom(cx->names().boundWithSpace); + } + + args.rval().setUndefined(); + return true; +} + +static bool +intrinsic_SetPrototype(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].isObjectOrNull()); + + RootedObject obj(cx, &args[0].toObject()); + RootedObject proto(cx, args[1].toObjectOrNull()); + if (!SetPrototype(cx, obj, proto)) + return false; + + args.rval().setUndefined(); + return true; +} + +/* + * 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); + + RootedValue value(cx, args[1]); + ScopedJSFreePtr<char> str(DecompileArgument(cx, args[0].toInt32(), value)); + if (!str) + return false; + RootedAtom atom(cx, Atomize(cx, str, strlen(str))); + if (!atom) + return false; + args.rval().setString(atom); + return true; +} + +static bool +intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + MOZ_ASSERT(args.length() >= 3); + MOZ_ASSERT(args[0].isObject()); + + RootedObject obj(cx, &args[0].toObject()); + RootedId id(cx); + if (!ValueToId<CanGC>(cx, args[1], &id)) + return false; + RootedValue value(cx, args[2]); + + unsigned attrs = 0; + if (args.length() >= 4) { + 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; + } else { + // If the fourth argument is unspecified, the attributes are for a + // plain data property. + attrs = JSPROP_ENUMERATE; + } + + Rooted<PropertyDescriptor> desc(cx); + desc.setDataDescriptor(value, attrs); + if (!DefineProperty(cx, obj, id, desc)) + return false; + + args.rval().setUndefined(); + return true; +} + +static bool +intrinsic_ObjectHasPrototype(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + RootedObject obj(cx, &args[0].toObject()); + RootedObject proto(cx, &args[1].toObject()); + + RootedObject actualProto(cx); + if (!GetPrototype(cx, obj, &actualProto)) + return false; + + 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_ASSERT(args[1].isInt32()); + + args[0].toObject().as<NativeObject>().setReservedSlot(args[1].toPrivateUint32(), 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_ASSERT(args[1].isInt32()); + + args.rval().set(args[0].toObject().as<NativeObject>().getReservedSlot(args[1].toPrivateUint32())); + 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; +} + +/** + * Intrinsic for creating an empty array in the compartment of the object + * passed as the first argument. + * + * Returns the array, wrapped in the default wrapper to use between the two + * compartments. + */ +static bool +intrinsic_NewArrayInCompartment(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + RootedObject wrapped(cx, &args[0].toObject()); + MOZ_ASSERT(IsWrapper(wrapped)); + RootedObject obj(cx, UncheckedUnwrap(wrapped)); + + RootedArrayObject arr(cx); + { + AutoCompartment ac(cx, obj); + arr = NewDenseEmptyArray(cx); + if (!arr) + return false; + } + + args.rval().setObject(*arr); + return wrapped->compartment()->wrap(cx, args.rval()); +} + +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; +} + +static bool +intrinsic_GetIteratorPrototype(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + JSObject* obj = GlobalObject::getOrCreateIteratorPrototype(cx, cx->global()); + if (!obj) + return false; + + args.rval().setObject(*obj); + return true; +} + +static bool +intrinsic_NewArrayIterator(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + RootedObject proto(cx, GlobalObject::getOrCreateArrayIteratorPrototype(cx, cx->global())); + if (!proto) + return false; + + JSObject* obj = NewObjectWithGivenProto(cx, &ArrayIteratorObject::class_, proto); + if (!obj) + return false; + + args.rval().setObject(*obj); + 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()); + + Rooted<MapIteratorObject*> mapIterator(cx, &args[0].toObject().as<MapIteratorObject>()); + RootedArrayObject result(cx, &args[1].toObject().as<ArrayObject>()); + + args.rval().setBoolean(MapIteratorObject::next(mapIterator, result, cx)); + return true; +} + +static bool +intrinsic_CreateMapIterationResultPair(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + RootedObject result(cx, 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()); + + Rooted<SetIteratorObject*> setIterator(cx, &args[0].toObject().as<SetIteratorObject>()); + RootedArrayObject result(cx, &args[1].toObject().as<ArrayObject>()); + + args.rval().setBoolean(SetIteratorObject::next(setIterator, result, cx)); + return true; +} + +static bool +intrinsic_CreateSetIterationResult(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + RootedObject result(cx, SetIteratorObject::createResult(cx)); + if (!result) + return false; + + args.rval().setObject(*result); + return true; +} + +static bool +intrinsic_NewStringIterator(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + RootedObject proto(cx, GlobalObject::getOrCreateStringIteratorPrototype(cx, cx->global())); + if (!proto) + return false; + + JSObject* obj = NewObjectWithGivenProto(cx, &StringIteratorObject::class_, proto); + if (!obj) + return false; + + args.rval().setObject(*obj); + return true; +} + +static bool +intrinsic_NewListIterator(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + RootedObject proto(cx, GlobalObject::getOrCreateIteratorPrototype(cx, cx->global())); + if (!proto) + return false; + + RootedObject iterator(cx); + iterator = NewObjectWithGivenProto(cx, &ListIteratorObject::class_, proto); + if (!iterator) + return false; + + args.rval().setObject(*iterator); + return true; +} + +static bool +intrinsic_ActiveFunction(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + ScriptFrameIter iter(cx); + MOZ_ASSERT(iter.isFunctionFrame()); + args.rval().setObject(*iter.callee(cx)); + return true; +} + +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()); + RootedAtom atom(cx, AtomizeString(cx, args[1].toString())); + if (!atom) + return false; + + fun->setAtom(atom); +#ifdef DEBUG + fun->setExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT, BooleanValue(true)); +#endif + args.rval().setUndefined(); + return true; +} + +static bool +intrinsic_StarGeneratorObjectIsClosed(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + StarGeneratorObject* genObj = &args[0].toObject().as<StarGeneratorObject>(); + args.rval().setBoolean(genObj->isClosed()); + return true; +} + +bool +js::intrinsic_IsSuspendedStarGenerator(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + + if (!args[0].isObject() || !args[0].toObject().is<StarGeneratorObject>()) { + args.rval().setBoolean(false); + return true; + } + + StarGeneratorObject& genObj = args[0].toObject().as<StarGeneratorObject>(); + args.rval().setBoolean(!genObj.isClosed() && genObj.isSuspended()); + return true; +} + +static bool +intrinsic_LegacyGeneratorObjectIsClosed(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + LegacyGeneratorObject* genObj = &args[0].toObject().as<LegacyGeneratorObject>(); + args.rval().setBoolean(genObj->isClosed()); + return true; +} + +static bool +intrinsic_CloseClosingLegacyGeneratorObject(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + LegacyGeneratorObject* genObj = &args[0].toObject().as<LegacyGeneratorObject>(); + MOZ_ASSERT(genObj->isClosing()); + genObj->setClosed(); + return true; +} + +static bool +intrinsic_ThrowStopIteration(JSContext* cx, unsigned argc, Value* vp) +{ + MOZ_ASSERT(CallArgsFromVp(argc, vp).length() == 0); + + return ThrowStopIteration(cx); +} + +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() || genObj->isClosing()); + 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_IsWrappedArrayBuffer(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + + if (!args[0].isObject()) { + args.rval().setBoolean(false); + return true; + } + + JSObject* obj = &args[0].toObject(); + if (!obj->is<WrapperObject>()) { + args.rval().setBoolean(false); + return true; + } + + JSObject* unwrapped = CheckedUnwrap(obj); + if (!unwrapped) { + JS_ReportErrorASCII(cx, "Permission denied to access object"); + return false; + } + + args.rval().setBoolean(unwrapped->is<T>()); + 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(); + args.rval().setInt32(mozilla::AssertedCast<int32_t>(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); + + JSObject* obj = CheckedUnwrap(&args[0].toObject()); + if (!obj) { + JS_ReportErrorASCII(cx, "Permission denied to access object"); + return false; + } + + uint32_t length = obj->as<T>().byteLength(); + args.rval().setInt32(mozilla::AssertedCast<int32_t>(length)); + return true; +} + +template<typename T> +static bool +intrinsic_ArrayBufferCopyData(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 5); + + bool isWrapped = args[4].toBoolean(); + Rooted<T*> toBuffer(cx); + if (!isWrapped) { + toBuffer = &args[0].toObject().as<T>(); + } else { + JSObject* wrapped = &args[0].toObject(); + MOZ_ASSERT(wrapped->is<WrapperObject>()); + RootedObject toBufferObj(cx, CheckedUnwrap(wrapped)); + if (!toBufferObj) { + JS_ReportErrorASCII(cx, "Permission denied to access object"); + return false; + } + toBuffer = toBufferObj.as<T>(); + } + Rooted<T*> fromBuffer(cx, &args[1].toObject().as<T>()); + uint32_t fromIndex = uint32_t(args[2].toInt32()); + uint32_t count = uint32_t(args[3].toInt32()); + + T::copyData(toBuffer, fromBuffer, fromIndex, count); + + args.rval().setUndefined(); + return true; +} + +static bool +intrinsic_IsSpecificTypedArray(JSContext* cx, unsigned argc, Value* vp, Scalar::Type type) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + JSObject* obj = &args[0].toObject(); + + bool isArray = JS_GetArrayBufferViewType(obj) == type; + + args.rval().setBoolean(isArray); + return true; +} + +static bool +intrinsic_IsUint8TypedArray(JSContext* cx, unsigned argc, Value* vp) +{ + return intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::Uint8) || + intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::Uint8Clamped); +} + +static bool +intrinsic_IsInt8TypedArray(JSContext* cx, unsigned argc, Value* vp) +{ + return intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::Int8); +} + +static bool +intrinsic_IsUint16TypedArray(JSContext* cx, unsigned argc, Value* vp) +{ + return intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::Uint16); +} + +static bool +intrinsic_IsInt16TypedArray(JSContext* cx, unsigned argc, Value* vp) +{ + return intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::Int16); +} + +static bool +intrinsic_IsUint32TypedArray(JSContext* cx, unsigned argc, Value* vp) +{ + return intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::Uint32); +} + +static bool +intrinsic_IsInt32TypedArray(JSContext* cx, unsigned argc, Value* vp) +{ + return intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::Int32); +} + +static bool +intrinsic_IsFloat32TypedArray(JSContext* cx, unsigned argc, Value* vp) +{ + return intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::Float32); +} + +static bool +intrinsic_IsPossiblyWrappedTypedArray(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + + bool isTypedArray = false; + if (args[0].isObject()) { + JSObject* obj = CheckedUnwrap(&args[0].toObject()); + if (!obj) { + JS_ReportErrorASCII(cx, "Permission denied to access object"); + return false; + } + + isTypedArray = obj->is<TypedArrayObject>(); + } + + args.rval().setBoolean(isTypedArray); + 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(TypedArrayObject::bufferValue(tarray)); + 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])); + + args.rval().set(TypedArrayObject::byteOffsetValue(&args[0].toObject().as<TypedArrayObject>())); + 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); + + RootedObject obj(cx, &args[0].toObject()); + MOZ_ASSERT(obj->is<TypedArrayObject>()); + args.rval().setInt32(obj->as<TypedArrayObject>().length()); + 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()); + + JSObject* obj = CheckedUnwrap(&args[0].toObject()); + if (!obj) { + JS_ReportErrorASCII(cx, "Permission denied to access object"); + return false; + } + + MOZ_ASSERT(obj->is<TypedArrayObject>()); + uint32_t typedArrayLength = obj->as<TypedArrayObject>().length(); + args.rval().setInt32(mozilla::AssertedCast<int32_t>(typedArrayLength)); + 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()); + + JSObject* obj = CheckedUnwrap(&args[0].toObject()); + if (!obj) { + JS_ReportErrorASCII(cx, "Permission denied to access object"); + return false; + } + + MOZ_ASSERT(obj->is<TypedArrayObject>()); + bool detached = obj->as<TypedArrayObject>().hasDetachedBuffer(); + args.rval().setBoolean(detached); + return true; +} + +static bool +intrinsic_MoveTypedArrayElements(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 4); + + Rooted<TypedArrayObject*> tarray(cx, &args[0].toObject().as<TypedArrayObject>()); + uint32_t to = uint32_t(args[1].toInt32()); + uint32_t from = uint32_t(args[2].toInt32()); + uint32_t count = uint32_t(args[3].toInt32()); + + MOZ_ASSERT(count > 0, + "don't call this method if copying no elements, because then " + "the not-detached requirement is wrong"); + + if (tarray->hasDetachedBuffer()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); + return false; + } + + // Don't multiply by |tarray->bytesPerElement()| in case the compiler can't + // strength-reduce multiplication by 1/2/4/8 into the equivalent shift. + const size_t ElementShift = TypedArrayShift(tarray->type()); + + MOZ_ASSERT((UINT32_MAX >> ElementShift) > to); + uint32_t byteDest = to << ElementShift; + + MOZ_ASSERT((UINT32_MAX >> ElementShift) > from); + uint32_t byteSrc = from << ElementShift; + + MOZ_ASSERT((UINT32_MAX >> ElementShift) >= count); + uint32_t byteSize = count << ElementShift; + +#ifdef DEBUG + { + uint32_t viewByteLength = tarray->byteLength(); + MOZ_ASSERT(byteSize <= viewByteLength); + MOZ_ASSERT(byteDest < viewByteLength); + MOZ_ASSERT(byteSrc < viewByteLength); + MOZ_ASSERT(byteDest <= viewByteLength - byteSize); + MOZ_ASSERT(byteSrc <= viewByteLength - byteSize); + } +#endif + + SharedMem<uint8_t*> data = tarray->viewDataEither().cast<uint8_t*>(); + jit::AtomicOperations::memmoveSafeWhenRacy(data + byteDest, data + byteSrc, byteSize); + + args.rval().setUndefined(); + 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? + JSObject* unwrapped = CheckedUnwrap(obj); + if (!unwrapped->is<TypedArrayObject>()) { + // By *appearances* this can't happen, as self-hosted TypedArraySet + // checked this. But. Who's to say a GC couldn't happen between + // the check that this value was a typed array, and this extraction + // occurring? A GC might turn a cross-compartment wrapper |obj| into + // |unwrapped == obj|, a dead object no longer connected its typed + // array. + // + // Yeah, yeah, it's pretty unlikely. Are you willing to stake a + // sec-critical bug on that assessment, now and forever, against + // all changes those pesky GC and JIT people might make? + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); + 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->as<TypedArrayObject>(); +} + +// ES6 draft 20150403 22.2.3.22.2, steps 12-24, 29. +static bool +intrinsic_SetFromTypedArrayApproach(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 4); + + Rooted<TypedArrayObject*> target(cx, &args[0].toObject().as<TypedArrayObject>()); + MOZ_ASSERT(!target->hasDetachedBuffer(), + "something should have defended against a target viewing a " + "detached buffer"); + + // 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; + + double doubleTargetOffset = args[2].toNumber(); + MOZ_ASSERT(doubleTargetOffset >= 0, "caller failed to ensure |targetOffset >= 0|"); + + uint32_t targetLength = uint32_t(args[3].toInt32()); + + // Handle all checks preceding the actual element-setting. A visual skim + // of 22.2.3.22.2 should confirm these are the only steps after steps 1-11 + // that might abort processing (other than for reason of internal error.) + + // Steps 12-13. + if (unsafeTypedArrayCrossCompartment->hasDetachedBuffer()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); + return false; + } + + // Steps 21, 23. + uint32_t unsafeSrcLengthCrossCompartment = unsafeTypedArrayCrossCompartment->length(); + if (unsafeSrcLengthCrossCompartment + doubleTargetOffset > targetLength) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX); + return false; + } + + // Now that that's confirmed, we can use |targetOffset| of a sane type. + uint32_t targetOffset = uint32_t(doubleTargetOffset); + + // The remaining steps are unobservable *except* through their effect on + // which elements are copied and how. + + Scalar::Type targetType = target->type(); + Scalar::Type unsafeSrcTypeCrossCompartment = unsafeTypedArrayCrossCompartment->type(); + + size_t targetElementSize = TypedArrayElemSize(targetType); + SharedMem<uint8_t*> targetData = + target->viewDataEither().cast<uint8_t*>() + targetOffset * targetElementSize; + + SharedMem<uint8_t*> unsafeSrcDataCrossCompartment = + unsafeTypedArrayCrossCompartment->viewDataEither().cast<uint8_t*>(); + + uint32_t unsafeSrcElementSizeCrossCompartment = + TypedArrayElemSize(unsafeSrcTypeCrossCompartment); + uint32_t unsafeSrcByteLengthCrossCompartment = + unsafeSrcLengthCrossCompartment * unsafeSrcElementSizeCrossCompartment; + + // Step 29. + // + // The same-type case requires exact copying preserving the bit-level + // encoding of the source data, so move the values. (We could PodCopy if + // we knew the buffers differed, but it's doubtful the work to check + // wouldn't swap any minor wins PodCopy would afford. Because of the + // TOTALLY UNSAFE CROSS-COMPARTMENT NONSENSE here, comparing buffer + // pointers directly could give an incorrect answer.) If this occurs, + // the %TypedArray%.prototype.set operation is completely finished. + if (targetType == unsafeSrcTypeCrossCompartment) { + jit::AtomicOperations::memmoveSafeWhenRacy(targetData, + unsafeSrcDataCrossCompartment, + unsafeSrcByteLengthCrossCompartment); + args.rval().setInt32(JS_SETTYPEDARRAY_SAME_TYPE); + return true; + } + + // Every other bit of element-copying is handled by step 28. Indicate + // whether such copying must take care not to overlap, so that self-hosted + // code may correctly perform the copying. + + SharedMem<uint8_t*> unsafeSrcDataLimitCrossCompartment = + unsafeSrcDataCrossCompartment + unsafeSrcByteLengthCrossCompartment; + SharedMem<uint8_t*> targetDataLimit = + target->viewDataEither().cast<uint8_t*>() + targetLength * targetElementSize; + + // Step 24 test (but not steps 24a-d -- the caller handles those). + bool overlap = + IsInRange(targetData.unwrap(/*safe - used for ptr value*/), + unsafeSrcDataCrossCompartment.unwrap(/*safe - ditto*/), + unsafeSrcDataLimitCrossCompartment.unwrap(/*safe - ditto*/)) || + IsInRange(unsafeSrcDataCrossCompartment.unwrap(/*safe - ditto*/), + targetData.unwrap(/*safe - ditto*/), + targetDataLimit.unwrap(/*safe - ditto*/)); + + args.rval().setInt32(overlap ? JS_SETTYPEDARRAY_OVERLAPPING : JS_SETTYPEDARRAY_DISJOINT); + return true; +} + +template <typename From, typename To> +static void +CopyValues(SharedMem<To*> dest, SharedMem<From*> src, uint32_t count) +{ +#ifdef DEBUG + void* destVoid = dest.template cast<void*>().unwrap(/*safe - used for ptr value*/); + void* destVoidEnd = (dest + count).template cast<void*>().unwrap(/*safe - ditto*/); + const void* srcVoid = src.template cast<void*>().unwrap(/*safe - ditto*/); + const void* srcVoidEnd = (src + count).template cast<void*>().unwrap(/*safe - ditto*/); + MOZ_ASSERT(!IsInRange(destVoid, srcVoid, srcVoidEnd)); + MOZ_ASSERT(!IsInRange(srcVoid, destVoid, destVoidEnd)); +#endif + + using namespace jit; + + for (; count > 0; count--) { + AtomicOperations::storeSafeWhenRacy(dest++, + To(AtomicOperations::loadSafeWhenRacy(src++))); + } +} + +struct DisjointElements +{ + template <typename To> + static void + copy(SharedMem<To*> dest, SharedMem<void*> src, Scalar::Type fromType, uint32_t count) { + switch (fromType) { + case Scalar::Int8: + CopyValues(dest, src.cast<int8_t*>(), count); + return; + + case Scalar::Uint8: + CopyValues(dest, src.cast<uint8_t*>(), count); + return; + + case Scalar::Int16: + CopyValues(dest, src.cast<int16_t*>(), count); + return; + + case Scalar::Uint16: + CopyValues(dest, src.cast<uint16_t*>(), count); + return; + + case Scalar::Int32: + CopyValues(dest, src.cast<int32_t*>(), count); + return; + + case Scalar::Uint32: + CopyValues(dest, src.cast<uint32_t*>(), count); + return; + + case Scalar::Float32: + CopyValues(dest, src.cast<float*>(), count); + return; + + case Scalar::Float64: + CopyValues(dest, src.cast<double*>(), count); + return; + + case Scalar::Uint8Clamped: + CopyValues(dest, src.cast<uint8_clamped*>(), count); + return; + + default: + MOZ_CRASH("NonoverlappingSet with bogus from-type"); + } + } +}; + +static void +CopyToDisjointArray(TypedArrayObject* target, uint32_t targetOffset, SharedMem<void*> src, + Scalar::Type srcType, uint32_t count) +{ + Scalar::Type destType = target->type(); + SharedMem<uint8_t*> dest = target->viewDataEither().cast<uint8_t*>() + targetOffset * TypedArrayElemSize(destType); + + switch (destType) { + case Scalar::Int8: { + DisjointElements::copy(dest.cast<int8_t*>(), src, srcType, count); + break; + } + + case Scalar::Uint8: { + DisjointElements::copy(dest.cast<uint8_t*>(), src, srcType, count); + break; + } + + case Scalar::Int16: { + DisjointElements::copy(dest.cast<int16_t*>(), src, srcType, count); + break; + } + + case Scalar::Uint16: { + DisjointElements::copy(dest.cast<uint16_t*>(), src, srcType, count); + break; + } + + case Scalar::Int32: { + DisjointElements::copy(dest.cast<int32_t*>(), src, srcType, count); + break; + } + + case Scalar::Uint32: { + DisjointElements::copy(dest.cast<uint32_t*>(), src, srcType, count); + break; + } + + case Scalar::Float32: { + DisjointElements::copy(dest.cast<float*>(), src, srcType, count); + break; + } + + case Scalar::Float64: { + DisjointElements::copy(dest.cast<double*>(), src, srcType, count); + break; + } + + case Scalar::Uint8Clamped: { + DisjointElements::copy(dest.cast<uint8_clamped*>(), src, srcType, count); + break; + } + + default: + MOZ_CRASH("setFromTypedArray with a typed array with bogus type"); + } +} + +// |unsafeSrcCrossCompartment| is produced by |DangerouslyUnwrapTypedArray|, +// counseling extreme caution when using it. As directed by +// |DangerouslyUnwrapTypedArray|, sigil this pointer and all variables derived +// from it to counsel extreme caution here. +void +js::SetDisjointTypedElements(TypedArrayObject* target, uint32_t targetOffset, + TypedArrayObject* unsafeSrcCrossCompartment) +{ + Scalar::Type unsafeSrcTypeCrossCompartment = unsafeSrcCrossCompartment->type(); + + SharedMem<void*> unsafeSrcDataCrossCompartment = unsafeSrcCrossCompartment->viewDataEither(); + uint32_t count = unsafeSrcCrossCompartment->length(); + + CopyToDisjointArray(target, targetOffset, + unsafeSrcDataCrossCompartment, + unsafeSrcTypeCrossCompartment, count); +} + +static bool +intrinsic_SetDisjointTypedElements(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 3); + + Rooted<TypedArrayObject*> target(cx, &args[0].toObject().as<TypedArrayObject>()); + MOZ_ASSERT(!target->hasDetachedBuffer(), + "a typed array viewing a detached buffer has no elements to " + "set, so it's nonsensical to be setting them"); + + uint32_t targetOffset = uint32_t(args[1].toInt32()); + + // As directed by |DangerouslyUnwrapTypedArray|, sigil this pointer and all + // variables derived from it to counsel extreme caution here. + Rooted<TypedArrayObject*> unsafeSrcCrossCompartment(cx); + unsafeSrcCrossCompartment = DangerouslyUnwrapTypedArray(cx, &args[2].toObject()); + if (!unsafeSrcCrossCompartment) + return false; + + SetDisjointTypedElements(target, targetOffset, unsafeSrcCrossCompartment); + + args.rval().setUndefined(); + return true; +} + +static bool +intrinsic_SetOverlappingTypedElements(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 3); + + Rooted<TypedArrayObject*> target(cx, &args[0].toObject().as<TypedArrayObject>()); + MOZ_ASSERT(!target->hasDetachedBuffer(), + "shouldn't set elements if underlying buffer is detached"); + + uint32_t targetOffset = uint32_t(args[1].toInt32()); + + // As directed by |DangerouslyUnwrapTypedArray|, sigil this pointer and all + // variables derived from it to counsel extreme caution here. + Rooted<TypedArrayObject*> unsafeSrcCrossCompartment(cx); + unsafeSrcCrossCompartment = DangerouslyUnwrapTypedArray(cx, &args[2].toObject()); + if (!unsafeSrcCrossCompartment) + return false; + + // Smarter algorithms exist to perform overlapping transfers of the sort + // this method performs (for example, v8's self-hosted implementation). + // But it seems likely deliberate overlapping transfers are rare enough + // that it's not worth the trouble to implement one (and worry about its + // safety/correctness!). Make a copy and do a disjoint set from that. + uint32_t count = unsafeSrcCrossCompartment->length(); + Scalar::Type unsafeSrcTypeCrossCompartment = unsafeSrcCrossCompartment->type(); + size_t sourceByteLen = count * TypedArrayElemSize(unsafeSrcTypeCrossCompartment); + + auto copyOfSrcData = target->zone()->make_pod_array<uint8_t>(sourceByteLen); + if (!copyOfSrcData) + return false; + + jit::AtomicOperations::memcpySafeWhenRacy(SharedMem<uint8_t*>::unshared(copyOfSrcData.get()), + unsafeSrcCrossCompartment->viewDataEither().cast<uint8_t*>(), + sourceByteLen); + + CopyToDisjointArray(target, targetOffset, SharedMem<void*>::unshared(copyOfSrcData.get()), + unsafeSrcTypeCrossCompartment, count); + + 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); + + RootedString matched(cx, args[0].toString()); + RootedString string(cx, args[1].toString()); + + int32_t position = int32_t(args[2].toNumber()); + MOZ_ASSERT(position >= 0); + + RootedObject captures(cx, &args[3].toObject()); +#ifdef DEBUG + bool isArray = false; + MOZ_ALWAYS_TRUE(IsArray(cx, captures, &isArray)); + MOZ_ASSERT(isArray); +#endif + + RootedString replacement(cx, args[4].toString()); + + int32_t firstDollarIndex = int32_t(args[5].toNumber()); + MOZ_ASSERT(firstDollarIndex >= 0); + + RootedLinearString matchedLinear(cx, matched->ensureLinear(cx)); + if (!matchedLinear) + return false; + RootedLinearString stringLinear(cx, string->ensureLinear(cx)); + if (!stringLinear) + return false; + RootedLinearString replacementLinear(cx, replacement->ensureLinear(cx)); + if (!replacementLinear) + return false; + + return RegExpGetSubstitution(cx, matchedLinear, stringLinear, size_t(position), captures, + replacementLinear, size_t(firstDollarIndex), 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; +} + +bool +js::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()); + + RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); + if (!group) + return false; + + RootedObject aobj(cx); + aobj = str_split_string(cx, group, 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()); + + RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); + if (!group) + return false; + + RootedObject aobj(cx); + aobj = str_split_string(cx, group, 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()); + + RootedValue selfHostedFun(cx); + if (!GlobalObject::getIntrinsicValue(cx, cx->global(), name, &selfHostedFun)) + return false; + + MOZ_ASSERT(selfHostedFun.toObject().is<JSFunction>()); + + 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 js::Call(cx, selfHostedFun, args.thisv(), args2, args.rval()); +} + +bool +js::CallSelfHostedFunction(JSContext* cx, const char* name, HandleValue thisv, + const AnyInvokeArgs& args, MutableHandleValue rval) +{ + RootedAtom funAtom(cx, Atomize(cx, name, strlen(name))); + if (!funAtom) + return false; + RootedPropertyName funName(cx, funAtom->asPropertyName()); + return CallSelfHostedFunction(cx, funName, thisv, args, rval); +} + +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 + // IsTypedArrayEnsuringArrayBuffer 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. + ScriptFrameIter iter(cx); + MOZ_ASSERT(iter.isFunctionFrame()); + + while (!iter.done()) { + MOZ_ASSERT(iter.callee(cx)->isSelfHostedOrIntrinsic() && + !iter.callee(cx)->isBoundFunction()); + JSAutoByteString funNameBytes; + const char* funName = GetFunctionNameBytes(cx, iter.callee(cx), &funNameBytes); + if (!funName) + return false; + if (strcmp(funName, "IsTypedArrayEnsuringArrayBuffer") != 0) { + JS_ReportErrorNumberLatin1(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; +} + +/** + * 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); + + const char* locale = cx->runtime()->getDefaultLocale(); + if (!locale) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEFAULT_LOCALE_ERROR); + return false; + } + + RootedString jslocale(cx, JS_NewStringCopyZ(cx, locale)); + if (!jslocale) + return false; + + args.rval().setString(jslocale); + return true; +} + +static bool +intrinsic_AddContentTelemetry(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + + int id = args[0].toInt32(); + MOZ_ASSERT(id < JS_TELEMETRY_END); + MOZ_ASSERT(id >= 0); + + if (!cx->compartment()->isProbablySystemOrAddonCode()) + cx->runtime()->addTelemetry(id, args[1].toInt32()); + + args.rval().setUndefined(); + return true; +} + +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()); + + RootedObject object(cx, &args[0].toObject()); + object = CheckedUnwrap(object); + MOZ_ASSERT(object->is<TypedArrayObject>()); + + 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. + RootedObject ctor(cx); + if (!GetBuiltinConstructor(cx, protoKey, &ctor)) + return false; + + args.rval().setObject(*ctor); + return true; +} + +static bool +intrinsic_NameForTypedArray(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + RootedObject object(cx, &args[0].toObject()); + MOZ_ASSERT(object->is<TypedArrayObject>()); + + JSProtoKey protoKey = StandardProtoKeyOrNull(object); + MOZ_ASSERT(protoKey); + + args.rval().setString(ClassName(protoKey, cx)); + return true; +} + +static bool +intrinsic_HostResolveImportedModule(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(args[0].toObject().is<ModuleObject>()); + MOZ_ASSERT(args[1].isString()); + + RootedFunction moduleResolveHook(cx, cx->global()->moduleResolveHook()); + if (!moduleResolveHook) { + JS_ReportErrorASCII(cx, "Module resolve hook not set"); + return false; + } + + RootedValue result(cx); + if (!JS_CallFunction(cx, nullptr, moduleResolveHook, args, &result)) + return false; + + if (!result.isObject() || !result.toObject().is<ModuleObject>()) { + JS_ReportErrorASCII(cx, "Module resolve hook did not return Module object"); + return false; + } + + args.rval().set(result); + return true; +} + +static bool +intrinsic_CreateModuleEnvironment(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>()); + module->createEnvironment(); + args.rval().setUndefined(); + 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); + MOZ_ASSERT(environment->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL)); + environment->setSlot(shape->slot(), args[2]); + 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_SetModuleState(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>()); + ModuleState newState = args[1].toInt32(); + module->setState(newState); + args.rval().setUndefined(); + return true; +} + +static bool +intrinsic_EvaluateModule(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::evaluate(cx, module, args.rval()); +} + +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()); + RootedObject namespace_(cx, 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; +} + +// 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_FN("std_Array_join", array_join, 1,0), + JS_INLINABLE_FN("std_Array_push", array_push, 1,0, ArrayPush), + JS_INLINABLE_FN("std_Array_pop", array_pop, 0,0, ArrayPop), + JS_INLINABLE_FN("std_Array_shift", array_shift, 0,0, ArrayShift), + JS_FN("std_Array_unshift", array_unshift, 1,0), + JS_INLINABLE_FN("std_Array_slice", array_slice, 2,0, ArraySlice), + JS_FN("std_Array_sort", array_sort, 1,0), + JS_FN("std_Array_reverse", array_reverse, 0,0), + JS_INLINABLE_FN("std_Array_splice", array_splice, 2,0, ArraySplice), + + 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_imul", math_imul, 2,0, MathImul), + JS_INLINABLE_FN("std_Math_log2", math_log2, 1,0, MathLog2), + + JS_FN("std_Map_has", MapObject::has, 1,0), + JS_FN("std_Map_iterator", MapObject::entries, 0,0), + + JS_FN("std_Number_valueOf", num_valueOf, 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_defineProperty", obj_defineProperty, 3,0), + JS_FN("std_Object_getOwnPropertyNames", obj_getOwnPropertyNames, 1,0), + JS_FN("std_Object_getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor, 2,0), + JS_FN("std_Object_hasOwnProperty", obj_hasOwnProperty, 1,0), + JS_FN("std_Object_setPrototypeOf", intrinsic_SetPrototype, 2,0), + JS_FN("std_Object_toString", obj_toString, 0,0), + + JS_FN("std_Reflect_getPrototypeOf", Reflect_getPrototypeOf, 1,0), + JS_FN("std_Reflect_isExtensible", Reflect_isExtensible, 1,0), + + JS_FN("std_Set_has", SetObject::has, 1,0), + JS_FN("std_Set_iterator", SetObject::values, 0,0), + + JS_INLINABLE_FN("std_String_fromCharCode", str_fromCharCode, 1,0, StringFromCharCode), + 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_lastIndexOf", str_lastIndexOf, 1,0), + JS_FN("std_String_startsWith", str_startsWith, 1,0), + JS_FN("std_String_toLowerCase", str_toLowerCase, 0,0), + JS_FN("std_String_toUpperCase", str_toUpperCase, 0,0), + + JS_INLINABLE_FN("std_String_charAt", str_charAt, 1,0, StringCharAt), + JS_FN("std_String_endsWith", str_endsWith, 1,0), + JS_FN("std_String_trim", str_trim, 0,0), + JS_FN("std_String_trimLeft", str_trimLeft, 0,0), + JS_FN("std_String_trimRight", str_trimRight, 0,0), + JS_FN("std_String_toLocaleLowerCase", str_toLocaleLowerCase, 0,0), + JS_FN("std_String_toLocaleUpperCase", str_toLocaleUpperCase, 0,0), +#if !EXPOSE_INTL_API + JS_FN("std_String_localeCompare", str_localeCompare, 1,0), +#else + JS_FN("std_String_normalize", str_normalize, 0,0), +#endif + JS_FN("std_String_concat", str_concat, 1,0), + + JS_FN("std_TypedArray_buffer", js::TypedArray_bufferGetter, 1,0), + + JS_FN("std_WeakMap_has", WeakMap_has, 1,0), + JS_FN("std_WeakMap_get", WeakMap_get, 2,0), + JS_FN("std_WeakMap_set", WeakMap_set, 2,0), + JS_FN("std_WeakMap_delete", WeakMap_delete, 1,0), + + JS_FN("std_SIMD_Int8x16_extractLane", simd_int8x16_extractLane, 2,0), + JS_FN("std_SIMD_Int16x8_extractLane", simd_int16x8_extractLane, 2,0), + JS_INLINABLE_FN("std_SIMD_Int32x4_extractLane", simd_int32x4_extractLane, 2,0, SimdInt32x4_extractLane), + JS_FN("std_SIMD_Uint8x16_extractLane", simd_uint8x16_extractLane, 2,0), + JS_FN("std_SIMD_Uint16x8_extractLane", simd_uint16x8_extractLane, 2,0), + JS_FN("std_SIMD_Uint32x4_extractLane", simd_uint32x4_extractLane, 2,0), + JS_INLINABLE_FN("std_SIMD_Float32x4_extractLane", simd_float32x4_extractLane,2,0, SimdFloat32x4_extractLane), + JS_FN("std_SIMD_Float64x2_extractLane", simd_float64x2_extractLane, 2,0), + JS_FN("std_SIMD_Bool8x16_extractLane", simd_bool8x16_extractLane, 2,0), + JS_FN("std_SIMD_Bool16x8_extractLane", simd_bool16x8_extractLane, 2,0), + JS_FN("std_SIMD_Bool32x4_extractLane", simd_bool32x4_extractLane, 2,0), + JS_FN("std_SIMD_Bool64x2_extractLane", simd_bool64x2_extractLane, 2,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("IsWrappedArrayConstructor", intrinsic_IsWrappedArrayConstructor, 1,0, + IntrinsicIsWrappedArrayConstructor), + JS_INLINABLE_FN("ToInteger", intrinsic_ToInteger, 1,0, IntrinsicToInteger), + JS_INLINABLE_FN("ToString", intrinsic_ToString, 1,0, IntrinsicToString), + 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("IsFunctionObject",intrinsic_IsInstanceOfBuiltin<JSFunction>, 1,0), + JS_FN("GetBuiltinConstructorImpl", intrinsic_GetBuiltinConstructor, 1,0), + 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("ThrowInternalError", intrinsic_ThrowInternalError, 4,0), + JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1,0), + JS_FN("DumpMessage", intrinsic_DumpMessage, 1,0), + JS_FN("OwnPropertyKeys", intrinsic_OwnPropertyKeys, 1,0), + JS_FN("MakeDefaultConstructor", intrinsic_MakeDefaultConstructor, 2,0), + JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0), + JS_FN("_NameForTypedArray", intrinsic_NameForTypedArray, 1,0), + JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0), + JS_FN("_FinishBoundFunctionInit", intrinsic_FinishBoundFunctionInit, 3,0), + JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0), + JS_FN("AddContentTelemetry", intrinsic_AddContentTelemetry, 2,0), + + JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing, 0,0, + IntrinsicIsConstructing), + JS_INLINABLE_FN("SubstringKernel", intrinsic_SubstringKernel, 3,0, + IntrinsicSubstringKernel), + JS_INLINABLE_FN("_DefineDataProperty", intrinsic_DefineDataProperty, 4,0, + IntrinsicDefineDataProperty), + 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("NewArrayInCompartment", intrinsic_NewArrayInCompartment, 1,0), + + JS_FN("IsPackedArray", intrinsic_IsPackedArray, 1,0), + + JS_FN("GetIteratorPrototype", intrinsic_GetIteratorPrototype, 0,0), + + JS_FN("NewArrayIterator", intrinsic_NewArrayIterator, 0,0), + JS_FN("CallArrayIteratorMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<ArrayIteratorObject>>, 2,0), + + JS_FN("NewListIterator", intrinsic_NewListIterator, 0,0), + JS_FN("CallListIteratorMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<ListIteratorObject>>, 2,0), + JS_FN("ActiveFunction", intrinsic_ActiveFunction, 0,0), + + JS_FN("_SetCanonicalName", intrinsic_SetCanonicalName, 2,0), + + JS_INLINABLE_FN("IsArrayIterator", + intrinsic_IsInstanceOfBuiltin<ArrayIteratorObject>, 1,0, + IntrinsicIsArrayIterator), + JS_INLINABLE_FN("IsMapIterator", + intrinsic_IsInstanceOfBuiltin<MapIteratorObject>, 1,0, + IntrinsicIsMapIterator), + JS_INLINABLE_FN("IsSetIterator", + intrinsic_IsInstanceOfBuiltin<SetIteratorObject>, 1,0, + IntrinsicIsSetIterator), + JS_INLINABLE_FN("IsStringIterator", + intrinsic_IsInstanceOfBuiltin<StringIteratorObject>, 1,0, + IntrinsicIsStringIterator), + JS_INLINABLE_FN("IsListIterator", + intrinsic_IsInstanceOfBuiltin<ListIteratorObject>, 1,0, + IntrinsicIsListIterator), + + 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_FN("NewStringIterator", intrinsic_NewStringIterator, 0,0), + JS_FN("CallStringIteratorMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<StringIteratorObject>>, 2,0), + + JS_FN("IsStarGeneratorObject", + intrinsic_IsInstanceOfBuiltin<StarGeneratorObject>, 1,0), + JS_FN("StarGeneratorObjectIsClosed", intrinsic_StarGeneratorObjectIsClosed, 1,0), + JS_FN("IsSuspendedStarGenerator",intrinsic_IsSuspendedStarGenerator,1,0), + + JS_FN("IsLegacyGeneratorObject", + intrinsic_IsInstanceOfBuiltin<LegacyGeneratorObject>, 1,0), + JS_FN("LegacyGeneratorObjectIsClosed", intrinsic_LegacyGeneratorObjectIsClosed, 1,0), + JS_FN("CloseClosingLegacyGeneratorObject", intrinsic_CloseClosingLegacyGeneratorObject, 1,0), + JS_FN("ThrowStopIteration", intrinsic_ThrowStopIteration, 0,0), + + JS_FN("GeneratorIsRunning", intrinsic_GeneratorIsRunning, 1,0), + JS_FN("GeneratorSetClosed", intrinsic_GeneratorSetClosed, 1,0), + + JS_FN("IsArrayBuffer", + intrinsic_IsInstanceOfBuiltin<ArrayBufferObject>, 1,0), + JS_FN("IsSharedArrayBuffer", + intrinsic_IsInstanceOfBuiltin<SharedArrayBufferObject>, 1,0), + JS_FN("IsWrappedArrayBuffer", + intrinsic_IsWrappedArrayBuffer<ArrayBufferObject>, 1,0), + JS_FN("IsWrappedSharedArrayBuffer", + intrinsic_IsWrappedArrayBuffer<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>, 5,0), + + JS_FN("SharedArrayBufferByteLength", + intrinsic_ArrayBufferByteLength<SharedArrayBufferObject>, 1,0), + JS_FN("PossiblyWrappedSharedArrayBufferByteLength", + intrinsic_PossiblyWrappedArrayBufferByteLength<SharedArrayBufferObject>, 1,0), + JS_FN("SharedArrayBufferCopyData", + intrinsic_ArrayBufferCopyData<SharedArrayBufferObject>, 5,0), + + JS_FN("IsUint8TypedArray", intrinsic_IsUint8TypedArray, 1,0), + JS_FN("IsInt8TypedArray", intrinsic_IsInt8TypedArray, 1,0), + JS_FN("IsUint16TypedArray", intrinsic_IsUint16TypedArray, 1,0), + JS_FN("IsInt16TypedArray", intrinsic_IsInt16TypedArray, 1,0), + JS_FN("IsUint32TypedArray", intrinsic_IsUint32TypedArray, 1,0), + JS_FN("IsInt32TypedArray", intrinsic_IsInt32TypedArray, 1,0), + JS_FN("IsFloat32TypedArray", intrinsic_IsFloat32TypedArray, 1,0), + JS_INLINABLE_FN("IsTypedArray", + intrinsic_IsInstanceOfBuiltin<TypedArrayObject>, 1,0, + IntrinsicIsTypedArray), + JS_INLINABLE_FN("IsPossiblyWrappedTypedArray",intrinsic_IsPossiblyWrappedTypedArray,1,0, + IntrinsicIsPossiblyWrappedTypedArray), + + JS_FN("TypedArrayBuffer", intrinsic_TypedArrayBuffer, 1,0), + JS_FN("TypedArrayByteOffset", intrinsic_TypedArrayByteOffset, 1,0), + JS_FN("TypedArrayElementShift", intrinsic_TypedArrayElementShift, 1,0), + + 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("MoveTypedArrayElements", intrinsic_MoveTypedArrayElements, 4,0), + JS_FN("SetFromTypedArrayApproach",intrinsic_SetFromTypedArrayApproach, 4, 0), + JS_FN("SetOverlappingTypedElements",intrinsic_SetOverlappingTypedElements,3,0), + + JS_INLINABLE_FN("SetDisjointTypedElements",intrinsic_SetDisjointTypedElements,3,0, + IntrinsicSetDisjointTypedElements), + + 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("CallLegacyGeneratorMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<LegacyGeneratorObject>>, 2, 0), + JS_FN("CallStarGeneratorMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<StarGeneratorObject>>, 2, 0), + + JS_FN("IsWeakSet", intrinsic_IsInstanceOfBuiltin<WeakSetObject>, 1,0), + JS_FN("CallWeakSetMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<WeakSetObject>>, 2, 0), + + JS_FN("Promise_static_resolve", Promise_static_resolve, 1, 0), + JS_FN("Promise_static_reject", Promise_reject, 1, 0), + JS_FN("Promise_then", Promise_then, 2, 0), + + // See builtin/TypedObject.h for descriptors of the typedobj functions. + JS_FN("NewOpaqueTypedObject", js::NewOpaqueTypedObject, 1, 0), + JS_FN("NewDerivedTypedObject", js::NewDerivedTypedObject, 3, 0), + JS_FN("TypedObjectBuffer", TypedObject::GetBuffer, 1, 0), + JS_FN("TypedObjectByteOffset", TypedObject::GetByteOffset, 1, 0), + JS_FN("AttachTypedObject", js::AttachTypedObject, 3, 0), + JS_FN("TypedObjectIsAttached", js::TypedObjectIsAttached, 1, 0), + JS_FN("TypedObjectTypeDescr", js::TypedObjectTypeDescr, 1, 0), + JS_FN("ClampToUint8", js::ClampToUint8, 1, 0), + JS_FN("GetTypedObjectModule", js::GetTypedObjectModule, 0, 0), + JS_FN("GetSimdTypeDescr", js::GetSimdTypeDescr, 1, 0), + + JS_INLINABLE_FN("ObjectIsTypeDescr" , js::ObjectIsTypeDescr, 1, 0, + IntrinsicObjectIsTypeDescr), + JS_INLINABLE_FN("ObjectIsTypedObject", js::ObjectIsTypedObject, 1, 0, + IntrinsicObjectIsTypedObject), + JS_INLINABLE_FN("ObjectIsOpaqueTypedObject", js::ObjectIsOpaqueTypedObject, 1, 0, + IntrinsicObjectIsOpaqueTypedObject), + JS_INLINABLE_FN("ObjectIsTransparentTypedObject", js::ObjectIsTransparentTypedObject, 1, 0, + IntrinsicObjectIsTransparentTypedObject), + JS_INLINABLE_FN("TypeDescrIsArrayType", js::TypeDescrIsArrayType, 1, 0, + IntrinsicTypeDescrIsArrayType), + JS_INLINABLE_FN("TypeDescrIsSimpleType", js::TypeDescrIsSimpleType, 1, 0, + IntrinsicTypeDescrIsSimpleType), + JS_INLINABLE_FN("SetTypedObjectOffset", js::SetTypedObjectOffset, 2, 0, + IntrinsicSetTypedObjectOffset), + +#define LOAD_AND_STORE_SCALAR_FN_DECLS(_constant, _type, _name) \ + JS_FN("Store_" #_name, js::StoreScalar##_type::Func, 3, 0), \ + JS_FN("Load_" #_name, js::LoadScalar##_type::Func, 3, 0), + JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(LOAD_AND_STORE_SCALAR_FN_DECLS) +#undef LOAD_AND_STORE_SCALAR_FN_DECLS + +#define LOAD_AND_STORE_REFERENCE_FN_DECLS(_constant, _type, _name) \ + JS_FN("Store_" #_name, js::StoreReference##_name::Func, 3, 0), \ + JS_FN("Load_" #_name, js::LoadReference##_name::Func, 3, 0), + JS_FOR_EACH_REFERENCE_TYPE_REPR(LOAD_AND_STORE_REFERENCE_FN_DECLS) +#undef LOAD_AND_STORE_REFERENCE_FN_DECLS + + // 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), + JS_FN("intl_canonicalizeTimeZone", intl_canonicalizeTimeZone, 1,0), + JS_FN("intl_Collator", intl_Collator, 2,0), + JS_FN("intl_Collator_availableLocales", intl_Collator_availableLocales, 0,0), + JS_FN("intl_CompareStrings", intl_CompareStrings, 3,0), + JS_FN("intl_DateTimeFormat", intl_DateTimeFormat, 2,0), + JS_FN("intl_DateTimeFormat_availableLocales", intl_DateTimeFormat_availableLocales, 0,0), + JS_FN("intl_defaultTimeZone", intl_defaultTimeZone, 0,0), + JS_FN("intl_defaultTimeZoneOffset", intl_defaultTimeZoneOffset, 0,0), + JS_FN("intl_FormatDateTime", intl_FormatDateTime, 2,0), + JS_FN("intl_FormatNumber", intl_FormatNumber, 2,0), + JS_FN("intl_GetCalendarInfo", intl_GetCalendarInfo, 1,0), + JS_FN("intl_IsValidTimeZoneName", intl_IsValidTimeZoneName, 1,0), + JS_FN("intl_NumberFormat", intl_NumberFormat, 2,0), + JS_FN("intl_NumberFormat_availableLocales", intl_NumberFormat_availableLocales, 0,0), + JS_FN("intl_numberingSystem", intl_numberingSystem, 1,0), + JS_FN("intl_patternForSkeleton", intl_patternForSkeleton, 2,0), + + JS_INLINABLE_FN("IsRegExpObject", + intrinsic_IsInstanceOfBuiltin<RegExpObject>, 1,0, + IsRegExpObject), + JS_FN("CallRegExpMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<RegExpObject>>, 2,0), + JS_INLINABLE_FN("RegExpMatcher", RegExpMatcher, 4,0, + RegExpMatcher), + JS_INLINABLE_FN("RegExpSearcher", RegExpSearcher, 4,0, + RegExpSearcher), + JS_INLINABLE_FN("RegExpTester", RegExpTester, 4,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, 6,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_INLINABLE_FN("StringSplitString", intrinsic_StringSplitString, 2, 0, + IntrinsicStringSplitString), + JS_FN("StringSplitStringLimit", intrinsic_StringSplitStringLimit, 3, 0), + + // See builtin/RegExp.h for descriptions of the regexp_* functions. + JS_FN("regexp_exec_no_statics", regexp_exec_no_statics, 2,0), + JS_FN("regexp_test_no_statics", regexp_test_no_statics, 2,0), + JS_FN("regexp_construct_raw_flags", regexp_construct_raw_flags, 2,0), + JS_FN("regexp_clone", regexp_clone, 1,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("CreateModuleEnvironment", intrinsic_CreateModuleEnvironment, 1, 0), + JS_FN("CreateImportBinding", intrinsic_CreateImportBinding, 4, 0), + JS_FN("CreateNamespaceBinding", intrinsic_CreateNamespaceBinding, 3, 0), + JS_FN("InstantiateModuleFunctionDeclarations", + intrinsic_InstantiateModuleFunctionDeclarations, 1, 0), + JS_FN("SetModuleState", intrinsic_SetModuleState, 1, 0), + JS_FN("EvaluateModule", intrinsic_EvaluateModule, 1, 0), + JS_FN("IsModuleNamespace", intrinsic_IsInstanceOfBuiltin<ModuleNamespaceObject>, 1, 0), + JS_FN("NewModuleNamespace", intrinsic_NewModuleNamespace, 2, 0), + JS_FN("AddModuleNamespaceBinding", intrinsic_AddModuleNamespaceBinding, 4, 0), + JS_FN("ModuleNamespaceExports", intrinsic_ModuleNamespaceExports, 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.setSelfHostingMode(true); + options.setCanLazilyParse(false); + options.setVersion(JSVERSION_LATEST); + options.werrorOption = true; + options.strictOption = true; + +#ifdef DEBUG + options.extraWarningsOption = true; +#endif +} + +GlobalObject* +JSRuntime::createSelfHostingGlobal(JSContext* cx) +{ + MOZ_ASSERT(!cx->isExceptionPending()); + MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); + + JS::CompartmentOptions options; + options.creationOptions().setZone(JS::FreshZone); + options.behaviors().setDiscardSource(true); + + JSCompartment* compartment = NewCompartment(cx, nullptr, nullptr, options); + if (!compartment) + return nullptr; + + static const ClassOps shgClassOps = { + nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, + JS_GlobalObjectTraceHook + }; + + static const Class shgClass = { + "self-hosting-global", JSCLASS_GLOBAL_FLAGS, + &shgClassOps + }; + + AutoCompartment ac(cx, compartment); + Rooted<GlobalObject*> shg(cx, GlobalObject::createInternal(cx, &shgClass)); + if (!shg) + return nullptr; + + cx->runtime()->selfHostingGlobal_ = shg; + compartment->isSelfHosting = true; + compartment->setIsSystem(true); + + 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); + + RootedValue exn(cx); + if (!cx->getPendingException(&exn)) { + fprintf(file, "error getting pending exception\n"); + return; + } + cx->clearPendingException(); + + ErrorReport report(cx); + if (!report.init(cx, exn, js::ErrorReport::WithSideEffects)) { + fprintf(file, "out of memory initializing ErrorReport\n"); + return; + } + + MOZ_ASSERT(!JSREPORT_IS_WARNING(report.report()->flags)); + PrintError(cx, file, report.toStringResult(), report.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); + } +}; + +bool +JSRuntime::initSelfHosting(JSContext* cx) +{ + MOZ_ASSERT(!selfHostingGlobal_); + + if (cx->runtime()->parentRuntime) { + selfHostingGlobal_ = cx->runtime()->parentRuntime->selfHostingGlobal_; + return true; + } + + /* + * Self hosted state can be accessed from threads for other runtimes + * parented to this one, so cannot include state in the nursery. + */ + JS::AutoDisableGenerationalGC disable(cx->runtime()); + + Rooted<GlobalObject*> shg(cx, JSRuntime::createSelfHostingGlobal(cx)); + if (!shg) + return false; + + JSAutoCompartment ac(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); + + CompileOptions options(cx); + FillSelfHostingCompileOptions(options); + + RootedValue rv(cx); + + uint32_t srcLen = GetRawScriptsSize(); + + const unsigned char* compressed = compressedSources; + uint32_t compressedLen = GetCompressedSize(); + ScopedJSFreePtr<char> src(selfHostingGlobal_->zone()->pod_malloc<char>(srcLen)); + if (!src || !DecompressString(compressed, compressedLen, + reinterpret_cast<unsigned char*>(src.get()), srcLen)) + { + return false; + } + + if (!Evaluate(cx, options, src, srcLen, &rv)) + return false; + + return true; +} + +void +JSRuntime::finishSelfHosting() +{ + selfHostingGlobal_ = nullptr; +} + +void +JSRuntime::markSelfHostingGlobal(JSTracer* trc) +{ + if (selfHostingGlobal_ && !parentRuntime) + TraceRoot(trc, &selfHostingGlobal_, "self-hosting global"); +} + +bool +JSRuntime::isSelfHostingCompartment(JSCompartment* comp) const +{ + return selfHostingGlobal_->compartment() == comp; +} + +bool +JSRuntime::isSelfHostingZone(const JS::Zone* zone) const +{ + return selfHostingGlobal_ && selfHostingGlobal_->zoneFromAnyThread() == zone; +} + +static bool +CloneValue(JSContext* cx, HandleValue selfHostedValue, MutableHandleValue vp); + +static bool +GetUnclonedValue(JSContext* cx, HandleNativeObject selfHostedObject, + HandleId id, MutableHandleValue vp) +{ + vp.setUndefined(); + + if (JSID_IS_INT(id)) { + size_t index = JSID_TO_INT(id); + if (index < selfHostedObject->getDenseInitializedLength() && + !selfHostedObject->getDenseElement(index).isMagic(JS_ELEMENTS_HOLE)) + { + vp.set(selfHostedObject->getDenseElement(JSID_TO_INT(id))); + return true; + } + } + + // Since all atoms used by self hosting are marked as permanent, any + // attempt to look up a non-permanent atom will fail. We should only + // see such atoms when code is looking for properties on the self + // hosted global which aren't present. + if (JSID_IS_STRING(id) && !JSID_TO_STRING(id)->isPermanentAtom()) { + MOZ_ASSERT(selfHostedObject->is<GlobalObject>()); + RootedValue value(cx, IdToValue(id)); + return ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NO_SUCH_SELF_HOSTED_PROP, + JSDVG_IGNORE_STACK, value, nullptr, nullptr, nullptr); + } + + RootedShape shape(cx, selfHostedObject->lookupPure(id)); + if (!shape) { + RootedValue value(cx, IdToValue(id)); + return ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NO_SUCH_SELF_HOSTED_PROP, + JSDVG_IGNORE_STACK, value, nullptr, nullptr, nullptr); + } + + MOZ_ASSERT(shape->hasSlot() && shape->hasDefaultGetter()); + vp.set(selfHostedObject->getSlot(shape->slot())); + return true; +} + +static bool +CloneProperties(JSContext* cx, HandleNativeObject selfHostedObject, HandleObject clone) +{ + AutoIdVector 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.... + 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]; + if (!GetUnclonedValue(cx, selfHostedObject, id, &selfHostedValue)) + return false; + if (!CloneValue(cx, selfHostedValue, &val) || + !JS_DefinePropertyById(cx, clone, id, val, attrs[i])) + { + return false; + } + } + + return true; +} + +static JSString* +CloneString(JSContext* cx, JSFlatString* 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); +} + +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 the main 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>()); + bool hasName = selfHostedFunction->name() != nullptr; + + // Arrow functions use the first extended slot for their lexical |this| value. + MOZ_ASSERT(!selfHostedFunction->isArrow()); + js::gc::AllocKind kind = hasName + ? gc::AllocKind::FUNCTION_EXTENDED + : selfHostedFunction->getAllocKind(); + MOZ_ASSERT(!CanReuseScriptForClone(cx->compartment(), selfHostedFunction, cx->global())); + Rooted<LexicalEnvironmentObject*> globalLexical(cx, &cx->global()->lexicalEnvironment()); + RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope()); + clone = CloneFunctionAndScript(cx, selfHostedFunction, globalLexical, emptyGlobalScope, + kind); + // To be able to re-lazify the cloned function, its name in the + // self-hosting compartment has to be stored on the clone. + if (clone && hasName) { + clone->as<JSFunction>().setExtendedSlot(LAZY_FUNCTION_NAME_SLOT, + StringValue(selfHostedFunction->name())); + } + } 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(), nullptr, cx->tempLifoAlloc()); + } 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->isFlat()) + MOZ_CRASH(); + RootedString str(cx, CloneString(cx, &selfHostedString->asFlat())); + if (!str) + return nullptr; + clone = StringObject::create(cx, str); + } else if (selfHostedObject->is<ArrayObject>()) { + clone = NewDenseEmptyArray(cx, nullptr, TenuredObject); + } else { + MOZ_ASSERT(selfHostedObject->isNative()); + clone = NewObjectWithGivenProto(cx, selfHostedObject->getClass(), nullptr, + selfHostedObject->asTenured().getAllocKind(), + SingletonObject); + } + 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()->isFlat()) + MOZ_CRASH(); + JSFlatString* selfHostedString = &selfHostedValue.toString()->asFlat(); + 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(size_t(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, + HandleObject proto, NewObjectKind newKind, + MutableHandleFunction fun) +{ + MOZ_ASSERT(newKind != GenericObject); + + RootedAtom funName(cx, name); + JSFunction* selfHostedFun = getUnclonedSelfHostedFunction(cx, selfHostedName); + if (!selfHostedFun) + return false; + + if (!selfHostedFun->isClassConstructor() && !selfHostedFun->hasGuessedAtom() && + selfHostedFun->name() != selfHostedName) + { + MOZ_ASSERT(selfHostedFun->getExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT).toBoolean()); + funName = selfHostedFun->name(); + } + + fun.set(NewScriptedFunction(cx, nargs, JSFunction::INTERPRETED_LAZY, + funName, proto, gc::AllocKind::FUNCTION_EXTENDED, newKind)); + if (!fun) + return false; + fun->setIsSelfHostedBuiltin(); + fun->setExtendedSlot(LAZY_FUNCTION_NAME_SLOT, StringValue(selfHostedName)); + return true; +} + +bool +JSRuntime::cloneSelfHostedFunctionScript(JSContext* cx, HandlePropertyName name, + HandleFunction targetFun) +{ + RootedFunction sourceFun(cx, getUnclonedSelfHostedFunction(cx, name)); + if (!sourceFun) + return false; + // JSFunction::generatorKind can't handle lazy self-hosted functions, so we make sure there + // aren't any. + MOZ_ASSERT(!sourceFun->isGenerator()); + MOZ_ASSERT(targetFun->isExtended()); + MOZ_ASSERT(targetFun->isInterpretedLazy()); + MOZ_ASSERT(targetFun->isSelfHostedBuiltin()); + + RootedScript sourceScript(cx, sourceFun->getOrCreateScript(cx)); + if (!sourceScript) + 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)) + return false; + MOZ_ASSERT(!targetFun->isInterpretedLazy()); + + MOZ_ASSERT(sourceFun->nargs() == targetFun->nargs()); + MOZ_ASSERT(sourceFun->hasRest() == targetFun->hasRest()); + + // The target function might have been relazified after its flags changed. + targetFun->setFlags(targetFun->flags() | sourceFun->flags()); + return true; +} + +bool +JSRuntime::getUnclonedSelfHostedValue(JSContext* cx, HandlePropertyName name, + MutableHandleValue vp) +{ + RootedId id(cx, NameToId(name)); + return GetUnclonedValue(cx, HandleNativeObject::fromMarkedLocation(&selfHostingGlobal_), id, vp); +} + +JSFunction* +JSRuntime::getUnclonedSelfHostedFunction(JSContext* cx, HandlePropertyName name) +{ + RootedValue selfHostedValue(cx); + if (!getUnclonedSelfHostedValue(cx, name, &selfHostedValue)) + return nullptr; + + return &selfHostedValue.toObject().as<JSFunction>(); +} + +bool +JSRuntime::cloneSelfHostedValue(JSContext* cx, HandlePropertyName name, MutableHandleValue vp) +{ + RootedValue selfHostedValue(cx); + if (!getUnclonedSelfHostedValue(cx, name, &selfHostedValue)) + return false; + + /* + * 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(cx, name); + MOZ_ASSERT(selfHostedFun); + MOZ_ASSERT(selfHostedFun->getExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT).toBoolean()); +#endif +} + +JSFunction* +js::SelfHostedFunction(JSContext* cx, HandlePropertyName propName) +{ + RootedValue func(cx); + if (!GlobalObject::getIntrinsicValue(cx, cx->global(), propName, &func)) + return nullptr; + + MOZ_ASSERT(func.isObject()); + MOZ_ASSERT(func.toObject().is<JSFunction>()); + return &func.toObject().as<JSFunction>(); +} + +bool +js::IsSelfHostedFunctionWithName(JSFunction* fun, JSAtom* name) +{ + return fun->isSelfHostedBuiltin() && GetSelfHostedFunctionName(fun) == name; +} + +JSAtom* +js::GetSelfHostedFunctionName(JSFunction* fun) +{ + Value name = fun->getExtendedSlot(LAZY_FUNCTION_NAME_SLOT); + if (!name.isString()) + return nullptr; + return &name.toString()->asAtom(); +} + +static_assert(JSString::MAX_LENGTH <= INT32_MAX, + "StringIteratorNext in builtin/String.js assumes the stored index " + "into the string is an Int32Value"); |