gecko-dev/js/src/vm/SelfHosting.cpp
Phil Ringnalda c4745d28b1 Backed out 9 changesets (bug 1142775, bug 1139683, bug 1143810, bug 1142761, bug 1142784, bug 1142794, bug 1144819) for widespread bustage
CLOSED TREE

Backed out changeset 7613fc978d36 (bug 1142794)
Backed out changeset e5f0cb31263d (bug 1142784)
Backed out changeset dcd0af73ac84 (bug 1142784)
Backed out changeset 034f9c8e79ee (bug 1142784)
Backed out changeset ce0ee37e3ca9 (bug 1142775)
Backed out changeset 1519b8f2bbba (bug 1142761)
Backed out changeset 26fd55677841 (bug 1139683)
Backed out changeset 7ebc76a450c3 (bug 1144819)
Backed out changeset 92adb459d519 (bug 1143810)
2015-03-22 09:34:25 -07:00

1491 lines
52 KiB
C++

/* -*- 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<CanGC>(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<CanGC>(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<CanGC>(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<CanGC>(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<JSFunction>());
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<JSFunction>().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<char> str(DecompileArgument(cx, args[0].toInt32(), value));
if (!str)
return false;
RootedAtom atom(cx, Atomize(cx, str, strlen(str)));
if (!atom)
return false;
args.rval().setString(atom);
return true;
}
/*
* 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<TypedObject>()) {
MOZ_ASSERT_IF(IsAnyTypedArray(arrobj.get()), idx < AnyTypedArrayLength(arrobj.get()));
MOZ_ASSERT_IF(arrobj->is<TypedObject>(), idx < uint32_t(arrobj->as<TypedObject>().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<ArrayObject>().getDenseInitializedLength());
arrobj->as<ArrayObject>().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<CanGC>(cx, args[1], &id))
return false;
RootedValue value(cx, args[2]);
unsigned attributes = args[3].toInt32();
Rooted<PropertyDescriptor> 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<NativeObject>().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<NativeObject>().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<ArrayObject>() && !obj->hasLazyGroup() &&
!obj->group()->hasAllFlags(OBJECT_FLAG_NON_PACKED) &&
obj->as<ArrayObject>().getDenseInitializedLength() ==
obj->as<ArrayObject>().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<ArrayIteratorObject>());
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<StringIteratorObject>());
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<StarGeneratorObject>());
return true;
}
static bool
intrinsic_StarGeneratorObjectIsClosed(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
StarGeneratorObject *genObj = &args[0].toObject().as<StarGeneratorObject>();
args.rval().setBoolean(genObj->isClosed());
return true;
}
bool
js::intrinsic_IsSuspendedStarGenerator(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
if (!args[0].isObject() || !args[0].toObject().is<StarGeneratorObject>()) {
args.rval().setBoolean(false);
return true;
}
StarGeneratorObject &genObj = args[0].toObject().as<StarGeneratorObject>();
args.rval().setBoolean(!genObj.isClosed() && genObj.isSuspended());
return true;
}
static bool
intrinsic_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<LegacyGeneratorObject>());
return true;
}
static bool
intrinsic_LegacyGeneratorObjectIsClosed(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
LegacyGeneratorObject *genObj = &args[0].toObject().as<LegacyGeneratorObject>();
args.rval().setBoolean(genObj->isClosed());
return true;
}
static bool
intrinsic_CloseClosingLegacyGeneratorObject(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
LegacyGeneratorObject *genObj = &args[0].toObject().as<LegacyGeneratorObject>();
MOZ_ASSERT(genObj->isClosing());
genObj->setClosed();
return true;
}
static bool
intrinsic_ThrowStopIteration(JSContext *cx, unsigned argc, Value *vp)
{
MOZ_ASSERT(CallArgsFromVp(argc, vp).length() == 0);
return ThrowStopIteration(cx);
}
static bool
intrinsic_GeneratorIsRunning(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
GeneratorObject *genObj = &args[0].toObject().as<GeneratorObject>();
args.rval().setBoolean(genObj->isRunning() || genObj->isClosing());
return true;
}
static bool
intrinsic_GeneratorSetClosed(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
GeneratorObject *genObj = &args[0].toObject().as<GeneratorObject>();
genObj->setClosed();
return true;
}
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<TypedArrayObject>());
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<TypedArrayObject*> tarray(cx, &args[0].toObject().as<TypedArrayObject>());
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<TypedArrayObject>()));
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<TypedArrayObject>().type());
MOZ_ASSERT(shift == 0 || shift == 1 || shift == 2 || shift == 3);
args.rval().setInt32(mozilla::AssertedCast<int32_t>(shift));
return true;
}
// Return the value of [[ArrayLength]] internal slot of the TypedArray
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<TypedArrayObject>());
args.rval().setInt32(obj->as<TypedArrayObject>().length());
return true;
}
bool
js::intrinsic_MoveTypedArrayElements(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 4);
Rooted<TypedArrayObject*> tarray(cx, &args[0].toObject().as<TypedArrayObject>());
uint32_t to = uint32_t(args[1].toInt32());
uint32_t from = uint32_t(args[2].toInt32());
uint32_t count = uint32_t(args[3].toInt32());
MOZ_ASSERT(count > 0,
"don't call this method if copying no elements, because then "
"the not-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<uint8_t*>(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<JSFunction>());
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<typename T>
bool
Is(HandleValue v)
{
return v.isObject() && v.toObject().is<T>();
}
template<IsAcceptableThis Test>
static bool
CallNonGenericSelfhostedMethod(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod<Test, CallSelfHostedNonGenericMethod>(cx, args);
}
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<WeakSetObject>());
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<Is<ArrayIteratorObject>>, 2,0),
JS_FN("NewStringIterator", intrinsic_NewStringIterator, 0,0),
JS_FN("IsStringIterator", intrinsic_IsStringIterator, 1,0),
JS_FN("CallStringIteratorMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<StringIteratorObject>>, 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<Is<TypedArrayObject>>, 2, 0),
JS_FN("CallLegacyGeneratorMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<LegacyGeneratorObject>>, 2, 0),
JS_FN("CallStarGeneratorMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<StarGeneratorObject>>, 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<GlobalObject*> 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<GlobalObject*> 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<char> src(selfHostingGlobal_->zone()->pod_malloc<char>(srcLen));
if (!src || !DecompressString(compressed, compressedLen,
reinterpret_cast<unsigned char *>(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<GlobalObject>());
RootedValue value(cx, IdToValue(id));
return ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NO_SUCH_SELF_HOSTED_PROP,
JSDVG_IGNORE_STACK, value, NullPtr(), nullptr, nullptr);
}
RootedShape shape(cx, selfHostedObject->lookupPure(id));
if (!shape) {
RootedValue value(cx, IdToValue(id));
return ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NO_SUCH_SELF_HOSTED_PROP,
JSDVG_IGNORE_STACK, value, NullPtr(), nullptr, nullptr);
}
MOZ_ASSERT(shape->hasSlot() && shape->hasDefaultGetter());
vp.set(selfHostedObject->getSlot(shape->slot()));
return true;
}
static bool
CloneProperties(JSContext *cx, HandleNativeObject selfHostedObject, HandleObject clone)
{
AutoIdVector ids(cx);
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<NoGC> 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<NoGC>(cx, selfHostedString->latin1Chars(nogc), len);
else
clone = NewStringCopyNDontDeflate<NoGC>(cx, selfHostedString->twoByteChars(nogc), len);
if (clone)
return clone;
}
AutoStableStringChars chars(cx);
if (!chars.init(cx, selfHostedString))
return nullptr;
return chars.isLatin1()
? NewStringCopyN<CanGC>(cx, chars.latin1Range().start().get(), len)
: NewStringCopyNDontDeflate<CanGC>(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<JSFunction>()) {
RootedFunction selfHostedFunction(cx, &selfHostedObject->as<JSFunction>());
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<JSFunction>().setExtendedSlot(0, StringValue(selfHostedFunction->atom()));
} else if (selfHostedObject->is<RegExpObject>()) {
RegExpObject &reobj = selfHostedObject->as<RegExpObject>();
RootedAtom source(cx, reobj.getSource());
MOZ_ASSERT(source->isPermanentAtom());
clone = RegExpObject::createNoStatics(cx, source, reobj.getFlags(), nullptr, cx->tempLifoAlloc());
} else if (selfHostedObject->is<DateObject>()) {
clone = JS_NewDateObjectMsec(cx, selfHostedObject->as<DateObject>().UTCTime().toNumber());
} else if (selfHostedObject->is<BooleanObject>()) {
clone = BooleanObject::create(cx, selfHostedObject->as<BooleanObject>().unbox());
} else if (selfHostedObject->is<NumberObject>()) {
clone = NumberObject::create(cx, selfHostedObject->as<NumberObject>().unbox());
} else if (selfHostedObject->is<StringObject>()) {
JSString *selfHostedString = selfHostedObject->as<StringObject>().unbox();
if (!selfHostedString->isFlat())
MOZ_CRASH();
RootedString str(cx, CloneString(cx, &selfHostedString->asFlat()));
if (!str)
return nullptr;
clone = StringObject::create(cx, str);
} else if (selfHostedObject->is<ArrayObject>()) {
clone = NewDenseEmptyArray(cx, NullPtr(), TenuredObject);
} else {
MOZ_ASSERT(selfHostedObject->isNative());
clone = NewObjectWithGivenProto(cx, selfHostedObject->getClass(), NullPtr(),
selfHostedObject->asTenured().getAllocKind(),
SingletonObject);
}
if (!clone)
return nullptr;
if (!CloneProperties(cx, selfHostedObject, clone))
return nullptr;
return clone;
}
static bool
CloneValue(JSContext *cx, HandleValue selfHostedValue, MutableHandleValue vp)
{
if (selfHostedValue.isObject()) {
RootedNativeObject selfHostedObject(cx, &selfHostedValue.toObject().as<NativeObject>());
JSObject *clone = CloneObject(cx, selfHostedObject);
if (!clone)
return false;
vp.setObject(*clone);
} else if (selfHostedValue.isBoolean() || selfHostedValue.isNumber() || selfHostedValue.isNullOrUndefined()) {
// Nothing to do here: these are represented inline in the value.
vp.set(selfHostedValue);
} else if (selfHostedValue.isString()) {
if (!selfHostedValue.toString()->isFlat())
MOZ_CRASH();
JSFlatString *selfHostedString = &selfHostedValue.toString()->asFlat();
JSString *clone = CloneString(cx, selfHostedString);
if (!clone)
return false;
vp.setString(clone);
} else if (selfHostedValue.isSymbol()) {
// Well-known symbols are shared.
mozilla::DebugOnly<JS::Symbol *> sym = selfHostedValue.toSymbol();
MOZ_ASSERT(sym->isWellKnownSymbol());
MOZ_ASSERT(cx->wellKnownSymbols().get(size_t(sym->code())) == sym);
vp.set(selfHostedValue);
} else {
MOZ_CRASH("Self-hosting CloneValue can't clone given value.");
}
return true;
}
bool
JSRuntime::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>());
// 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<JSFunction>());
return &func.toObject().as<JSFunction>();
}
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");