/* -*- 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/Casting.h" #include "mozilla/DebugOnly.h" #include "jscntxt.h" #include "jscompartment.h" #include "jsdate.h" #include "jsfriendapi.h" #include "jshashutil.h" #include "jsweakmap.h" #include "jswrapper.h" #include "selfhosted.out.h" #include "builtin/Intl.h" #include "builtin/Object.h" #include "builtin/SelfHostingDefines.h" #include "builtin/TypedObject.h" #include "builtin/WeakSetObject.h" #include "gc/Marking.h" #include "vm/Compression.h" #include "vm/GeneratorObject.h" #include "vm/Interpreter.h" #include "vm/String.h" #include "vm/TypedArrayCommon.h" #include "jsfuninlines.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; static void selfHosting_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) { PrintError(cx, stderr, message, report, true); } bool js::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; } bool js::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; } bool js::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; } bool js::intrinsic_ToString(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); RootedString str(cx); str = ToString(cx, args[0]); if (!str) return false; args.rval().setString(str); return true; } bool intrinsic_ToPropertyKey(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); RootedId id(cx); if (!ValueToId(cx, args[0], &id)) return false; args.rval().set(IdToValue(id)); return true; } bool js::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; } bool js::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); return GetOwnPropertyKeys(cx, args, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS); } bool js::intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() >= 1); uint32_t errorNumber = args[0].toInt32(); #ifdef DEBUG const JSErrorFormatString *efs = GetErrorMessage(nullptr, errorNumber); MOZ_ASSERT(efs->argCount == args.length() - 1); #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(cx, val); if (!str) return false; errorArgs[i - 1].encodeLatin1(cx, str); } else if (val.isString()) { errorArgs[i - 1].encodeLatin1(cx, val.toString()); } else { errorArgs[i - 1].initBytes(DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, NullPtr()).release()); } if (!errorArgs[i - 1]) return false; } JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, errorNumber, errorArgs[0].ptr(), errorArgs[1].ptr(), errorArgs[2].ptr()); 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(cx, args[0]); if (str) { fprintf(stderr, "Self-hosted JavaScript assertion info: "); str->dumpCharsNoNewline(); fputc('\n', stderr); } } #endif MOZ_ASSERT(false); return false; } 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()); MOZ_ASSERT(args[1].isObject()); // 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().setIsSelfHostedConstructor(); 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 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; } /* * NewDenseArray(length): Allocates and returns a new dense array with * the given length where all values are initialized to holes. */ bool js::intrinsic_NewDenseArray(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); // Check that index is an int32 if (!args[0].isInt32()) { JS_ReportError(cx, "Expected int32 as second argument"); return false; } uint32_t length = args[0].toInt32(); // Make a new buffer and initialize it up to length. RootedArrayObject buffer(cx, NewDenseFullyAllocatedArray(cx, length)); if (!buffer) return false; ObjectGroup *newgroup = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array); if (!newgroup) return false; buffer->setGroup(newgroup); NativeObject::EnsureDenseResult edr = buffer->ensureDenseElements(cx, length, 0); switch (edr) { case NativeObject::ED_OK: args.rval().setObject(*buffer); return true; case NativeObject::ED_SPARSE: // shouldn't happen! MOZ_ASSERT(!"%EnsureDenseArrayElements() would yield sparse array"); JS_ReportError(cx, "%EnsureDenseArrayElements() would yield sparse array"); break; case NativeObject::ED_FAILED: break; } return false; } /* * UnsafePutElements(arr0, idx0, elem0, ..., arrN, idxN, elemN): For each set of * (arr, idx, elem) arguments that are passed, performs the assignment * |arr[idx] = elem|. |arr| must be either a dense array or a typed array. * * If |arr| is a dense array, the index must be an int32 less than the * initialized length of |arr|. Use |%EnsureDenseResultArrayElements| * to ensure that the initialized length is long enough. * * If |arr| is a typed array, the index must be an int32 less than the * length of |arr|. */ bool js::intrinsic_UnsafePutElements(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); if ((args.length() % 3) != 0) { JS_ReportError(cx, "Incorrect number of arguments, not divisible by 3"); return false; } for (uint32_t base = 0; base < args.length(); base += 3) { uint32_t arri = base; uint32_t idxi = base+1; uint32_t elemi = base+2; MOZ_ASSERT(args[arri].isObject()); MOZ_ASSERT(args[arri].toObject().isNative() || IsTypedObjectArray(args[arri].toObject())); MOZ_ASSERT(args[idxi].isInt32()); RootedObject arrobj(cx, &args[arri].toObject()); uint32_t idx = args[idxi].toInt32(); if (IsAnyTypedArray(arrobj.get()) || arrobj->is()) { MOZ_ASSERT_IF(IsAnyTypedArray(arrobj.get()), idx < AnyTypedArrayLength(arrobj.get())); MOZ_ASSERT_IF(arrobj->is(), idx < uint32_t(arrobj->as().length())); RootedValue tmp(cx, args[elemi]); // XXX: Always non-strict. ObjectOpResult ignored; if (!SetElement(cx, arrobj, arrobj, idx, &tmp, ignored)) return false; } else { MOZ_ASSERT(idx < arrobj->as().getDenseInitializedLength()); arrobj->as().setDenseElementWithType(cx, idx, args[elemi]); } } args.rval().setUndefined(); return true; } bool js::intrinsic_DefineDataProperty(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 4); MOZ_ASSERT(args[0].isObject()); MOZ_ASSERT(args[3].isInt32()); RootedObject obj(cx, &args[0].toObject()); RootedId id(cx); if (!ValueToId(cx, args[1], &id)) return false; RootedValue value(cx, args[2]); unsigned attributes = args[3].toInt32(); Rooted desc(cx); unsigned attrs = 0; 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; desc.setDataDescriptor(value, attrs); return StandardDefineProperty(cx, obj, id, desc); } bool js::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().setReservedSlot(args[1].toPrivateUint32(), args[2]); args.rval().setUndefined(); return true; } bool js::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().getReservedSlot(args[1].toPrivateUint32())); return true; } bool js::intrinsic_UnsafeGetObjectFromReservedSlot(JSContext *cx, unsigned argc, Value *vp) { if (!intrinsic_UnsafeGetReservedSlot(cx, argc, vp)) return false; MOZ_ASSERT(vp->isObject()); return true; } bool js::intrinsic_UnsafeGetInt32FromReservedSlot(JSContext *cx, unsigned argc, Value *vp) { if (!intrinsic_UnsafeGetReservedSlot(cx, argc, vp)) return false; MOZ_ASSERT(vp->isInt32()); return true; } bool js::intrinsic_UnsafeGetStringFromReservedSlot(JSContext *cx, unsigned argc, Value *vp) { if (!intrinsic_UnsafeGetReservedSlot(cx, argc, vp)) return false; MOZ_ASSERT(vp->isString()); return true; } bool js::intrinsic_UnsafeGetBooleanFromReservedSlot(JSContext *cx, unsigned argc, Value *vp) { if (!intrinsic_UnsafeGetReservedSlot(cx, argc, vp)) return false; MOZ_ASSERT(vp->isBoolean()); return true; } bool js::intrinsic_IsPackedArray(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 1); MOZ_ASSERT(args[0].isObject()); JSObject *obj = &args[0].toObject(); bool isPacked = obj->is() && !obj->hasLazyGroup() && !obj->group()->hasAllFlags(OBJECT_FLAG_NON_PACKED) && obj->as().getDenseInitializedLength() == obj->as().length(); args.rval().setBoolean(isPacked); 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; } bool js::intrinsic_IsArrayIterator(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()); 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; } bool js::intrinsic_IsStringIterator(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()); return true; } static bool intrinsic_IsStarGeneratorObject(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()); 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(); 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()) { args.rval().setBoolean(false); return true; } StarGeneratorObject &genObj = args[0].toObject().as(); args.rval().setBoolean(!genObj.isClosed() && genObj.isSuspended()); return true; } static bool intrinsic_IsLegacyGeneratorObject(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()); 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(); 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(); 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(); 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(); genObj->setClosed(); return true; } bool js::intrinsic_IsTypedArray(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()); return true; } bool js::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 tarray(cx, &args[0].toObject().as()); if (!TypedArrayObject::ensureHasBuffer(cx, tarray)) return false; args.rval().set(TypedArrayObject::bufferValue(tarray)); return true; } bool js::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())); return true; } bool js::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().type()); MOZ_ASSERT(shift == 0 || shift == 1 || shift == 2 || shift == 3); args.rval().setInt32(mozilla::AssertedCast(shift)); return true; } // Return the value of [[ArrayLength]] internal slot of the TypedArray bool js::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()); args.rval().setInt32(obj->as().length()); return true; } bool js::intrinsic_MoveTypedArrayElements(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 4); Rooted tarray(cx, &args[0].toObject().as()); 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-neutered requirement is wrong"); if (tarray->hasBuffer() && tarray->buffer()->isNeutered()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); 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 uint8_t *data = static_cast(tarray->viewData()); mozilla::PodMove(&data[byteDest], &data[byteSrc], byteSize); args.rval().setUndefined(); return true; } bool CallSelfHostedNonGenericMethod(JSContext *cx, 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()); InvokeArgs args2(cx); if (!args2.init(args.length() - 1)) return false; args2.setCallee(selfHostedFun); args2.setThis(args.thisv()); for (size_t i = 0; i < args.length() - 1; i++) args2[i].set(args[i]); if (!Invoke(cx, args2)) return false; args.rval().set(args2.rval()); return true; } template bool Is(HandleValue v) { return v.isObject() && v.toObject().is(); } template static bool CallNonGenericSelfhostedMethod(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); return CallNonGenericMethod(cx, args); } static bool intrinsic_IsWeakSet(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()); return true; } /** * 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_ReportErrorNumber(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; } bool js::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()); MOZ_ASSERT(IsAnyTypedArray(&args[0].toObject())); RootedObject object(cx, &args[0].toObject()); JSProtoKey protoKey = StandardProtoKeyOrNull(object); MOZ_ASSERT(protoKey); RootedValue ctor(cx, cx->global()->getConstructor(protoKey)); MOZ_ASSERT(ctor.isObject()); args.rval().set(ctor); 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_FN("std_Array_join", array_join, 1,0), JS_FN("std_Array_push", array_push, 1,0), JS_FN("std_Array_pop", array_pop, 0,0), JS_FN("std_Array_shift", array_shift, 0,0), JS_FN("std_Array_unshift", array_unshift, 1,0), JS_FN("std_Array_slice", array_slice, 2,0), JS_FN("std_Array_sort", array_sort, 1,0), JS_FN("std_Date_now", date_now, 0,0), JS_FN("std_Date_valueOf", date_valueOf, 0,0), JS_FN("std_Function_bind", fun_bind, 1,0), JS_FN("std_Function_apply", fun_apply, 2,0), JS_FN("std_Math_floor", math_floor, 1,0), JS_FN("std_Math_max", math_max, 2,0), JS_FN("std_Math_min", math_min, 2,0), JS_FN("std_Math_abs", math_abs, 1,0), JS_FN("std_Math_imul", math_imul, 2,0), JS_FN("std_Math_log2", math_log2, 1,0), 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_FN("std_Object_create", obj_create, 2,0), JS_FN("std_Object_defineProperty", obj_defineProperty, 3,0), JS_FN("std_Object_getPrototypeOf", obj_getPrototypeOf, 1,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_Set_has", SetObject::has, 1,0), JS_FN("std_Set_iterator", SetObject::values, 0,0), JS_FN("std_String_fromCharCode", str_fromCharCode, 1,0), JS_FN("std_String_charCodeAt", str_charCodeAt, 1,0), JS_FN("std_String_indexOf", str_indexOf, 1,0), JS_FN("std_String_lastIndexOf", str_lastIndexOf, 1,0), JS_FN("std_String_match", str_match, 1,0), JS_FN("std_String_replace", str_replace, 2,0), JS_FN("std_String_split", str_split, 2,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_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_WeakMap_clear", WeakMap_clear, 0,0), // Helper funtions after this point. JS_FN("ToObject", intrinsic_ToObject, 1,0), JS_FN("IsObject", intrinsic_IsObject, 1,0), JS_FN("ToInteger", intrinsic_ToInteger, 1,0), JS_FN("ToString", intrinsic_ToString, 1,0), JS_FN("ToPropertyKey", intrinsic_ToPropertyKey, 1,0), JS_FN("IsCallable", intrinsic_IsCallable, 1,0), JS_FN("IsConstructor", intrinsic_IsConstructor, 1,0), JS_FN("OwnPropertyKeys", intrinsic_OwnPropertyKeys, 1,0), JS_FN("ThrowError", intrinsic_ThrowError, 4,0), JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1,0), JS_FN("MakeConstructible", intrinsic_MakeConstructible, 2,0), JS_FN("_IsConstructing", intrinsic_IsConstructing, 0,0), JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0), JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0), JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0), JS_FN("SubstringKernel", intrinsic_SubstringKernel, 3,0), JS_FN("UnsafePutElements", intrinsic_UnsafePutElements, 3,0), JS_FN("_DefineDataProperty", intrinsic_DefineDataProperty, 4,0), JS_FN("UnsafeSetReservedSlot", intrinsic_UnsafeSetReservedSlot, 3,0), JS_FN("UnsafeGetReservedSlot", intrinsic_UnsafeGetReservedSlot, 2,0), JS_FN("UnsafeGetObjectFromReservedSlot", intrinsic_UnsafeGetObjectFromReservedSlot, 2, 0), JS_FN("UnsafeGetInt32FromReservedSlot", intrinsic_UnsafeGetInt32FromReservedSlot, 2, 0), JS_FN("UnsafeGetStringFromReservedSlot", intrinsic_UnsafeGetStringFromReservedSlot, 2, 0), JS_FN("UnsafeGetBooleanFromReservedSlot", intrinsic_UnsafeGetBooleanFromReservedSlot, 2, 0), JS_FN("IsPackedArray", intrinsic_IsPackedArray, 1,0), JS_FN("GetIteratorPrototype", intrinsic_GetIteratorPrototype, 0,0), JS_FN("NewArrayIterator", intrinsic_NewArrayIterator, 0,0), JS_FN("IsArrayIterator", intrinsic_IsArrayIterator, 1,0), JS_FN("CallArrayIteratorMethodIfWrapped", CallNonGenericSelfhostedMethod>, 2,0), JS_FN("NewStringIterator", intrinsic_NewStringIterator, 0,0), JS_FN("IsStringIterator", intrinsic_IsStringIterator, 1,0), JS_FN("CallStringIteratorMethodIfWrapped", CallNonGenericSelfhostedMethod>, 2,0), JS_FN("IsStarGeneratorObject", intrinsic_IsStarGeneratorObject, 1,0), JS_FN("StarGeneratorObjectIsClosed", intrinsic_StarGeneratorObjectIsClosed, 1,0), JS_FN("IsSuspendedStarGenerator",intrinsic_IsSuspendedStarGenerator,1,0), JS_FN("IsLegacyGeneratorObject", intrinsic_IsLegacyGeneratorObject, 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("IsTypedArray", intrinsic_IsTypedArray, 1,0), JS_FN("TypedArrayBuffer", intrinsic_TypedArrayBuffer, 1,0), JS_FN("TypedArrayByteOffset", intrinsic_TypedArrayByteOffset, 1,0), JS_FN("TypedArrayElementShift", intrinsic_TypedArrayElementShift, 1,0), JS_FN("TypedArrayLength", intrinsic_TypedArrayLength, 1,0), JS_FN("MoveTypedArrayElements", intrinsic_MoveTypedArrayElements, 4,0), JS_FN("CallTypedArrayMethodIfWrapped", CallNonGenericSelfhostedMethod>, 2, 0), JS_FN("CallLegacyGeneratorMethodIfWrapped", CallNonGenericSelfhostedMethod>, 2, 0), JS_FN("CallStarGeneratorMethodIfWrapped", CallNonGenericSelfhostedMethod>, 2, 0), JS_FN("IsWeakSet", intrinsic_IsWeakSet, 1,0), JS_FN("NewDenseArray", intrinsic_NewDenseArray, 1,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("SetTypedObjectOffset", js::SetTypedObjectOffset, 2, 0), JS_FN("ObjectIsTypeDescr" , js::ObjectIsTypeDescr, 1, 0), JS_FN("ObjectIsTypedObject", js::ObjectIsTypedObject, 1, 0), JS_FN("ObjectIsTransparentTypedObject", js::ObjectIsTransparentTypedObject, 1, 0), JS_FN("TypedObjectIsAttached", js::TypedObjectIsAttached, 1, 0), JS_FN("TypedObjectTypeDescr", js::TypedObjectTypeDescr, 1, 0), JS_FN("ObjectIsOpaqueTypedObject", js::ObjectIsOpaqueTypedObject, 1, 0), JS_FN("TypeDescrIsArrayType", js::TypeDescrIsArrayType, 1, 0), JS_FN("TypeDescrIsSimpleType", js::TypeDescrIsSimpleType, 1, 0), JS_FN("ClampToUint8", js::ClampToUint8, 1, 0), JS_FN("GetTypedObjectModule", js::GetTypedObjectModule, 0, 0), JS_FN("GetFloat32x4TypeDescr", js::GetFloat32x4TypeDescr, 0, 0), JS_FN("GetFloat64x2TypeDescr", js::GetFloat64x2TypeDescr, 0, 0), JS_FN("GetInt32x4TypeDescr", js::GetInt32x4TypeDescr, 0, 0), #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##_type::Func, 3, 0), \ JS_FN("Load_" #_name, js::LoadReference##_type::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_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_FormatDateTime", intl_FormatDateTime, 2,0), JS_FN("intl_FormatNumber", intl_FormatNumber, 2,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), // 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_no_statics", regexp_construct_no_statics, 2,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.setDiscardSource(true); options.setZone(JS::FreshZone); JSCompartment *compartment = NewCompartment(cx, nullptr, nullptr, options); if (!compartment) return nullptr; static const Class shgClass = { "self-hosting-global", JSCLASS_GLOBAL_FLAGS, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook }; AutoCompartment ac(cx, compartment); Rooted shg(cx, GlobalObject::createInternal(cx, &shgClass)); if (!shg) return nullptr; cx->runtime()->selfHostingGlobal_ = shg; compartment->isSelfHosting = true; compartment->isSystem = true; if (!GlobalObject::initSelfHostingBuiltins(cx, shg, intrinsic_functions)) return nullptr; JS_FireOnNewGlobalObject(cx, shg); return shg; } 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 shg(cx, JSRuntime::createSelfHostingGlobal(cx)); if (!shg) return false; JSAutoCompartment ac(cx, shg); CompileOptions options(cx); FillSelfHostingCompileOptions(options); /* * 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. */ JSErrorReporter oldReporter = JS_SetErrorReporter(cx->runtime(), selfHosting_ErrorReporter); RootedValue rv(cx); bool ok = true; char *filename = getenv("MOZ_SELFHOSTEDJS"); if (filename) { RootedScript script(cx); if (Compile(cx, options, filename, &script)) ok = Execute(cx, script, *shg.get(), rv.address()); } else { uint32_t srcLen = GetRawScriptsSize(); const unsigned char *compressed = compressedSources; uint32_t compressedLen = GetCompressedSize(); ScopedJSFreePtr src(selfHostingGlobal_->zone()->pod_malloc(srcLen)); if (!src || !DecompressString(compressed, compressedLen, reinterpret_cast(src.get()), srcLen)) { ok = false; } ok = ok && Evaluate(cx, options, src, srcLen, &rv); } JS_SetErrorReporter(cx->runtime(), oldReporter); return ok; } void JSRuntime::finishSelfHosting() { selfHostingGlobal_ = nullptr; } void JSRuntime::markSelfHostingGlobal(JSTracer *trc) { if (selfHostingGlobal_ && !parentRuntime) MarkObjectRoot(trc, &selfHostingGlobal_, "self-hosting global"); } bool JSRuntime::isSelfHostingCompartment(JSCompartment *comp) { return selfHostingGlobal_->compartment() == comp; } bool JSRuntime::isSelfHostingZone(JS::Zone *zone) { 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()); 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); 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; } } for (Shape::Range range(selfHostedObject->lastProperty()); !range.empty(); range.popFront()) { Shape &shape = range.front(); if (shape.enumerable() && !ids.append(shape.propid())) 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, 0)) { 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(cx, selfHostedString->latin1Chars(nogc), len); else clone = NewStringCopyNDontDeflate(cx, selfHostedString->twoByteChars(nogc), len); if (clone) return clone; } AutoStableStringChars chars(cx); if (!chars.init(cx, selfHostedString)) return nullptr; return chars.isLatin1() ? NewStringCopyN(cx, chars.latin1Range().start().get(), len) : NewStringCopyNDontDeflate(cx, chars.twoByteRange().start().get(), len); } static JSObject * CloneObject(JSContext *cx, HandleNativeObject selfHostedObject) { AutoCycleDetector detect(cx, selfHostedObject); if (!detect.init()) return nullptr; if (detect.foundCycle()) { JS_ReportError(cx, "SelfHosted cloning cannot handle cyclic object graphs."); return nullptr; } RootedObject clone(cx); if (selfHostedObject->is()) { RootedFunction selfHostedFunction(cx, &selfHostedObject->as()); bool hasName = selfHostedFunction->atom() != nullptr; // Arrow functions use the first extended slot for their lexical |this| value. MOZ_ASSERT(!selfHostedFunction->isArrow()); js::gc::AllocKind kind = hasName ? JSFunction::ExtendedFinalizeKind : selfHostedFunction->getAllocKind(); clone = CloneFunctionObject(cx, selfHostedFunction, cx->global(), kind, TenuredObject); // 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().setExtendedSlot(0, StringValue(selfHostedFunction->atom())); } else if (selfHostedObject->is()) { RegExpObject &reobj = selfHostedObject->as(); RootedAtom source(cx, reobj.getSource()); MOZ_ASSERT(source->isPermanentAtom()); clone = RegExpObject::createNoStatics(cx, source, reobj.getFlags(), nullptr, cx->tempLifoAlloc()); } else if (selfHostedObject->is()) { clone = JS_NewDateObjectMsec(cx, selfHostedObject->as().UTCTime().toNumber()); } else if (selfHostedObject->is()) { clone = BooleanObject::create(cx, selfHostedObject->as().unbox()); } else if (selfHostedObject->is()) { clone = NumberObject::create(cx, selfHostedObject->as().unbox()); } else if (selfHostedObject->is()) { JSString *selfHostedString = selfHostedObject->as().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()) { 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()); 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 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::cloneSelfHostedFunctionScript(JSContext *cx, HandlePropertyName name, HandleFunction targetFun) { RootedId id(cx, NameToId(name)); RootedValue funVal(cx); if (!GetUnclonedValue(cx, HandleNativeObject::fromMarkedLocation(&selfHostingGlobal_), id, &funVal)) return false; RootedFunction sourceFun(cx, &funVal.toObject().as()); // JSFunction::generatorKind can't handle lazy self-hosted functions, so we make sure there // aren't any. MOZ_ASSERT(!sourceFun->isGenerator()); RootedScript sourceScript(cx, sourceFun->getOrCreateScript(cx)); if (!sourceScript) return false; MOZ_ASSERT(!sourceScript->enclosingStaticScope()); JSScript *cscript = CloneScript(cx, NullPtr(), targetFun, sourceScript); if (!cscript) return false; cscript->setFunction(targetFun); MOZ_ASSERT(sourceFun->nargs() == targetFun->nargs()); // The target function might have been relazified after it's flags changed. targetFun->setFlags((targetFun->flags() & ~JSFunction::INTERPRETED_LAZY) | sourceFun->flags() | JSFunction::EXTENDED); targetFun->setScript(cscript); MOZ_ASSERT(targetFun->isExtended()); return true; } bool JSRuntime::cloneSelfHostedValue(JSContext *cx, HandlePropertyName name, MutableHandleValue vp) { RootedId id(cx, NameToId(name)); RootedValue selfHostedValue(cx); if (!GetUnclonedValue(cx, HandleNativeObject::fromMarkedLocation(&selfHostingGlobal_), id, &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); } 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()); return &func.toObject().as(); } bool js::IsSelfHostedFunctionWithName(JSFunction *fun, JSAtom *name) { return fun->isSelfHostedBuiltin() && fun->getExtendedSlot(0).toString() == name; } static_assert(JSString::MAX_LENGTH <= INT32_MAX, "StringIteratorNext in builtin/String.js assumes the stored index " "into the string is an Int32Value");