/* -*- 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;
}

template<typename T>
static bool
intrinsic_GuardToBuiltin(JSContext* cx, unsigned argc, Value* vp)
{
    CallArgs args = CallArgsFromVp(argc, vp);
    MOZ_ASSERT(args.length() == 1);
    MOZ_ASSERT(args[0].isObject());

    if (args[0].toObject().is<T>()) {
        args.rval().setObject(args[0].toObject());
        return true;
    }
    args.rval().setNull();
    return true;
}

/**
 * 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),
    JS_FN("std_String_normalize",                str_normalize,                0,0),
    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("GuardToArrayIterator",
                    intrinsic_GuardToBuiltin<ArrayIteratorObject>, 1,0,
                    IntrinsicGuardToArrayIterator),
    JS_INLINABLE_FN("GuardToMapIterator",
                    intrinsic_GuardToBuiltin<MapIteratorObject>,   1,0,
                    IntrinsicGuardToMapIterator),
    JS_INLINABLE_FN("GuardToSetIterator",
                    intrinsic_GuardToBuiltin<SetIteratorObject>,   1,0,
                    IntrinsicGuardToSetIterator),
    JS_INLINABLE_FN("GuardToStringIterator",
                    intrinsic_GuardToBuiltin<StringIteratorObject>, 1,0,
                    IntrinsicGuardToStringIterator),
    JS_INLINABLE_FN("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_INLINABLE_FN("GuardToMapObject", intrinsic_GuardToBuiltin<MapObject>, 1, 0,
          IntrinsicGuardToMapObject),
    JS_FN("IsWeakSet", intrinsic_IsInstanceOfBuiltin<WeakSetObject>, 1,0),

    JS_INLINABLE_FN("GuardToSetObject", intrinsic_GuardToBuiltin<SetObject>, 1, 0,
          IntrinsicGuardToSetObject),
    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_ComputeDisplayNames", intl_ComputeDisplayNames, 3,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->explicitName() != 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->explicitName()));
        }
    } 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->explicitName() != selfHostedName)
    {
        MOZ_ASSERT(selfHostedFun->getExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT).toBoolean());
        funName = selfHostedFun->explicitName();
    }

    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(sourceScript->hasRest() == targetFun->nonLazyScript()->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");