michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "vm/SelfHosting.h" michael@0: michael@0: #include "jscntxt.h" michael@0: #include "jscompartment.h" michael@0: #include "jsfriendapi.h" michael@0: #include "jshashutil.h" michael@0: #include "jsobj.h" michael@0: #include "jswrapper.h" michael@0: #include "selfhosted.out.h" michael@0: michael@0: #include "builtin/Intl.h" michael@0: #include "builtin/SelfHostingDefines.h" michael@0: #include "builtin/TypedObject.h" michael@0: #include "gc/Marking.h" michael@0: #include "vm/Compression.h" michael@0: #include "vm/ForkJoin.h" michael@0: #include "vm/Interpreter.h" michael@0: #include "vm/String.h" michael@0: michael@0: #include "jsfuninlines.h" michael@0: #include "jsscriptinlines.h" michael@0: michael@0: #include "vm/BooleanObject-inl.h" michael@0: #include "vm/NumberObject-inl.h" michael@0: #include "vm/StringObject-inl.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::selfhosted; michael@0: michael@0: static void michael@0: selfHosting_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) michael@0: { michael@0: PrintError(cx, stderr, message, report, true); michael@0: } michael@0: michael@0: static const JSClass self_hosting_global_class = { michael@0: "self-hosting-global", JSCLASS_GLOBAL_FLAGS, michael@0: JS_PropertyStub, JS_DeletePropertyStub, michael@0: JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, michael@0: JS_ConvertStub, nullptr, michael@0: nullptr, nullptr, nullptr, michael@0: JS_GlobalObjectTraceHook michael@0: }; michael@0: michael@0: bool michael@0: js::intrinsic_ToObject(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: RootedValue val(cx, args[0]); michael@0: RootedObject obj(cx, ToObject(cx, val)); michael@0: if (!obj) michael@0: return false; michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: intrinsic_ToInteger(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: double result; michael@0: if (!ToInteger(cx, args[0], &result)) michael@0: return false; michael@0: args.rval().setDouble(result); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: js::intrinsic_IsCallable(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: Value val = args[0]; michael@0: bool isCallable = val.isObject() && val.toObject().isCallable(); michael@0: args.rval().setBoolean(isCallable); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: js::intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() >= 1); michael@0: uint32_t errorNumber = args[0].toInt32(); michael@0: michael@0: #ifdef DEBUG michael@0: const JSErrorFormatString *efs = michael@0: js_GetLocalizedErrorMessage(cx, nullptr, nullptr, errorNumber); michael@0: JS_ASSERT(efs->argCount == args.length() - 1); michael@0: #endif michael@0: michael@0: JSAutoByteString errorArgs[3]; michael@0: for (unsigned i = 1; i < 4 && i < args.length(); i++) { michael@0: RootedValue val(cx, args[i]); michael@0: if (val.isInt32()) { michael@0: JSString *str = ToString(cx, val); michael@0: if (!str) michael@0: return false; michael@0: errorArgs[i - 1].encodeLatin1(cx, str); michael@0: } else if (val.isString()) { michael@0: errorArgs[i - 1].encodeLatin1(cx, val.toString()); michael@0: } else { michael@0: errorArgs[i - 1].initBytes(DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, NullPtr())); michael@0: } michael@0: if (!errorArgs[i - 1]) michael@0: return false; michael@0: } michael@0: michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, errorNumber, michael@0: errorArgs[0].ptr(), errorArgs[1].ptr(), errorArgs[2].ptr()); michael@0: return false; michael@0: } michael@0: michael@0: /** michael@0: * Handles an assertion failure in self-hosted code just like an assertion michael@0: * failure in C++ code. Information about the failure can be provided in args[0]. michael@0: */ michael@0: static bool michael@0: intrinsic_AssertionFailed(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: #ifdef DEBUG michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() > 0) { michael@0: // try to dump the informative string michael@0: JSString *str = ToString(cx, args[0]); michael@0: if (str) { michael@0: const jschar *chars = str->getChars(cx); michael@0: if (chars) { michael@0: fprintf(stderr, "Self-hosted JavaScript assertion info: "); michael@0: JSString::dumpChars(chars, str->length()); michael@0: fputc('\n', stderr); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: JS_ASSERT(false); michael@0: return false; michael@0: } michael@0: michael@0: static bool michael@0: intrinsic_MakeConstructible(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 2); michael@0: JS_ASSERT(args[0].isObject()); michael@0: JS_ASSERT(args[0].toObject().is()); michael@0: JS_ASSERT(args[1].isObject()); michael@0: michael@0: // Normal .prototype properties aren't enumerable. But for this to clone michael@0: // correctly, it must be enumerable. michael@0: RootedObject ctor(cx, &args[0].toObject()); michael@0: if (!JSObject::defineProperty(cx, ctor, cx->names().prototype, args[1], michael@0: JS_PropertyStub, JS_StrictPropertyStub, michael@0: JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: ctor->as().setIsSelfHostedConstructor(); michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * Used to decompile values in the nearest non-builtin stack frame, falling michael@0: * back to decompiling in the current frame. Helpful for printing higher-order michael@0: * function arguments. michael@0: * michael@0: * The user must supply the argument number of the value in question; it michael@0: * _cannot_ be automatically determined. michael@0: */ michael@0: static bool michael@0: intrinsic_DecompileArg(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 2); michael@0: michael@0: RootedValue value(cx, args[1]); michael@0: ScopedJSFreePtr str(DecompileArgument(cx, args[0].toInt32(), value)); michael@0: if (!str) michael@0: return false; michael@0: RootedAtom atom(cx, Atomize(cx, str, strlen(str))); michael@0: if (!atom) michael@0: return false; michael@0: args.rval().setString(atom); michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * SetScriptHints(fun, flags): Sets various internal hints to the ion michael@0: * compiler for use when compiling |fun| or calls to |fun|. Flags michael@0: * should be a dictionary object. michael@0: * michael@0: * The function |fun| should be a self-hosted function (in particular, michael@0: * it *must* be a JS function). michael@0: * michael@0: * Possible flags: michael@0: * - |cloneAtCallsite: true| will hint that |fun| should be cloned michael@0: * each callsite to improve TI resolution. This is important for michael@0: * higher-order functions like |Array.map|. michael@0: * - |inline: true| will hint that |fun| be inlined regardless of michael@0: * JIT heuristics. michael@0: */ michael@0: static bool michael@0: intrinsic_SetScriptHints(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() >= 2); michael@0: JS_ASSERT(args[0].isObject() && args[0].toObject().is()); michael@0: JS_ASSERT(args[1].isObject()); michael@0: michael@0: RootedFunction fun(cx, &args[0].toObject().as()); michael@0: RootedScript funScript(cx, fun->getOrCreateScript(cx)); michael@0: if (!funScript) michael@0: return false; michael@0: RootedObject flags(cx, &args[1].toObject()); michael@0: michael@0: RootedId id(cx); michael@0: RootedValue propv(cx); michael@0: michael@0: id = AtomToId(Atomize(cx, "cloneAtCallsite", strlen("cloneAtCallsite"))); michael@0: if (!JSObject::getGeneric(cx, flags, flags, id, &propv)) michael@0: return false; michael@0: if (ToBoolean(propv)) michael@0: funScript->setShouldCloneAtCallsite(); michael@0: michael@0: id = AtomToId(Atomize(cx, "inline", strlen("inline"))); michael@0: if (!JSObject::getGeneric(cx, flags, flags, id, &propv)) michael@0: return false; michael@0: if (ToBoolean(propv)) michael@0: funScript->setShouldInline(); michael@0: michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: /* michael@0: * Dump(val): Dumps a value for debugging, even in parallel mode. michael@0: */ michael@0: bool michael@0: intrinsic_Dump(ThreadSafeContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: js_DumpValue(args[0]); michael@0: if (args[0].isObject()) { michael@0: fprintf(stderr, "\n"); michael@0: js_DumpObject(&args[0].toObject()); michael@0: } michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(intrinsic_Dump_jitInfo, intrinsic_Dump_jitInfo, michael@0: intrinsic_Dump); michael@0: michael@0: bool michael@0: intrinsic_ParallelSpew(ThreadSafeContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 1); michael@0: JS_ASSERT(args[0].isString()); michael@0: michael@0: ScopedThreadSafeStringInspector inspector(args[0].toString()); michael@0: if (!inspector.ensureChars(cx)) michael@0: return false; michael@0: michael@0: ScopedJSFreePtr bytes(TwoByteCharsToNewUTF8CharsZ(cx, inspector.range()).c_str()); michael@0: parallel::Spew(parallel::SpewOps, bytes); michael@0: michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(intrinsic_ParallelSpew_jitInfo, intrinsic_ParallelSpew_jitInfo, michael@0: intrinsic_ParallelSpew); michael@0: #endif michael@0: michael@0: /* michael@0: * ForkJoin(func, feedback): Invokes |func| many times in parallel. michael@0: * michael@0: * See ForkJoin.cpp for details and ParallelArray.js for examples. michael@0: */ michael@0: static bool michael@0: intrinsic_ForkJoin(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return ForkJoin(cx, args); michael@0: } michael@0: michael@0: /* michael@0: * ForkJoinWorkerNumWorkers(): Returns the number of workers in the fork join michael@0: * thread pool, including the main thread. michael@0: */ michael@0: static bool michael@0: intrinsic_ForkJoinNumWorkers(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: args.rval().setInt32(cx->runtime()->threadPool.numWorkers()); michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * ForkJoinGetSlice(id): Returns the id of the next slice to be worked michael@0: * on. michael@0: * michael@0: * Acts as the identity function when called from outside of a ForkJoin michael@0: * thread. This odd API is because intrinsics must be called during the michael@0: * parallel warm up phase to populate observed type sets, so we must call it michael@0: * even during sequential execution. But since there is no thread pool during michael@0: * sequential execution, the selfhosted code is responsible for computing the michael@0: * next sequential slice id and passing it in itself. michael@0: */ michael@0: bool michael@0: js::intrinsic_ForkJoinGetSlice(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: MOZ_ASSERT(args.length() == 1); michael@0: MOZ_ASSERT(args[0].isInt32()); michael@0: args.rval().set(args[0]); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: intrinsic_ForkJoinGetSlicePar(ForkJoinContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: MOZ_ASSERT(args.length() == 1); michael@0: MOZ_ASSERT(args[0].isInt32()); michael@0: michael@0: uint16_t sliceId; michael@0: if (cx->getSlice(&sliceId)) michael@0: args.rval().setInt32(sliceId); michael@0: else michael@0: args.rval().setInt32(ThreadPool::MAX_SLICE_ID); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: JS_JITINFO_NATIVE_PARALLEL(intrinsic_ForkJoinGetSlice_jitInfo, michael@0: intrinsic_ForkJoinGetSlicePar); michael@0: michael@0: /* michael@0: * NewDenseArray(length): Allocates and returns a new dense array with michael@0: * the given length where all values are initialized to holes. michael@0: */ michael@0: bool michael@0: js::intrinsic_NewDenseArray(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: // Check that index is an int32 michael@0: if (!args[0].isInt32()) { michael@0: JS_ReportError(cx, "Expected int32 as second argument"); michael@0: return false; michael@0: } michael@0: uint32_t length = args[0].toInt32(); michael@0: michael@0: // Make a new buffer and initialize it up to length. michael@0: RootedObject buffer(cx, NewDenseAllocatedArray(cx, length)); michael@0: if (!buffer) michael@0: return false; michael@0: michael@0: types::TypeObject *newtype = types::GetTypeCallerInitObject(cx, JSProto_Array); michael@0: if (!newtype) michael@0: return false; michael@0: buffer->setType(newtype); michael@0: michael@0: JSObject::EnsureDenseResult edr = buffer->ensureDenseElements(cx, length, 0); michael@0: switch (edr) { michael@0: case JSObject::ED_OK: michael@0: args.rval().setObject(*buffer); michael@0: return true; michael@0: michael@0: case JSObject::ED_SPARSE: // shouldn't happen! michael@0: JS_ASSERT(!"%EnsureDenseArrayElements() would yield sparse array"); michael@0: JS_ReportError(cx, "%EnsureDenseArrayElements() would yield sparse array"); michael@0: break; michael@0: michael@0: case JSObject::ED_FAILED: michael@0: break; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: /* michael@0: * UnsafePutElements(arr0, idx0, elem0, ..., arrN, idxN, elemN): For each set of michael@0: * (arr, idx, elem) arguments that are passed, performs the assignment michael@0: * |arr[idx] = elem|. |arr| must be either a dense array or a typed array. michael@0: * michael@0: * If |arr| is a dense array, the index must be an int32 less than the michael@0: * initialized length of |arr|. Use |%EnsureDenseResultArrayElements| michael@0: * to ensure that the initialized length is long enough. michael@0: * michael@0: * If |arr| is a typed array, the index must be an int32 less than the michael@0: * length of |arr|. michael@0: */ michael@0: bool michael@0: js::intrinsic_UnsafePutElements(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if ((args.length() % 3) != 0) { michael@0: JS_ReportError(cx, "Incorrect number of arguments, not divisible by 3"); michael@0: return false; michael@0: } michael@0: michael@0: for (uint32_t base = 0; base < args.length(); base += 3) { michael@0: uint32_t arri = base; michael@0: uint32_t idxi = base+1; michael@0: uint32_t elemi = base+2; michael@0: michael@0: JS_ASSERT(args[arri].isObject()); michael@0: JS_ASSERT(args[arri].toObject().isNative() || IsTypedObjectArray(args[arri].toObject())); michael@0: JS_ASSERT(args[idxi].isInt32()); michael@0: michael@0: RootedObject arrobj(cx, &args[arri].toObject()); michael@0: uint32_t idx = args[idxi].toInt32(); michael@0: michael@0: if (arrobj->is() || arrobj->is()) { michael@0: JS_ASSERT(!arrobj->is() || idx < arrobj->as().length()); michael@0: JS_ASSERT(!arrobj->is() || idx < uint32_t(arrobj->as().length())); michael@0: RootedValue tmp(cx, args[elemi]); michael@0: // XXX: Always non-strict. michael@0: if (!JSObject::setElement(cx, arrobj, arrobj, idx, &tmp, false)) michael@0: return false; michael@0: } else { michael@0: JS_ASSERT(idx < arrobj->getDenseInitializedLength()); michael@0: arrobj->setDenseElementWithType(cx, idx, args[elemi]); michael@0: } michael@0: } michael@0: michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: js::intrinsic_DefineValueProperty(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: MOZ_ASSERT(args.length() == 4); michael@0: MOZ_ASSERT(args[0].isObject()); michael@0: MOZ_ASSERT(args[3].isInt32()); michael@0: michael@0: RootedObject obj(cx, &args[0].toObject()); michael@0: if (obj->is()) { michael@0: JS_ReportError(cx, "_DefineValueProperty can't be used on proxies"); michael@0: return false; michael@0: } michael@0: RootedId id(cx); michael@0: if (!ValueToId(cx, args[1], &id)) michael@0: return false; michael@0: RootedValue value(cx, args[2]); michael@0: unsigned attributes = args[3].toInt32(); michael@0: michael@0: unsigned resolvedAttributes = JSPROP_PERMANENT | JSPROP_READONLY; michael@0: michael@0: MOZ_ASSERT(bool(attributes & ATTR_ENUMERABLE) != bool(attributes & ATTR_NONENUMERABLE), michael@0: "_DefineValueProperty must receive either ATTR_ENUMERABLE xor ATTR_NONENUMERABLE"); michael@0: if (attributes & ATTR_ENUMERABLE) michael@0: resolvedAttributes |= JSPROP_ENUMERATE; michael@0: michael@0: MOZ_ASSERT(bool(attributes & ATTR_CONFIGURABLE) != bool(attributes & ATTR_NONCONFIGURABLE), michael@0: "_DefineValueProperty must receive either ATTR_CONFIGURABLE xor " michael@0: "ATTR_NONCONFIGURABLE"); michael@0: if (attributes & ATTR_CONFIGURABLE) michael@0: resolvedAttributes &= ~JSPROP_PERMANENT; michael@0: michael@0: MOZ_ASSERT(bool(attributes & ATTR_WRITABLE) != bool(attributes & ATTR_NONWRITABLE), michael@0: "_DefineValueProperty must receive either ATTR_WRITABLE xor ATTR_NONWRITABLE"); michael@0: if (attributes & ATTR_WRITABLE) michael@0: resolvedAttributes &= ~JSPROP_READONLY; michael@0: michael@0: return JSObject::defineGeneric(cx, obj, id, value, JS_PropertyStub, JS_StrictPropertyStub, michael@0: resolvedAttributes); michael@0: } michael@0: michael@0: bool michael@0: js::intrinsic_UnsafeSetReservedSlot(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 3); michael@0: JS_ASSERT(args[0].isObject()); michael@0: JS_ASSERT(args[1].isInt32()); michael@0: michael@0: args[0].toObject().setReservedSlot(args[1].toPrivateUint32(), args[2]); michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: js::intrinsic_UnsafeGetReservedSlot(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 2); michael@0: JS_ASSERT(args[0].isObject()); michael@0: JS_ASSERT(args[1].isInt32()); michael@0: michael@0: args.rval().set(args[0].toObject().getReservedSlot(args[1].toPrivateUint32())); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: js::intrinsic_HaveSameClass(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 2); michael@0: JS_ASSERT(args[0].isObject()); michael@0: JS_ASSERT(args[1].isObject()); michael@0: michael@0: args.rval().setBoolean(args[0].toObject().getClass() == args[1].toObject().getClass()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: js::intrinsic_IsPackedArray(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 1); michael@0: JS_ASSERT(args[0].isObject()); michael@0: michael@0: JSObject *obj = &args[0].toObject(); michael@0: bool isPacked = obj->is() && !obj->hasLazyType() && michael@0: !obj->type()->hasAllFlags(types::OBJECT_FLAG_NON_PACKED) && michael@0: obj->getDenseInitializedLength() == obj->as().length(); michael@0: michael@0: args.rval().setBoolean(isPacked); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: intrinsic_GetIteratorPrototype(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 0); michael@0: michael@0: JSObject *obj = GlobalObject::getOrCreateIteratorPrototype(cx, cx->global()); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: intrinsic_NewArrayIterator(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 0); michael@0: michael@0: RootedObject proto(cx, GlobalObject::getOrCreateArrayIteratorPrototype(cx, cx->global())); michael@0: if (!proto) michael@0: return false; michael@0: michael@0: JSObject *obj = NewObjectWithGivenProto(cx, proto->getClass(), proto, cx->global()); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: intrinsic_IsArrayIterator(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 1); michael@0: JS_ASSERT(args[0].isObject()); michael@0: michael@0: args.rval().setBoolean(args[0].toObject().is()); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: intrinsic_NewStringIterator(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 0); michael@0: michael@0: RootedObject proto(cx, GlobalObject::getOrCreateStringIteratorPrototype(cx, cx->global())); michael@0: if (!proto) michael@0: return false; michael@0: michael@0: JSObject *obj = NewObjectWithGivenProto(cx, &StringIteratorObject::class_, proto, cx->global()); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: intrinsic_IsStringIterator(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 1); michael@0: JS_ASSERT(args[0].isObject()); michael@0: michael@0: args.rval().setBoolean(args[0].toObject().is()); michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * ParallelTestsShouldPass(): Returns false if we are running in a michael@0: * mode (such as --ion-eager) that is known to cause additional michael@0: * bailouts or disqualifications for parallel array tests. michael@0: * michael@0: * This is needed because the parallel tests generally assert that, michael@0: * under normal conditions, they will run without bailouts or michael@0: * compilation failures, but this does not hold under "stress-testing" michael@0: * conditions like --ion-eager or --no-ti. However, running the tests michael@0: * under those conditions HAS exposed bugs and thus we do not wish to michael@0: * disable them entirely. Instead, we simply disable the assertions michael@0: * that state that no bailouts etc should occur. michael@0: */ michael@0: static bool michael@0: intrinsic_ParallelTestsShouldPass(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: args.rval().setBoolean(ParallelTestsShouldPass(cx)); michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * ShouldForceSequential(): Returns true if parallel ops should take michael@0: * the sequential fallback path. michael@0: */ michael@0: bool michael@0: js::intrinsic_ShouldForceSequential(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: #ifdef JS_THREADSAFE michael@0: args.rval().setBoolean(cx->runtime()->forkJoinWarmup || michael@0: InParallelSection()); michael@0: #else michael@0: args.rval().setBoolean(true); michael@0: #endif michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: js::intrinsic_InParallelSection(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: args.rval().setBoolean(false); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: intrinsic_InParallelSectionPar(ForkJoinContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: args.rval().setBoolean(true); michael@0: return true; michael@0: } michael@0: michael@0: JS_JITINFO_NATIVE_PARALLEL(intrinsic_InParallelSection_jitInfo, michael@0: intrinsic_InParallelSectionPar); michael@0: michael@0: /* These wrappers are needed in order to recognize the function michael@0: * pointers within the JIT, and the raw js:: functions can't be used michael@0: * directly because they take a ThreadSafeContext* argument. michael@0: */ michael@0: bool michael@0: js::intrinsic_ObjectIsTypedObject(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: return js::ObjectIsTypedObject(cx, argc, vp); michael@0: } michael@0: michael@0: bool michael@0: js::intrinsic_ObjectIsTransparentTypedObject(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: return js::ObjectIsTransparentTypedObject(cx, argc, vp); michael@0: } michael@0: michael@0: bool michael@0: js::intrinsic_ObjectIsOpaqueTypedObject(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: return js::ObjectIsOpaqueTypedObject(cx, argc, vp); michael@0: } michael@0: michael@0: bool michael@0: js::intrinsic_ObjectIsTypeDescr(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: return js::ObjectIsTypeDescr(cx, argc, vp); michael@0: } michael@0: michael@0: bool michael@0: js::intrinsic_TypeDescrIsSimpleType(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: return js::TypeDescrIsSimpleType(cx, argc, vp); michael@0: } michael@0: michael@0: bool michael@0: js::intrinsic_TypeDescrIsArrayType(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: return js::TypeDescrIsArrayType(cx, argc, vp); michael@0: } michael@0: michael@0: bool michael@0: js::intrinsic_TypeDescrIsUnsizedArrayType(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: return js::TypeDescrIsUnsizedArrayType(cx, argc, vp); michael@0: } michael@0: michael@0: bool michael@0: js::intrinsic_TypeDescrIsSizedArrayType(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: return js::TypeDescrIsSizedArrayType(cx, argc, vp); michael@0: } michael@0: michael@0: /** michael@0: * Returns the default locale as a well-formed, but not necessarily canonicalized, michael@0: * BCP-47 language tag. michael@0: */ michael@0: static bool michael@0: intrinsic_RuntimeDefaultLocale(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: const char *locale = cx->runtime()->getDefaultLocale(); michael@0: if (!locale) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEFAULT_LOCALE_ERROR); michael@0: return false; michael@0: } michael@0: michael@0: RootedString jslocale(cx, JS_NewStringCopyZ(cx, locale)); michael@0: if (!jslocale) michael@0: return false; michael@0: michael@0: args.rval().setString(jslocale); michael@0: return true; michael@0: } michael@0: michael@0: static const JSFunctionSpec intrinsic_functions[] = { michael@0: JS_FN("ToObject", intrinsic_ToObject, 1,0), michael@0: JS_FN("ToInteger", intrinsic_ToInteger, 1,0), michael@0: JS_FN("IsCallable", intrinsic_IsCallable, 1,0), michael@0: JS_FN("ThrowError", intrinsic_ThrowError, 4,0), michael@0: JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1,0), michael@0: JS_FN("SetScriptHints", intrinsic_SetScriptHints, 2,0), michael@0: JS_FN("MakeConstructible", intrinsic_MakeConstructible, 1,0), michael@0: JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0), michael@0: JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0), michael@0: michael@0: JS_FN("UnsafePutElements", intrinsic_UnsafePutElements, 3,0), michael@0: JS_FN("_DefineValueProperty", intrinsic_DefineValueProperty, 4,0), michael@0: JS_FN("UnsafeSetReservedSlot", intrinsic_UnsafeSetReservedSlot, 3,0), michael@0: JS_FN("UnsafeGetReservedSlot", intrinsic_UnsafeGetReservedSlot, 2,0), michael@0: JS_FN("HaveSameClass", intrinsic_HaveSameClass, 2,0), michael@0: JS_FN("IsPackedArray", intrinsic_IsPackedArray, 1,0), michael@0: michael@0: JS_FN("GetIteratorPrototype", intrinsic_GetIteratorPrototype, 0,0), michael@0: michael@0: JS_FN("NewArrayIterator", intrinsic_NewArrayIterator, 0,0), michael@0: JS_FN("IsArrayIterator", intrinsic_IsArrayIterator, 1,0), michael@0: michael@0: JS_FN("NewStringIterator", intrinsic_NewStringIterator, 0,0), michael@0: JS_FN("IsStringIterator", intrinsic_IsStringIterator, 1,0), michael@0: michael@0: JS_FN("ForkJoin", intrinsic_ForkJoin, 2,0), michael@0: JS_FN("ForkJoinNumWorkers", intrinsic_ForkJoinNumWorkers, 0,0), michael@0: JS_FN("NewDenseArray", intrinsic_NewDenseArray, 1,0), michael@0: JS_FN("ShouldForceSequential", intrinsic_ShouldForceSequential, 0,0), michael@0: JS_FN("ParallelTestsShouldPass", intrinsic_ParallelTestsShouldPass, 0,0), michael@0: JS_FNINFO("ClearThreadLocalArenas", michael@0: intrinsic_ClearThreadLocalArenas, michael@0: &intrinsic_ClearThreadLocalArenasInfo, 0,0), michael@0: JS_FNINFO("SetForkJoinTargetRegion", michael@0: intrinsic_SetForkJoinTargetRegion, michael@0: &intrinsic_SetForkJoinTargetRegionInfo, 2, 0), michael@0: JS_FNINFO("ForkJoinGetSlice", michael@0: intrinsic_ForkJoinGetSlice, michael@0: &intrinsic_ForkJoinGetSlice_jitInfo, 1, 0), michael@0: JS_FNINFO("InParallelSection", michael@0: intrinsic_InParallelSection, michael@0: &intrinsic_InParallelSection_jitInfo, 0, 0), michael@0: michael@0: // See builtin/TypedObject.h for descriptors of the typedobj functions. michael@0: JS_FN("NewOpaqueTypedObject", michael@0: js::NewOpaqueTypedObject, michael@0: 1, 0), michael@0: JS_FN("NewDerivedTypedObject", michael@0: js::NewDerivedTypedObject, michael@0: 3, 0), michael@0: JS_FNINFO("AttachTypedObject", michael@0: JSNativeThreadSafeWrapper, michael@0: &js::AttachTypedObjectJitInfo, 3, 0), michael@0: JS_FNINFO("SetTypedObjectOffset", michael@0: intrinsic_SetTypedObjectOffset, michael@0: &js::intrinsic_SetTypedObjectOffsetJitInfo, 2, 0), michael@0: JS_FNINFO("ObjectIsTypeDescr", michael@0: intrinsic_ObjectIsTypeDescr, michael@0: &js::ObjectIsTypeDescrJitInfo, 1, 0), michael@0: JS_FNINFO("ObjectIsTypedObject", michael@0: intrinsic_ObjectIsTypedObject, michael@0: &js::ObjectIsTypedObjectJitInfo, 1, 0), michael@0: JS_FNINFO("ObjectIsTransparentTypedObject", michael@0: intrinsic_ObjectIsTransparentTypedObject, michael@0: &js::ObjectIsTransparentTypedObjectJitInfo, 1, 0), michael@0: JS_FNINFO("TypedObjectIsAttached", michael@0: JSNativeThreadSafeWrapper, michael@0: &js::TypedObjectIsAttachedJitInfo, 1, 0), michael@0: JS_FNINFO("ObjectIsOpaqueTypedObject", michael@0: intrinsic_ObjectIsOpaqueTypedObject, michael@0: &js::ObjectIsOpaqueTypedObjectJitInfo, 1, 0), michael@0: JS_FNINFO("TypeDescrIsArrayType", michael@0: intrinsic_TypeDescrIsArrayType, michael@0: &js::TypeDescrIsArrayTypeJitInfo, 1, 0), michael@0: JS_FNINFO("TypeDescrIsUnsizedArrayType", michael@0: intrinsic_TypeDescrIsUnsizedArrayType, michael@0: &js::TypeDescrIsUnsizedArrayTypeJitInfo, 1, 0), michael@0: JS_FNINFO("TypeDescrIsSizedArrayType", michael@0: intrinsic_TypeDescrIsSizedArrayType, michael@0: &js::TypeDescrIsSizedArrayTypeJitInfo, 1, 0), michael@0: JS_FNINFO("TypeDescrIsSimpleType", michael@0: intrinsic_TypeDescrIsSimpleType, michael@0: &js::TypeDescrIsSimpleTypeJitInfo, 1, 0), michael@0: JS_FNINFO("ClampToUint8", michael@0: JSNativeThreadSafeWrapper, michael@0: &js::ClampToUint8JitInfo, 1, 0), michael@0: JS_FNINFO("Memcpy", michael@0: JSNativeThreadSafeWrapper, michael@0: &js::MemcpyJitInfo, 5, 0), michael@0: JS_FN("GetTypedObjectModule", js::GetTypedObjectModule, 0, 0), michael@0: JS_FN("GetFloat32x4TypeDescr", js::GetFloat32x4TypeDescr, 0, 0), michael@0: JS_FN("GetInt32x4TypeDescr", js::GetInt32x4TypeDescr, 0, 0), michael@0: michael@0: #define LOAD_AND_STORE_SCALAR_FN_DECLS(_constant, _type, _name) \ michael@0: JS_FNINFO("Store_" #_name, \ michael@0: JSNativeThreadSafeWrapper, \ michael@0: &js::StoreScalar##_type::JitInfo, 3, 0), \ michael@0: JS_FNINFO("Load_" #_name, \ michael@0: JSNativeThreadSafeWrapper, \ michael@0: &js::LoadScalar##_type::JitInfo, 3, 0), michael@0: JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(LOAD_AND_STORE_SCALAR_FN_DECLS) michael@0: michael@0: #define LOAD_AND_STORE_REFERENCE_FN_DECLS(_constant, _type, _name) \ michael@0: JS_FNINFO("Store_" #_name, \ michael@0: JSNativeThreadSafeWrapper, \ michael@0: &js::StoreReference##_type::JitInfo, 3, 0), \ michael@0: JS_FNINFO("Load_" #_name, \ michael@0: JSNativeThreadSafeWrapper, \ michael@0: &js::LoadReference##_type::JitInfo, 3, 0), michael@0: JS_FOR_EACH_REFERENCE_TYPE_REPR(LOAD_AND_STORE_REFERENCE_FN_DECLS) michael@0: michael@0: // See builtin/Intl.h for descriptions of the intl_* functions. michael@0: JS_FN("intl_availableCalendars", intl_availableCalendars, 1,0), michael@0: JS_FN("intl_availableCollations", intl_availableCollations, 1,0), michael@0: JS_FN("intl_Collator", intl_Collator, 2,0), michael@0: JS_FN("intl_Collator_availableLocales", intl_Collator_availableLocales, 0,0), michael@0: JS_FN("intl_CompareStrings", intl_CompareStrings, 3,0), michael@0: JS_FN("intl_DateTimeFormat", intl_DateTimeFormat, 2,0), michael@0: JS_FN("intl_DateTimeFormat_availableLocales", intl_DateTimeFormat_availableLocales, 0,0), michael@0: JS_FN("intl_FormatDateTime", intl_FormatDateTime, 2,0), michael@0: JS_FN("intl_FormatNumber", intl_FormatNumber, 2,0), michael@0: JS_FN("intl_NumberFormat", intl_NumberFormat, 2,0), michael@0: JS_FN("intl_NumberFormat_availableLocales", intl_NumberFormat_availableLocales, 0,0), michael@0: JS_FN("intl_numberingSystem", intl_numberingSystem, 1,0), michael@0: JS_FN("intl_patternForSkeleton", intl_patternForSkeleton, 2,0), michael@0: michael@0: // See builtin/RegExp.h for descriptions of the regexp_* functions. michael@0: JS_FN("regexp_exec_no_statics", regexp_exec_no_statics, 2,0), michael@0: JS_FN("regexp_test_no_statics", regexp_test_no_statics, 2,0), michael@0: michael@0: #ifdef DEBUG michael@0: JS_FNINFO("Dump", michael@0: JSNativeThreadSafeWrapper, michael@0: &intrinsic_Dump_jitInfo, 1,0), michael@0: michael@0: JS_FNINFO("ParallelSpew", michael@0: JSNativeThreadSafeWrapper, michael@0: &intrinsic_ParallelSpew_jitInfo, 1,0), michael@0: #endif michael@0: michael@0: JS_FS_END michael@0: }; michael@0: michael@0: void michael@0: js::FillSelfHostingCompileOptions(CompileOptions &options) michael@0: { michael@0: /* michael@0: * In self-hosting mode, scripts emit JSOP_GETINTRINSIC instead of michael@0: * JSOP_NAME or JSOP_GNAME to access unbound variables. JSOP_GETINTRINSIC michael@0: * does a name lookup in a special object, whose properties are filled in michael@0: * lazily upon first access for a given global. michael@0: * michael@0: * As that object is inaccessible to client code, the lookups are michael@0: * guaranteed to return the original objects, ensuring safe implementation michael@0: * of self-hosted builtins. michael@0: * michael@0: * Additionally, the special syntax callFunction(fun, receiver, ...args) michael@0: * is supported, for which bytecode is emitted that invokes |fun| with michael@0: * |receiver| as the this-object and ...args as the arguments. michael@0: */ michael@0: options.setIntroductionType("self-hosted"); michael@0: options.setFileAndLine("self-hosted", 1); michael@0: options.setSelfHostingMode(true); michael@0: options.setCanLazilyParse(false); michael@0: options.setVersion(JSVERSION_LATEST); michael@0: options.werrorOption = true; michael@0: options.strictOption = true; michael@0: michael@0: #ifdef DEBUG michael@0: options.extraWarningsOption = true; michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: JSRuntime::initSelfHosting(JSContext *cx) michael@0: { michael@0: JS_ASSERT(!selfHostingGlobal_); michael@0: michael@0: if (cx->runtime()->parentRuntime) { michael@0: selfHostingGlobal_ = cx->runtime()->parentRuntime->selfHostingGlobal_; michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * Self hosted state can be accessed from threads for other runtimes michael@0: * parented to this one, so cannot include state in the nursery. michael@0: */ michael@0: JS::AutoDisableGenerationalGC disable(cx->runtime()); michael@0: michael@0: bool receivesDefaultObject = !cx->options().noDefaultCompartmentObject(); michael@0: RootedObject savedGlobal(cx, receivesDefaultObject michael@0: ? js::DefaultObjectForContextOrNull(cx) michael@0: : nullptr); michael@0: JS::CompartmentOptions compartmentOptions; michael@0: compartmentOptions.setDiscardSource(true); michael@0: if (!(selfHostingGlobal_ = JS_NewGlobalObject(cx, &self_hosting_global_class, michael@0: nullptr, JS::DontFireOnNewGlobalHook, michael@0: compartmentOptions))) michael@0: return false; michael@0: JSAutoCompartment ac(cx, selfHostingGlobal_); michael@0: if (receivesDefaultObject) michael@0: js::SetDefaultObjectForContext(cx, selfHostingGlobal_); michael@0: Rooted shg(cx, &selfHostingGlobal_->as()); michael@0: selfHostingGlobal_->compartment()->isSelfHosting = true; michael@0: selfHostingGlobal_->compartment()->isSystem = true; michael@0: /* michael@0: * During initialization of standard classes for the self-hosting global, michael@0: * all self-hosted functions are ignored. Thus, we don't create cyclic michael@0: * dependencies in the order of initialization. michael@0: */ michael@0: if (!GlobalObject::initStandardClasses(cx, shg)) michael@0: return false; michael@0: michael@0: if (!JS_DefineFunctions(cx, shg, intrinsic_functions)) michael@0: return false; michael@0: michael@0: JS_FireOnNewGlobalObject(cx, shg); michael@0: michael@0: CompileOptions options(cx); michael@0: FillSelfHostingCompileOptions(options); michael@0: michael@0: /* michael@0: * Set a temporary error reporter printing to stderr because it is too michael@0: * early in the startup process for any other reporter to be registered michael@0: * and we don't want errors in self-hosted code to be silently swallowed. michael@0: */ michael@0: JSErrorReporter oldReporter = JS_SetErrorReporter(cx, selfHosting_ErrorReporter); michael@0: RootedValue rv(cx); michael@0: bool ok = false; michael@0: michael@0: char *filename = getenv("MOZ_SELFHOSTEDJS"); michael@0: if (filename) { michael@0: RootedScript script(cx, Compile(cx, shg, options, filename)); michael@0: if (script) michael@0: ok = Execute(cx, script, *shg.get(), rv.address()); michael@0: } else { michael@0: uint32_t srcLen = GetRawScriptsSize(); michael@0: michael@0: #ifdef USE_ZLIB michael@0: const unsigned char *compressed = compressedSources; michael@0: uint32_t compressedLen = GetCompressedSize(); michael@0: ScopedJSFreePtr src(reinterpret_cast(cx->malloc_(srcLen))); michael@0: if (!src || !DecompressString(compressed, compressedLen, michael@0: reinterpret_cast(src.get()), srcLen)) michael@0: { michael@0: return false; michael@0: } michael@0: #else michael@0: const char *src = rawSources; michael@0: #endif michael@0: michael@0: ok = Evaluate(cx, shg, options, src, srcLen, &rv); michael@0: } michael@0: JS_SetErrorReporter(cx, oldReporter); michael@0: if (receivesDefaultObject) michael@0: js::SetDefaultObjectForContext(cx, savedGlobal); michael@0: return ok; michael@0: } michael@0: michael@0: void michael@0: JSRuntime::finishSelfHosting() michael@0: { michael@0: selfHostingGlobal_ = nullptr; michael@0: } michael@0: michael@0: void michael@0: JSRuntime::markSelfHostingGlobal(JSTracer *trc) michael@0: { michael@0: if (selfHostingGlobal_ && !parentRuntime) michael@0: MarkObjectRoot(trc, &selfHostingGlobal_, "self-hosting global"); michael@0: } michael@0: michael@0: bool michael@0: JSRuntime::isSelfHostingCompartment(JSCompartment *comp) michael@0: { michael@0: return selfHostingGlobal_->compartment() == comp; michael@0: } michael@0: michael@0: static bool michael@0: CloneValue(JSContext *cx, HandleValue selfHostedValue, MutableHandleValue vp); michael@0: michael@0: static bool michael@0: GetUnclonedValue(JSContext *cx, HandleObject selfHostedObject, HandleId id, MutableHandleValue vp) michael@0: { michael@0: vp.setUndefined(); michael@0: michael@0: if (JSID_IS_INT(id)) { michael@0: size_t index = JSID_TO_INT(id); michael@0: if (index < selfHostedObject->getDenseInitializedLength() && michael@0: !selfHostedObject->getDenseElement(index).isMagic(JS_ELEMENTS_HOLE)) michael@0: { michael@0: vp.set(selfHostedObject->getDenseElement(JSID_TO_INT(id))); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: // Since all atoms used by self hosting are marked as permanent, any michael@0: // attempt to look up a non-permanent atom will fail. We should only michael@0: // see such atoms when code is looking for properties on the self michael@0: // hosted global which aren't present. michael@0: if (JSID_IS_STRING(id) && !JSID_TO_STRING(id)->isPermanentAtom()) { michael@0: JS_ASSERT(selfHostedObject->is()); michael@0: RootedValue value(cx, IdToValue(id)); michael@0: return js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NO_SUCH_SELF_HOSTED_PROP, michael@0: JSDVG_IGNORE_STACK, value, NullPtr(), nullptr, nullptr); michael@0: } michael@0: michael@0: RootedShape shape(cx, selfHostedObject->nativeLookupPure(id)); michael@0: if (!shape) { michael@0: RootedValue value(cx, IdToValue(id)); michael@0: return js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NO_SUCH_SELF_HOSTED_PROP, michael@0: JSDVG_IGNORE_STACK, value, NullPtr(), nullptr, nullptr); michael@0: } michael@0: michael@0: JS_ASSERT(shape->hasSlot() && shape->hasDefaultGetter()); michael@0: vp.set(selfHostedObject->getSlot(shape->slot())); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: CloneProperties(JSContext *cx, HandleObject selfHostedObject, HandleObject clone) michael@0: { michael@0: AutoIdVector ids(cx); michael@0: michael@0: for (size_t i = 0; i < selfHostedObject->getDenseInitializedLength(); i++) { michael@0: if (!selfHostedObject->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) { michael@0: if (!ids.append(INT_TO_JSID(i))) michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: for (Shape::Range range(selfHostedObject->lastProperty()); !range.empty(); range.popFront()) { michael@0: Shape &shape = range.front(); michael@0: if (shape.enumerable() && !ids.append(shape.propid())) michael@0: return false; michael@0: } michael@0: michael@0: RootedId id(cx); michael@0: RootedValue val(cx); michael@0: RootedValue selfHostedValue(cx); michael@0: for (uint32_t i = 0; i < ids.length(); i++) { michael@0: id = ids[i]; michael@0: if (!GetUnclonedValue(cx, selfHostedObject, id, &selfHostedValue)) michael@0: return false; michael@0: if (!CloneValue(cx, selfHostedValue, &val) || michael@0: !JS_DefinePropertyById(cx, clone, id, val.get(), nullptr, nullptr, 0)) michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static JSObject * michael@0: CloneObject(JSContext *cx, HandleObject selfHostedObject) michael@0: { michael@0: AutoCycleDetector detect(cx, selfHostedObject); michael@0: if (!detect.init()) michael@0: return nullptr; michael@0: if (detect.foundCycle()) { michael@0: JS_ReportError(cx, "SelfHosted cloning cannot handle cyclic object graphs."); michael@0: return nullptr; michael@0: } michael@0: michael@0: RootedObject clone(cx); michael@0: if (selfHostedObject->is()) { michael@0: RootedFunction selfHostedFunction(cx, &selfHostedObject->as()); michael@0: bool hasName = selfHostedFunction->atom() != nullptr; michael@0: // Arrow functions use the first extended slot for their lexical |this| value. michael@0: JS_ASSERT(!selfHostedFunction->isArrow()); michael@0: js::gc::AllocKind kind = hasName michael@0: ? JSFunction::ExtendedFinalizeKind michael@0: : selfHostedFunction->getAllocKind(); michael@0: clone = CloneFunctionObject(cx, selfHostedFunction, cx->global(), kind, TenuredObject); michael@0: // To be able to re-lazify the cloned function, its name in the michael@0: // self-hosting compartment has to be stored on the clone. michael@0: if (clone && hasName) michael@0: clone->as().setExtendedSlot(0, StringValue(selfHostedFunction->atom())); michael@0: } else if (selfHostedObject->is()) { michael@0: RegExpObject &reobj = selfHostedObject->as(); michael@0: RootedAtom source(cx, reobj.getSource()); michael@0: JS_ASSERT(source->isPermanentAtom()); michael@0: clone = RegExpObject::createNoStatics(cx, source, reobj.getFlags(), nullptr); michael@0: } else if (selfHostedObject->is()) { michael@0: clone = JS_NewDateObjectMsec(cx, selfHostedObject->as().UTCTime().toNumber()); michael@0: } else if (selfHostedObject->is()) { michael@0: clone = BooleanObject::create(cx, selfHostedObject->as().unbox()); michael@0: } else if (selfHostedObject->is()) { michael@0: clone = NumberObject::create(cx, selfHostedObject->as().unbox()); michael@0: } else if (selfHostedObject->is()) { michael@0: JSString *selfHostedString = selfHostedObject->as().unbox(); michael@0: if (!selfHostedString->isFlat()) michael@0: MOZ_CRASH(); michael@0: RootedString str(cx, js_NewStringCopyN(cx, michael@0: selfHostedString->asFlat().chars(), michael@0: selfHostedString->asFlat().length())); michael@0: if (!str) michael@0: return nullptr; michael@0: clone = StringObject::create(cx, str); michael@0: } else if (selfHostedObject->is()) { michael@0: clone = NewDenseEmptyArray(cx, nullptr, TenuredObject); michael@0: } else { michael@0: JS_ASSERT(selfHostedObject->isNative()); michael@0: clone = NewObjectWithGivenProto(cx, selfHostedObject->getClass(), nullptr, cx->global(), michael@0: selfHostedObject->tenuredGetAllocKind(), michael@0: SingletonObject); michael@0: } michael@0: if (!clone) michael@0: return nullptr; michael@0: if (!CloneProperties(cx, selfHostedObject, clone)) michael@0: return nullptr; michael@0: return clone; michael@0: } michael@0: michael@0: static bool michael@0: CloneValue(JSContext *cx, HandleValue selfHostedValue, MutableHandleValue vp) michael@0: { michael@0: if (selfHostedValue.isObject()) { michael@0: RootedObject selfHostedObject(cx, &selfHostedValue.toObject()); michael@0: JSObject *clone = CloneObject(cx, selfHostedObject); michael@0: if (!clone) michael@0: return false; michael@0: vp.setObject(*clone); michael@0: } else if (selfHostedValue.isBoolean() || selfHostedValue.isNumber() || selfHostedValue.isNullOrUndefined()) { michael@0: // Nothing to do here: these are represented inline in the value. michael@0: vp.set(selfHostedValue); michael@0: } else if (selfHostedValue.isString()) { michael@0: if (!selfHostedValue.toString()->isFlat()) michael@0: MOZ_CRASH(); michael@0: JSFlatString *selfHostedString = &selfHostedValue.toString()->asFlat(); michael@0: JSString *clone = js_NewStringCopyN(cx, michael@0: selfHostedString->chars(), michael@0: selfHostedString->length()); michael@0: if (!clone) michael@0: return false; michael@0: vp.setString(clone); michael@0: } else { michael@0: MOZ_CRASH("Self-hosting CloneValue can't clone given value."); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: JSRuntime::cloneSelfHostedFunctionScript(JSContext *cx, HandlePropertyName name, michael@0: HandleFunction targetFun) michael@0: { michael@0: RootedId id(cx, NameToId(name)); michael@0: RootedValue funVal(cx); michael@0: if (!GetUnclonedValue(cx, HandleObject::fromMarkedLocation(&selfHostingGlobal_), id, &funVal)) michael@0: return false; michael@0: michael@0: RootedFunction sourceFun(cx, &funVal.toObject().as()); michael@0: // JSFunction::generatorKind can't handle lazy self-hosted functions, so we make sure there michael@0: // aren't any. michael@0: JS_ASSERT(!sourceFun->isGenerator()); michael@0: RootedScript sourceScript(cx, sourceFun->getOrCreateScript(cx)); michael@0: if (!sourceScript) michael@0: return false; michael@0: JS_ASSERT(!sourceScript->enclosingStaticScope()); michael@0: JSScript *cscript = CloneScript(cx, NullPtr(), targetFun, sourceScript); michael@0: if (!cscript) michael@0: return false; michael@0: cscript->setFunction(targetFun); michael@0: michael@0: JS_ASSERT(sourceFun->nargs() == targetFun->nargs()); michael@0: // The target function might have been relazified after it's flags changed. michael@0: targetFun->setFlags((targetFun->flags() & ~JSFunction::INTERPRETED_LAZY) | michael@0: sourceFun->flags() | JSFunction::EXTENDED); michael@0: targetFun->setScript(cscript); michael@0: JS_ASSERT(targetFun->isExtended()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: JSRuntime::cloneSelfHostedValue(JSContext *cx, HandlePropertyName name, MutableHandleValue vp) michael@0: { michael@0: RootedId id(cx, NameToId(name)); michael@0: RootedValue selfHostedValue(cx); michael@0: if (!GetUnclonedValue(cx, HandleObject::fromMarkedLocation(&selfHostingGlobal_), id, &selfHostedValue)) michael@0: return false; michael@0: michael@0: /* michael@0: * We don't clone if we're operating in the self-hosting global, as that michael@0: * means we're currently executing the self-hosting script while michael@0: * initializing the runtime (see JSRuntime::initSelfHosting). michael@0: */ michael@0: if (cx->global() == selfHostingGlobal_) { michael@0: vp.set(selfHostedValue); michael@0: return true; michael@0: } michael@0: michael@0: return CloneValue(cx, selfHostedValue, vp); michael@0: } michael@0: michael@0: JSFunction * michael@0: js::SelfHostedFunction(JSContext *cx, HandlePropertyName propName) michael@0: { michael@0: RootedValue func(cx); michael@0: if (!GlobalObject::getIntrinsicValue(cx, cx->global(), propName, &func)) michael@0: return nullptr; michael@0: michael@0: JS_ASSERT(func.isObject()); michael@0: JS_ASSERT(func.toObject().is()); michael@0: return &func.toObject().as(); michael@0: } michael@0: michael@0: bool michael@0: js::IsSelfHostedFunctionWithName(JSFunction *fun, JSAtom *name) michael@0: { michael@0: return fun->isSelfHostedBuiltin() && fun->getExtendedSlot(0).toString() == name; michael@0: }