Source code

Revision control

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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 "mozilla/Maybe.h"
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
#include <algorithm>
#include <iterator>
#include "jsdate.h"
#include "jsfriendapi.h"
#include "jsmath.h"
#include "jsnum.h"
#include "selfhosted.out.h"
#include "builtin/Array.h"
#include "builtin/BigInt.h"
#ifdef JS_HAS_INTL_API
# include "builtin/intl/Collator.h"
# include "builtin/intl/DateTimeFormat.h"
# include "builtin/intl/DisplayNames.h"
# include "builtin/intl/IntlObject.h"
# include "builtin/intl/ListFormat.h"
# include "builtin/intl/Locale.h"
# include "builtin/intl/NumberFormat.h"
# include "builtin/intl/PluralRules.h"
# include "builtin/intl/RelativeTimeFormat.h"
#endif
#include "builtin/MapObject.h"
#include "builtin/ModuleObject.h"
#include "builtin/Object.h"
#include "builtin/Promise.h"
#include "builtin/Reflect.h"
#include "builtin/RegExp.h"
#include "builtin/SelfHostingDefines.h"
#include "builtin/String.h"
#include "builtin/WeakMapObject.h"
#include "frontend/BytecodeCompilation.h" // CompileGlobalScriptToStencil
#include "frontend/CompilationStencil.h" // js::frontend::CompilationStencil
#include "gc/Marking.h"
#include "gc/Policy.h"
#include "jit/AtomicOperations.h"
#include "jit/InlinableNatives.h"
#include "js/CharacterEncoding.h"
#include "js/CompilationAndEvaluation.h"
#include "js/Conversions.h"
#include "js/Date.h"
#include "js/ErrorReport.h" // JS::PrintError
#include "js/Exception.h"
#include "js/experimental/TypedData.h" // JS_GetArrayBufferViewType
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/Modules.h" // JS::GetModulePrivate
#include "js/PropertySpec.h"
#include "js/ScalarType.h" // js::Scalar::Type
#include "js/SourceText.h" // JS::SourceText
#include "js/StableStringChars.h"
#include "js/Transcoding.h"
#include "js/Warnings.h" // JS::{,Set}WarningReporter
#include "js/Wrapper.h"
#include "util/StringBuffer.h"
#include "vm/ArgumentsObject.h"
#include "vm/AsyncFunction.h"
#include "vm/AsyncIteration.h"
#include "vm/BigIntType.h"
#include "vm/BytecodeIterator.h"
#include "vm/BytecodeLocation.h"
#include "vm/Compression.h"
#include "vm/DateObject.h"
#include "vm/ErrorReporting.h" // js::MaybePrintAndClearPendingException
#include "vm/FrameIter.h" // js::ScriptFrameIter
#include "vm/FunctionFlags.h" // js::FunctionFlags
#include "vm/GeneratorObject.h"
#include "vm/Interpreter.h"
#include "vm/Iteration.h"
#include "vm/JSContext.h"
#include "vm/JSFunction.h"
#include "vm/JSObject.h"
#include "vm/PIC.h"
#include "vm/PlainObject.h" // js::PlainObject
#include "vm/Printer.h"
#include "vm/Realm.h"
#include "vm/RegExpObject.h"
#include "vm/StringType.h"
#include "vm/ToSource.h" // js::ValueToSource
#include "vm/TypedArrayObject.h"
#include "vm/Uint8Clamped.h"
#include "vm/WrapperObject.h"
#include "gc/GC-inl.h"
#include "vm/BooleanObject-inl.h"
#include "vm/BytecodeIterator-inl.h"
#include "vm/BytecodeLocation-inl.h"
#include "vm/Compartment-inl.h"
#include "vm/JSAtom-inl.h"
#include "vm/JSFunction-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/JSScript-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/NumberObject-inl.h"
#include "vm/StringObject-inl.h"
#include "vm/TypedArrayObject-inl.h"
using namespace js;
using namespace js::selfhosted;
using JS::AutoCheckCannotGC;
using JS::AutoStableStringChars;
using JS::CompileOptions;
using mozilla::Maybe;
static void selfHosting_WarningReporter(JSContext* cx, JSErrorReport* report) {
MOZ_ASSERT(report->isWarning());
JS::PrintError(cx, stderr, report, true);
}
static bool intrinsic_ToObject(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
JSObject* obj = ToObject(cx, args[0]);
if (!obj) {
return false;
}
args.rval().setObject(*obj);
return true;
}
static bool intrinsic_IsObject(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
Value val = args[0];
bool isObject = val.isObject();
args.rval().setBoolean(isObject);
return true;
}
static bool intrinsic_IsArray(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
RootedValue val(cx, args[0]);
if (val.isObject()) {
RootedObject obj(cx, &val.toObject());
bool isArray = false;
if (!IsArray(cx, obj, &isArray)) {
return false;
}
args.rval().setBoolean(isArray);
} else {
args.rval().setBoolean(false);
}
return true;
}
static bool intrinsic_IsCrossRealmArrayConstructor(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
bool result = false;
if (!IsCrossRealmArrayConstructor(cx, &args[0].toObject(), &result)) {
return false;
}
args.rval().setBoolean(result);
return true;
}
static bool intrinsic_ToLength(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
// Inline fast path for the common case.
if (args[0].isInt32()) {
int32_t i = args[0].toInt32();
args.rval().setInt32(i < 0 ? 0 : i);
return true;
}
uint64_t length = 0;
if (!ToLength(cx, args[0], &length)) {
return false;
}
args.rval().setNumber(double(length));
return true;
}
static bool intrinsic_ToInteger(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
double result;
if (!ToInteger(cx, args[0], &result)) {
return false;
}
args.rval().setNumber(result);
return true;
}
static bool intrinsic_ToSource(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
JSString* str = ValueToSource(cx, args[0]);
if (!str) {
return false;
}
args.rval().setString(str);
return true;
}
static bool intrinsic_ToPropertyKey(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
RootedId id(cx);
if (!ToPropertyKey(cx, args[0], &id)) {
return false;
}
args.rval().set(IdToValue(id));
return true;
}
static bool intrinsic_IsCallable(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setBoolean(IsCallable(args[0]));
return true;
}
static bool intrinsic_IsConstructor(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
args.rval().setBoolean(IsConstructor(args[0]));
return true;
}
template <typename T>
static bool intrinsic_IsInstanceOfBuiltin(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
args.rval().setBoolean(args[0].toObject().is<T>());
return true;
}
template <typename T>
static bool intrinsic_GuardToBuiltin(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
if (args[0].toObject().is<T>()) {
args.rval().setObject(args[0].toObject());
return true;
}
args.rval().setNull();
return true;
}
template <typename T>
static bool intrinsic_IsWrappedInstanceOfBuiltin(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();
if (!obj->is<WrapperObject>()) {
args.rval().setBoolean(false);
return true;
}
JSObject* unwrapped = CheckedUnwrapDynamic(obj, cx);
if (!unwrapped) {
ReportAccessDenied(cx);
return false;
}
args.rval().setBoolean(unwrapped->is<T>());
return true;
}
template <typename T>
static bool intrinsic_IsPossiblyWrappedInstanceOfBuiltin(JSContext* cx,
unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
JSObject* obj = CheckedUnwrapDynamic(&args[0].toObject(), cx);
if (!obj) {
ReportAccessDenied(cx);
return false;
}
args.rval().setBoolean(obj->is<T>());
return true;
}
static bool intrinsic_SubstringKernel(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args[0].isString());
MOZ_RELEASE_ASSERT(args[1].isInt32());
MOZ_RELEASE_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 void ThrowErrorWithType(JSContext* cx, JSExnType type,
const CallArgs& args) {
MOZ_RELEASE_ASSERT(args[0].isInt32());
uint32_t errorNumber = args[0].toInt32();
#ifdef DEBUG
const JSErrorFormatString* efs = GetErrorMessage(nullptr, errorNumber);
MOZ_ASSERT(efs->argCount == args.length() - 1);
MOZ_ASSERT(efs->exnType == type,
"error-throwing intrinsic and error number are inconsistent");
#endif
UniqueChars errorArgs[3];
for (unsigned i = 1; i < 4 && i < args.length(); i++) {
HandleValue val = args[i];
if (val.isInt32() || val.isString()) {
JSString* str = ToString<CanGC>(cx, val);
if (!str) {
return;
}
errorArgs[i - 1] = QuoteString(cx, str);
} else {
errorArgs[i - 1] =
DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, nullptr);
}
if (!errorArgs[i - 1]) {
return;
}
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber,
errorArgs[0].get(), errorArgs[1].get(),
errorArgs[2].get());
}
static bool intrinsic_ThrowRangeError(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() >= 1);
ThrowErrorWithType(cx, JSEXN_RANGEERR, args);
return false;
}
static bool intrinsic_ThrowTypeError(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() >= 1);
ThrowErrorWithType(cx, JSEXN_TYPEERR, args);
return false;
}
static bool intrinsic_ThrowSyntaxError(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() >= 1);
ThrowErrorWithType(cx, JSEXN_SYNTAXERR, args);
return false;
}
static bool intrinsic_ThrowAggregateError(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() >= 1);
ThrowErrorWithType(cx, JSEXN_AGGREGATEERR, args);
return false;
}
static bool intrinsic_ThrowInternalError(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() >= 1);
ThrowErrorWithType(cx, JSEXN_INTERNALERR, args);
return false;
}
static bool intrinsic_GetErrorMessage(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_RELEASE_ASSERT(args[0].isInt32());
const JSErrorFormatString* errorString =
GetErrorMessage(nullptr, args[0].toInt32());
MOZ_ASSERT(errorString);
MOZ_ASSERT(errorString->argCount == 0);
RootedString message(cx, JS_NewStringCopyZ(cx, errorString->format));
if (!message) {
return false;
}
args.rval().setString(message);
return true;
}
static bool intrinsic_CreateModuleSyntaxError(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 4);
MOZ_ASSERT(args[0].isObject());
MOZ_RELEASE_ASSERT(args[1].isInt32());
MOZ_RELEASE_ASSERT(args[2].isInt32());
MOZ_ASSERT(args[3].isString());
RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
RootedString filename(cx,
JS_NewStringCopyZ(cx, module->script()->filename()));
if (!filename) {
return false;
}
RootedString message(cx, args[3].toString());
RootedValue error(cx);
if (!JS::CreateError(cx, JSEXN_SYNTAXERR, nullptr, filename,
args[1].toInt32(), args[2].toInt32(), nullptr, message,
&error)) {
return false;
}
args.rval().set(error);
return true;
}
/**
* 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) {
js::Fprinter out(stderr);
out.put("Self-hosted JavaScript assertion info: ");
str->dumpCharsNoNewline(out);
out.putChar('\n');
}
}
#endif
MOZ_ASSERT(false);
return false;
}
/**
* Dumps a message to stderr, after stringifying it. Doesn't append a newline.
*/
static bool intrinsic_DumpMessage(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
#ifdef DEBUG
if (args.length() > 0) {
// try to dump the informative string
js::Fprinter out(stderr);
JSString* str = ToString<CanGC>(cx, args[0]);
if (str) {
str->dumpCharsNoNewline(out);
out.putChar('\n');
} else {
cx->recoverFromOutOfMemory();
}
}
#endif
args.rval().setUndefined();
return true;
}
static bool intrinsic_MakeConstructible(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
MOZ_ASSERT(args[0].isObject());
MOZ_ASSERT(args[0].toObject().is<JSFunction>());
MOZ_ASSERT(args[0].toObject().as<JSFunction>().isSelfHostedBuiltin());
MOZ_ASSERT(args[1].isObjectOrNull());
// Normal .prototype properties aren't enumerable. But for this to clone
// correctly, it must be enumerable.
RootedObject ctor(cx, &args[0].toObject());
if (!DefineDataProperty(
cx, ctor, cx->names().prototype, args[1],
JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
return false;
}
ctor->as<JSFunction>().setIsConstructor();
args.rval().setUndefined();
return true;
}
static bool intrinsic_FinishBoundFunctionInit(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 3);
MOZ_ASSERT(IsCallable(args[1]));
MOZ_RELEASE_ASSERT(args[2].isInt32());
RootedFunction bound(cx, &args[0].toObject().as<JSFunction>());
RootedObject targetObj(cx, &args[1].toObject());
int32_t argCount = args[2].toInt32();
args.rval().setUndefined();
return JSFunction::finishBoundFunctionInit(cx, bound, targetObj, argCount);
}
/*
* 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);
MOZ_RELEASE_ASSERT(args[0].isInt32());
HandleValue value = args[1];
JSString* str = DecompileArgument(cx, args[0].toInt32(), value);
if (!str) {
return false;
}
args.rval().setString(str);
return true;
}
static bool intrinsic_DefineDataProperty(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
// When DefineDataProperty is called with 3 arguments, it's compiled to
// JSOp::InitElem in the bytecode emitter so we shouldn't get here.
MOZ_ASSERT(args.length() == 4);
MOZ_ASSERT(args[0].isObject());
MOZ_RELEASE_ASSERT(args[3].isInt32());
RootedObject obj(cx, &args[0].toObject());
RootedId id(cx);
if (!ToPropertyKey(cx, args[1], &id)) {
return false;
}
RootedValue value(cx, args[2]);
unsigned attrs = 0;
unsigned attributes = args[3].toInt32();
MOZ_ASSERT(bool(attributes & ATTR_ENUMERABLE) !=
bool(attributes & ATTR_NONENUMERABLE),
"_DefineDataProperty must receive either ATTR_ENUMERABLE xor "
"ATTR_NONENUMERABLE");
if (attributes & ATTR_ENUMERABLE) {
attrs |= JSPROP_ENUMERATE;
}
MOZ_ASSERT(bool(attributes & ATTR_CONFIGURABLE) !=
bool(attributes & ATTR_NONCONFIGURABLE),
"_DefineDataProperty must receive either ATTR_CONFIGURABLE xor "
"ATTR_NONCONFIGURABLE");
if (attributes & ATTR_NONCONFIGURABLE) {
attrs |= JSPROP_PERMANENT;
}
MOZ_ASSERT(
bool(attributes & ATTR_WRITABLE) != bool(attributes & ATTR_NONWRITABLE),
"_DefineDataProperty must receive either ATTR_WRITABLE xor "
"ATTR_NONWRITABLE");
if (attributes & ATTR_NONWRITABLE) {
attrs |= JSPROP_READONLY;
}
Rooted<PropertyDescriptor> desc(cx);
desc.setDataDescriptor(value, attrs);
if (!DefineProperty(cx, obj, id, desc)) {
return false;
}
args.rval().setUndefined();
return true;
}
static bool intrinsic_DefineProperty(JSContext* cx, unsigned argc, Value* vp) {
// _DefineProperty(object, propertyKey, attributes,
// valueOrGetter, setter, strict)
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 6);
MOZ_ASSERT(args[0].isObject());
MOZ_ASSERT(args[1].isString() || args[1].isNumber() || args[1].isSymbol());
MOZ_RELEASE_ASSERT(args[2].isInt32());
MOZ_ASSERT(args[5].isBoolean());
RootedObject obj(cx, &args[0].toObject());
RootedId id(cx);
if (!PrimitiveValueToId<CanGC>(cx, args[1], &id)) {
return false;
}
Rooted<PropertyDescriptor> desc(cx);
unsigned attributes = args[2].toInt32();
unsigned attrs = 0;
if (attributes & ATTR_ENUMERABLE) {
attrs |= JSPROP_ENUMERATE;
} else if (!(attributes & ATTR_NONENUMERABLE)) {
attrs |= JSPROP_IGNORE_ENUMERATE;
}
if (attributes & ATTR_NONCONFIGURABLE) {
attrs |= JSPROP_PERMANENT;
} else if (!(attributes & ATTR_CONFIGURABLE)) {
attrs |= JSPROP_IGNORE_PERMANENT;
}
if (attributes & ATTR_NONWRITABLE) {
attrs |= JSPROP_READONLY;
} else if (!(attributes & ATTR_WRITABLE)) {
attrs |= JSPROP_IGNORE_READONLY;
}
// When args[4] is |null|, the data descriptor has a value component.
if ((attributes & DATA_DESCRIPTOR_KIND) && args[4].isNull()) {
desc.value().set(args[3]);
} else {
attrs |= JSPROP_IGNORE_VALUE;
}
if (attributes & ACCESSOR_DESCRIPTOR_KIND) {
Value getter = args[3];
MOZ_ASSERT(getter.isObject() || getter.isNullOrUndefined());
if (getter.isObject()) {
desc.setGetterObject(&getter.toObject());
}
if (!getter.isNull()) {
attrs |= JSPROP_GETTER;
}
Value setter = args[4];
MOZ_ASSERT(setter.isObject() || setter.isNullOrUndefined());
if (setter.isObject()) {
desc.setSetterObject(&setter.toObject());
}
if (!setter.isNull()) {
attrs |= JSPROP_SETTER;
}
// By convention, these bits are not used on accessor descriptors.
attrs &= ~(JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE);
}
desc.setAttributes(attrs);
desc.assertValid();
ObjectOpResult result;
if (!DefineProperty(cx, obj, id, desc, result)) {
return false;
}
bool strict = args[5].toBoolean();
if (strict && !result.ok()) {
// We need to tell our caller Object.defineProperty,
// that this operation failed, without actually throwing
// for web-compatibility reasons.
if (result.failureCode() == JSMSG_CANT_DEFINE_WINDOW_NC) {
args.rval().setBoolean(false);
return true;
}
return result.reportError(cx, obj, id);
}
args.rval().setBoolean(result.ok());
return true;
}
static bool intrinsic_ObjectHasPrototype(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
// Self-hosted code calls this intrinsic with builtin prototypes. These are
// always native objects.
auto* obj = &args[0].toObject().as<NativeObject>();
auto* proto = &args[1].toObject().as<NativeObject>();
JSObject* actualProto = obj->staticPrototype();
args.rval().setBoolean(actualProto == proto);
return true;
}
static bool intrinsic_UnsafeSetReservedSlot(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 3);
MOZ_ASSERT(args[0].isObject());
MOZ_RELEASE_ASSERT(args[1].isInt32());
MOZ_ASSERT(args[1].toInt32() >= 0);
uint32_t slot = uint32_t(args[1].toInt32());
args[0].toObject().as<NativeObject>().setReservedSlot(slot, args[2]);
args.rval().setUndefined();
return true;
}
static bool intrinsic_UnsafeGetReservedSlot(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
MOZ_ASSERT(args[0].isObject());
MOZ_RELEASE_ASSERT(args[1].isInt32());
MOZ_ASSERT(args[1].toInt32() >= 0);
uint32_t slot = uint32_t(args[1].toInt32());
args.rval().set(args[0].toObject().as<NativeObject>().getReservedSlot(slot));
return true;
}
static bool intrinsic_UnsafeGetObjectFromReservedSlot(JSContext* cx,
unsigned argc,
Value* vp) {
if (!intrinsic_UnsafeGetReservedSlot(cx, argc, vp)) {
return false;
}
MOZ_ASSERT(vp->isObject());
return true;
}
static bool intrinsic_UnsafeGetInt32FromReservedSlot(JSContext* cx,
unsigned argc, Value* vp) {
if (!intrinsic_UnsafeGetReservedSlot(cx, argc, vp)) {
return false;
}
MOZ_ASSERT(vp->isInt32());
return true;
}
static bool intrinsic_UnsafeGetStringFromReservedSlot(JSContext* cx,
unsigned argc,
Value* vp) {
if (!intrinsic_UnsafeGetReservedSlot(cx, argc, vp)) {
return false;
}
MOZ_ASSERT(vp->isString());
return true;
}
static bool intrinsic_UnsafeGetBooleanFromReservedSlot(JSContext* cx,
unsigned argc,
Value* vp) {
if (!intrinsic_UnsafeGetReservedSlot(cx, argc, vp)) {
return false;
}
MOZ_ASSERT(vp->isBoolean());
return true;
}
static bool intrinsic_ThisTimeValue(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isInt32());
const char* name = nullptr;
int32_t method = args[0].toInt32();
if (method == DATE_METHOD_LOCALE_TIME_STRING) {
name = "toLocaleTimeString";
} else if (method == DATE_METHOD_LOCALE_DATE_STRING) {
name = "toLocaleDateString";
} else {
MOZ_ASSERT(method == DATE_METHOD_LOCALE_STRING);
name = "toLocaleString";
}
auto* unwrapped = UnwrapAndTypeCheckThis<DateObject>(cx, args, name);
if (!unwrapped) {
return false;
}
args.rval().set(unwrapped->UTCTime());
return true;
}
static bool intrinsic_IsPackedArray(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
args.rval().setBoolean(IsPackedArray(&args[0].toObject()));
return true;
}
bool js::intrinsic_NewArrayIterator(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 0);
JSObject* obj = NewArrayIterator(cx);
if (!obj) {
return false;
}
args.rval().setObject(*obj);
return true;
}
static bool intrinsic_ArrayIteratorPrototypeOptimizable(JSContext* cx,
unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 0);
ForOfPIC::Chain* stubChain = ForOfPIC::getOrCreate(cx);
if (!stubChain) {
return false;
}
bool optimized;
if (!stubChain->tryOptimizeArrayIteratorNext(cx, &optimized)) {
return false;
}
args.rval().setBoolean(optimized);
return true;
}
static bool intrinsic_GetNextMapEntryForIterator(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
MOZ_ASSERT(args[0].toObject().is<MapIteratorObject>());
MOZ_ASSERT(args[1].isObject());
MapIteratorObject* mapIterator = &args[0].toObject().as<MapIteratorObject>();
ArrayObject* result = &args[1].toObject().as<ArrayObject>();
args.rval().setBoolean(MapIteratorObject::next(mapIterator, result));
return true;
}
static bool intrinsic_CreateMapIterationResultPair(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 0);
JSObject* result = MapIteratorObject::createResultPair(cx);
if (!result) {
return false;
}
args.rval().setObject(*result);
return true;
}
static bool intrinsic_GetNextSetEntryForIterator(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
MOZ_ASSERT(args[0].toObject().is<SetIteratorObject>());
MOZ_ASSERT(args[1].isObject());
SetIteratorObject* setIterator = &args[0].toObject().as<SetIteratorObject>();
ArrayObject* result = &args[1].toObject().as<ArrayObject>();
args.rval().setBoolean(SetIteratorObject::next(setIterator, result));
return true;
}
static bool intrinsic_CreateSetIterationResult(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 0);
JSObject* result = SetIteratorObject::createResult(cx);
if (!result) {
return false;
}
args.rval().setObject(*result);
return true;
}
bool js::intrinsic_NewStringIterator(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 0);
JSObject* obj = NewStringIterator(cx);
if (!obj) {
return false;
}
args.rval().setObject(*obj);
return true;
}
bool js::intrinsic_NewRegExpStringIterator(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 0);
JSObject* obj = NewRegExpStringIterator(cx);
if (!obj) {
return false;
}
args.rval().setObject(*obj);
return true;
}
static js::PropertyName* GetUnclonedSelfHostedFunctionName(JSFunction* fun) {
if (!fun->isExtended()) {
return nullptr;
}
Value name = fun->getExtendedSlot(ORIGINAL_FUNCTION_NAME_SLOT);
if (!name.isString()) {
return nullptr;
}
return name.toString()->asAtom().asPropertyName();
}
js::PropertyName* js::GetClonedSelfHostedFunctionName(const JSFunction* fun) {
if (!fun->isExtended()) {
return nullptr;
}
Value name = fun->getExtendedSlot(LAZY_FUNCTION_NAME_SLOT);
if (!name.isString()) {
return nullptr;
}
return name.toString()->asAtom().asPropertyName();
}
js::PropertyName* js::GetClonedSelfHostedFunctionNameOffMainThread(
JSFunction* fun) {
Value name = fun->getExtendedSlotOffMainThread(LAZY_FUNCTION_NAME_SLOT);
if (!name.isString()) {
return nullptr;
}
return name.toString()->asAtom().asPropertyName();
}
bool js::IsExtendedUnclonedSelfHostedFunctionName(JSAtom* name) {
if (name->length() < 2) {
return false;
}
return name->latin1OrTwoByteChar(0) ==
ExtendedUnclonedSelfHostedFunctionNamePrefix;
}
static void SetUnclonedSelfHostedFunctionName(JSFunction* fun, JSAtom* name) {
fun->setExtendedSlot(ORIGINAL_FUNCTION_NAME_SLOT, StringValue(name));
}
static void SetClonedSelfHostedFunctionName(JSFunction* fun, JSAtom* name) {
fun->setExtendedSlot(LAZY_FUNCTION_NAME_SLOT, StringValue(name));
}
static bool intrinsic_SetCanonicalName(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
MOZ_ASSERT(fun->isSelfHostedBuiltin());
MOZ_ASSERT(fun->isExtended());
MOZ_ASSERT(IsExtendedUnclonedSelfHostedFunctionName(fun->explicitName()));
JSAtom* atom = AtomizeString(cx, args[1].toString());
if (!atom) {
return false;
}
// _SetCanonicalName can only be called on top-level function declarations.
MOZ_ASSERT(fun->kind() == FunctionFlags::NormalFunction);
MOZ_ASSERT(!fun->isLambda());
// It's an error to call _SetCanonicalName multiple times.
MOZ_ASSERT(!GetUnclonedSelfHostedFunctionName(fun));
// Set the lazy function name so we can later retrieve the script from the
// self-hosting global.
SetUnclonedSelfHostedFunctionName(fun, fun->explicitName());
fun->setAtom(atom);
args.rval().setUndefined();
return true;
}
static bool intrinsic_SetIsInlinableLargeFunction(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
MOZ_ASSERT(fun->isSelfHostedBuiltin());
// _SetIsInlinableLargeFunction can only be called on top-level function
// declarations.
MOZ_ASSERT(fun->kind() == FunctionFlags::NormalFunction);
MOZ_ASSERT(!fun->isLambda());
fun->baseScript()->setIsInlinableLargeFunction();
args.rval().setUndefined();
return true;
}
static bool intrinsic_GeneratorObjectIsClosed(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->isClosed());
return true;
}
static bool intrinsic_IsSuspendedGenerator(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
if (!args[0].isObject() || !args[0].toObject().is<GeneratorObject>()) {
args.rval().setBoolean(false);
return true;
}
GeneratorObject& genObj = args[0].toObject().as<GeneratorObject>();
args.rval().setBoolean(!genObj.isClosed() && genObj.isSuspended());
return true;
}
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());
return true;
}
static bool intrinsic_GeneratorSetClosed(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
GeneratorObject* genObj = &args[0].toObject().as<GeneratorObject>();
genObj->setClosed();
return true;
}
template <typename T>
static bool intrinsic_ArrayBufferByteLength(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
MOZ_ASSERT(args[0].toObject().is<T>());
size_t byteLength = args[0].toObject().as<T>().byteLength().get();
args.rval().setNumber(byteLength);
return true;
}
template <typename T>
static bool intrinsic_PossiblyWrappedArrayBufferByteLength(JSContext* cx,
unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
T* obj = args[0].toObject().maybeUnwrapAs<T>();
if (!obj) {
ReportAccessDenied(cx);
return false;
}
size_t byteLength = obj->byteLength().get();
args.rval().setNumber(byteLength);
return true;
}
static void AssertNonNegativeInteger(const Value& v) {
MOZ_ASSERT(v.isNumber());
MOZ_ASSERT(v.toNumber() >= 0);
MOZ_ASSERT(v.toNumber() < DOUBLE_INTEGRAL_PRECISION_LIMIT);
MOZ_ASSERT(JS::ToInteger(v.toNumber()) == v.toNumber());
}
template <typename T>
static bool intrinsic_ArrayBufferCopyData(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 6);
AssertNonNegativeInteger(args[1]);
AssertNonNegativeInteger(args[3]);
AssertNonNegativeInteger(args[4]);
bool isWrapped = args[5].toBoolean();
Rooted<T*> toBuffer(cx);
if (!isWrapped) {
toBuffer = &args[0].toObject().as<T>();
} else {
JSObject* wrapped = &args[0].toObject();
MOZ_ASSERT(wrapped->is<WrapperObject>());
toBuffer = wrapped->maybeUnwrapAs<T>();
if (!toBuffer) {
ReportAccessDenied(cx);
return false;
}
}
size_t toIndex = size_t(args[1].toNumber());
Rooted<T*> fromBuffer(cx, &args[2].toObject().as<T>());
size_t fromIndex = size_t(args[3].toNumber());
size_t count = size_t(args[4].toNumber());
T::copyData(toBuffer, toIndex, fromBuffer, fromIndex, count);
args.rval().setUndefined();
return true;
}
// Arguments must both be SharedArrayBuffer or wrapped SharedArrayBuffer.
static bool intrinsic_SharedArrayBuffersMemorySame(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
auto* lhs = args[0].toObject().maybeUnwrapAs<SharedArrayBufferObject>();
if (!lhs) {
ReportAccessDenied(cx);
return false;
}
auto* rhs = args[1].toObject().maybeUnwrapAs<SharedArrayBufferObject>();
if (!rhs) {
ReportAccessDenied(cx);
return false;
}
args.rval().setBoolean(lhs->rawBufferObject() == rhs->rawBufferObject());
return true;
}
static bool intrinsic_GetTypedArrayKind(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
static_assert(TYPEDARRAY_KIND_INT8 == Scalar::Type::Int8,
"TYPEDARRAY_KIND_INT8 doesn't match the scalar type");
static_assert(TYPEDARRAY_KIND_UINT8 == Scalar::Type::Uint8,
"TYPEDARRAY_KIND_UINT8 doesn't match the scalar type");
static_assert(TYPEDARRAY_KIND_INT16 == Scalar::Type::Int16,
"TYPEDARRAY_KIND_INT16 doesn't match the scalar type");
static_assert(TYPEDARRAY_KIND_UINT16 == Scalar::Type::Uint16,
"TYPEDARRAY_KIND_UINT16 doesn't match the scalar type");
static_assert(TYPEDARRAY_KIND_INT32 == Scalar::Type::Int32,
"TYPEDARRAY_KIND_INT32 doesn't match the scalar type");
static_assert(TYPEDARRAY_KIND_UINT32 == Scalar::Type::Uint32,
"TYPEDARRAY_KIND_UINT32 doesn't match the scalar type");
static_assert(TYPEDARRAY_KIND_FLOAT32 == Scalar::Type::Float32,
"TYPEDARRAY_KIND_FLOAT32 doesn't match the scalar type");
static_assert(TYPEDARRAY_KIND_FLOAT64 == Scalar::Type::Float64,
"TYPEDARRAY_KIND_FLOAT64 doesn't match the scalar type");
static_assert(TYPEDARRAY_KIND_UINT8CLAMPED == Scalar::Type::Uint8Clamped,
"TYPEDARRAY_KIND_UINT8CLAMPED doesn't match the scalar type");
static_assert(TYPEDARRAY_KIND_BIGINT64 == Scalar::Type::BigInt64,
"TYPEDARRAY_KIND_BIGINT64 doesn't match the scalar type");
static_assert(TYPEDARRAY_KIND_BIGUINT64 == Scalar::Type::BigUint64,
"TYPEDARRAY_KIND_BIGUINT64 doesn't match the scalar type");
JSObject* obj = &args[0].toObject();
Scalar::Type type = JS_GetArrayBufferViewType(obj);
args.rval().setInt32(static_cast<int32_t>(type));
return true;
}
static bool intrinsic_IsTypedArrayConstructor(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
args.rval().setBoolean(js::IsTypedArrayConstructor(&args[0].toObject()));
return true;
}
static bool intrinsic_TypedArrayBuffer(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(TypedArrayObject::is(args[0]));
Rooted<TypedArrayObject*> tarray(cx,
&args[0].toObject().as<TypedArrayObject>());
if (!TypedArrayObject::ensureHasBuffer(cx, tarray)) {
return false;
}
args.rval().set(tarray->bufferValue());
return true;
}
static bool intrinsic_TypedArrayByteOffset(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(TypedArrayObject::is(args[0]));
auto* tarr = &args[0].toObject().as<TypedArrayObject>();
args.rval().set(tarr->byteOffsetValue());
return true;
}
static bool intrinsic_TypedArrayElementSize(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(TypedArrayObject::is(args[0]));
unsigned size =
TypedArrayElemSize(args[0].toObject().as<TypedArrayObject>().type());
MOZ_ASSERT(size == 1 || size == 2 || size == 4 || size == 8);
args.rval().setInt32(mozilla::AssertedCast<int32_t>(size));
return true;
}
// Return the value of [[ArrayLength]] internal slot of the TypedArray
static bool intrinsic_TypedArrayLength(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(TypedArrayObject::is(args[0]));
auto* tarr = &args[0].toObject().as<TypedArrayObject>();
args.rval().set(tarr->lengthValue());
return true;
}
static bool intrinsic_PossiblyWrappedTypedArrayLength(JSContext* cx,
unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
TypedArrayObject* obj = args[0].toObject().maybeUnwrapAs<TypedArrayObject>();
if (!obj) {
ReportAccessDenied(cx);
return false;
}
args.rval().set(obj->lengthValue());
return true;
}
static bool intrinsic_PossiblyWrappedTypedArrayHasDetachedBuffer(JSContext* cx,
unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
TypedArrayObject* obj = args[0].toObject().maybeUnwrapAs<TypedArrayObject>();
if (!obj) {
ReportAccessDenied(cx);
return false;
}
bool detached = obj->hasDetachedBuffer();
args.rval().setBoolean(detached);
return true;
}
// Extract the TypedArrayObject* underlying |obj| and return it. This method,
// in a TOTALLY UNSAFE manner, completely violates the normal compartment
// boundaries, returning an object not necessarily in the current compartment
// or in |obj|'s compartment.
//
// All callers of this method are expected to sigil this TypedArrayObject*, and
// all values and information derived from it, with an "unsafe" prefix, to
// indicate the extreme caution required when dealing with such values.
//
// If calling code discipline ever fails to be maintained, it's gonna have a
// bad time.
static TypedArrayObject* DangerouslyUnwrapTypedArray(JSContext* cx,
JSObject* obj) {
// An unwrapped pointer to an object potentially on the other side of a
// compartment boundary! Isn't this such fun?
TypedArrayObject* unwrapped = obj->maybeUnwrapAs<TypedArrayObject>();
if (!unwrapped) {
ReportAccessDenied(cx);
return nullptr;
}
// Be super-duper careful using this, as we've just punched through
// the compartment boundary, and things like buffer() on this aren't
// same-compartment with anything else in the calling method.
return unwrapped;
}
// The specification requires us to perform bitwise copying when |sourceType|
// and |targetType| are the same (ES2017, ยง22.2.3.24, step 15). Additionally,
// as an optimization, we can also perform bitwise copying when |sourceType|
// and |targetType| have compatible bit-level representations.
static bool IsTypedArrayBitwiseSlice(Scalar::Type sourceType,
Scalar::Type targetType) {
switch (sourceType) {
case Scalar::Int8:
return targetType == Scalar::Int8 || targetType == Scalar::Uint8;
case Scalar::Uint8:
case Scalar::Uint8Clamped:
return targetType == Scalar::Int8 || targetType == Scalar::Uint8 ||
targetType == Scalar::Uint8Clamped;
case Scalar::Int16:
case Scalar::Uint16:
return targetType == Scalar::Int16 || targetType == Scalar::Uint16;
case Scalar::Int32:
case Scalar::Uint32:
return targetType == Scalar::Int32 || targetType == Scalar::Uint32;
case Scalar::Float32:
return targetType == Scalar::Float32;
case Scalar::Float64:
return targetType == Scalar::Float64;
case Scalar::BigInt64:
case Scalar::BigUint64:
return targetType == Scalar::BigInt64 || targetType == Scalar::BigUint64;
default:
MOZ_CRASH("IsTypedArrayBitwiseSlice with a bogus typed array type");
}
}
static bool intrinsic_TypedArrayBitwiseSlice(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 4);
MOZ_ASSERT(args[0].isObject());
MOZ_ASSERT(args[1].isObject());
AssertNonNegativeInteger(args[2]);
AssertNonNegativeInteger(args[3]);
Rooted<TypedArrayObject*> source(cx,
&args[0].toObject().as<TypedArrayObject>());
MOZ_ASSERT(!source->hasDetachedBuffer());
// As directed by |DangerouslyUnwrapTypedArray|, sigil this pointer and all
// variables derived from it to counsel extreme caution here.
Rooted<TypedArrayObject*> unsafeTypedArrayCrossCompartment(cx);
unsafeTypedArrayCrossCompartment =
DangerouslyUnwrapTypedArray(cx, &args[1].toObject());
if (!unsafeTypedArrayCrossCompartment) {
return false;
}
MOZ_ASSERT(!unsafeTypedArrayCrossCompartment->hasDetachedBuffer());
Scalar::Type sourceType = source->type();
if (!IsTypedArrayBitwiseSlice(sourceType,
unsafeTypedArrayCrossCompartment->type())) {
args.rval().setBoolean(false);
return true;
}
size_t sourceOffset = size_t(args[2].toNumber());
size_t count = size_t(args[3].toNumber());
MOZ_ASSERT(count > 0 && count <= source->length().get());
MOZ_ASSERT(sourceOffset <= source->length().get() - count);
MOZ_ASSERT(count <= unsafeTypedArrayCrossCompartment->length().get());
size_t elementSize = TypedArrayElemSize(sourceType);
MOZ_ASSERT(elementSize ==
TypedArrayElemSize(unsafeTypedArrayCrossCompartment->type()));
SharedMem<uint8_t*> sourceData =
source->dataPointerEither().cast<uint8_t*>() + sourceOffset * elementSize;
SharedMem<uint8_t*> unsafeTargetDataCrossCompartment =
unsafeTypedArrayCrossCompartment->dataPointerEither().cast<uint8_t*>();
size_t byteLength = count * elementSize;
// The same-type case requires exact copying preserving the bit-level
// encoding of the source data, so use memcpy if possible. If source and
// target are the same buffer, we can't use memcpy (or memmove), because
// the specification requires sequential copying of the values. This case
// is only possible if a @@species constructor created a specifically
// crafted typed array. It won't happen in normal code and hence doesn't
// need to be optimized.
if (!TypedArrayObject::sameBuffer(source, unsafeTypedArrayCrossCompartment)) {
if (source->isSharedMemory() ||
unsafeTypedArrayCrossCompartment->isSharedMemory()) {
jit::AtomicOperations::memcpySafeWhenRacy(
unsafeTargetDataCrossCompartment, sourceData, byteLength);
} else {
memcpy(unsafeTargetDataCrossCompartment.unwrapUnshared(),
sourceData.unwrapUnshared(), byteLength);
}
} else {
using namespace jit;
for (; byteLength > 0; byteLength--) {
AtomicOperations::storeSafeWhenRacy(
unsafeTargetDataCrossCompartment++,
AtomicOperations::loadSafeWhenRacy(sourceData++));
}
}
args.rval().setBoolean(true);
return true;
}
static bool intrinsic_TypedArrayInitFromPackedArray(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].isObject());
Rooted<TypedArrayObject*> target(cx,
&args[0].toObject().as<TypedArrayObject>());
MOZ_ASSERT(!target->hasDetachedBuffer());
MOZ_ASSERT(!target->isSharedMemory());
RootedArrayObject source(cx, &args[1].toObject().as<ArrayObject>());
MOZ_ASSERT(IsPackedArray(source));
MOZ_ASSERT(source->length() == target->length().get());
switch (target->type()) {
#define INIT_TYPED_ARRAY(T, N) \
case Scalar::N: { \
if (!ElementSpecific<T, UnsharedOps>::initFromIterablePackedArray( \
cx, target, source)) { \
return false; \
} \
break; \
}
JS_FOR_EACH_TYPED_ARRAY(INIT_TYPED_ARRAY)
#undef INIT_TYPED_ARRAY
default:
MOZ_CRASH(
"TypedArrayInitFromPackedArray with a typed array with bogus type");
}
args.rval().setUndefined();
return true;
}
static bool intrinsic_RegExpCreate(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1 || args.length() == 2);
MOZ_ASSERT_IF(args.length() == 2,
args[1].isString() || args[1].isUndefined());
MOZ_ASSERT(!args.isConstructing());
return RegExpCreate(cx, args[0], args.get(1), args.rval());
}
static bool intrinsic_RegExpGetSubstitution(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 6);
RootedArrayObject matchResult(cx, &args[0].toObject().as<ArrayObject>());
RootedLinearString string(cx, args[1].toString()->ensureLinear(cx));
if (!string) {
return false;
}
int32_t position = int32_t(args[2].toNumber());
MOZ_ASSERT(position >= 0);
RootedLinearString replacement(cx, args[3].toString()->ensureLinear(cx));
if (!replacement) {
return false;
}
int32_t firstDollarIndex = int32_t(args[4].toNumber());
MOZ_ASSERT(firstDollarIndex >= 0);
RootedValue namedCaptures(cx, args[5]);
MOZ_ASSERT(namedCaptures.isUndefined() || namedCaptures.isObject());
return RegExpGetSubstitution(cx, matchResult, string, size_t(position),
replacement, size_t(firstDollarIndex),
namedCaptures, args.rval());
}
static bool intrinsic_StringReplaceString(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 3);
RootedString string(cx, args[0].toString());
RootedString pattern(cx, args[1].toString());
RootedString replacement(cx, args[2].toString());
JSString* result = str_replace_string_raw(cx, string, pattern, replacement);
if (!result) {
return false;
}
args.rval().setString(result);
return true;
}
static bool intrinsic_StringReplaceAllString(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 3);
RootedString string(cx, args[0].toString());
RootedString pattern(cx, args[1].toString());
RootedString replacement(cx, args[2].toString());
JSString* result =
str_replaceAll_string_raw(cx, string, pattern, replacement);
if (!result) {
return false;
}
args.rval().setString(result);
return true;
}
static bool intrinsic_StringSplitString(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
RootedString string(cx, args[0].toString());
RootedString sep(cx, args[1].toString());
JSObject* aobj = StringSplitString(cx, string, sep, INT32_MAX);
if (!aobj) {
return false;
}
args.rval().setObject(*aobj);
return true;
}
static bool intrinsic_StringSplitStringLimit(JSContext* cx, unsigned argc,
Value*