JSArrayBufferPrototype.cpp [plain text]
#include "config.h"
#include "JSArrayBufferPrototype.h"
#include "JSArrayBuffer.h"
#include "JSArrayBufferPrototypeInlines.h"
#include "JSCInlines.h"
namespace JSC {
static JSC_DECLARE_HOST_FUNCTION(arrayBufferProtoFuncSlice);
static JSC_DECLARE_HOST_FUNCTION(arrayBufferProtoGetterFuncByteLength);
static JSC_DECLARE_HOST_FUNCTION(sharedArrayBufferProtoFuncSlice);
static JSC_DECLARE_HOST_FUNCTION(sharedArrayBufferProtoGetterFuncByteLength);
Optional<JSValue> arrayBufferSpeciesConstructorSlow(JSGlobalObject* globalObject, JSArrayBuffer* thisObject, ArrayBufferSharingMode mode)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
bool isValid = speciesWatchpointIsValid(vm, thisObject, mode);
scope.assertNoException();
if (LIKELY(isValid))
return WTF::nullopt;
JSValue constructor = thisObject->get(globalObject, vm.propertyNames->constructor);
RETURN_IF_EXCEPTION(scope, WTF::nullopt);
if (constructor.isConstructor(vm)) {
JSObject* constructorObject = jsCast<JSObject*>(constructor);
JSGlobalObject* globalObjectFromConstructor = constructorObject->globalObject(vm);
bool isArrayBufferConstructorFromAnotherRealm = globalObject != globalObjectFromConstructor
&& constructorObject == globalObjectFromConstructor->arrayBufferConstructor(mode);
if (isArrayBufferConstructorFromAnotherRealm)
return WTF::nullopt;
}
if (constructor.isUndefined())
return WTF::nullopt;
if (!constructor.isObject()) {
throwTypeError(globalObject, scope, "constructor property should not be null"_s);
return WTF::nullopt;
}
JSValue species = constructor.get(globalObject, vm.propertyNames->speciesSymbol);
RETURN_IF_EXCEPTION(scope, WTF::nullopt);
return species.isUndefinedOrNull() ? WTF::nullopt : makeOptional(species);
}
enum class SpeciesConstructResult : uint8_t {
FastPath,
Exception,
CreatedObject
};
static ALWAYS_INLINE std::pair<SpeciesConstructResult, JSArrayBuffer*> speciesConstructArrayBuffer(JSGlobalObject* globalObject, JSArrayBuffer* thisObject, unsigned length, ArrayBufferSharingMode mode)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
constexpr std::pair<SpeciesConstructResult, JSArrayBuffer*> errorResult { SpeciesConstructResult::Exception, nullptr };
constexpr std::pair<SpeciesConstructResult, JSArrayBuffer*> fastPathResult { SpeciesConstructResult::FastPath, nullptr };
Optional<JSValue> species = arrayBufferSpeciesConstructor(globalObject, thisObject, mode);
RETURN_IF_EXCEPTION(scope, errorResult);
if (!species)
return fastPathResult;
MarkedArgumentBuffer args;
args.append(jsNumber(length));
ASSERT(!args.hasOverflowed());
JSObject* newObject = construct(globalObject, species.value(), args, "Species construction did not get a valid constructor");
RETURN_IF_EXCEPTION(scope, errorResult);
JSArrayBuffer* result = jsDynamicCast<JSArrayBuffer*>(vm, newObject);
if (UNLIKELY(!result)) {
throwTypeError(globalObject, scope, "Species construction does not create ArrayBuffer"_s);
return errorResult;
}
if (mode == ArrayBufferSharingMode::Default) {
if (result->isShared()) {
throwTypeError(globalObject, scope, "ArrayBuffer.prototype.slice creates SharedArrayBuffer"_s);
return errorResult;
}
if (result->impl()->isDetached()) {
throwVMTypeError(globalObject, scope, "Created ArrayBuffer is detached"_s);
return errorResult;
}
} else {
if (!result->isShared()) {
throwTypeError(globalObject, scope, "SharedArrayBuffer.prototype.slice creates non-shared ArrayBuffer"_s);
return errorResult;
}
}
if (result == thisObject) {
throwVMTypeError(globalObject, scope, "Species construction returns same ArrayBuffer to a receiver"_s);
return errorResult;
}
if (result->impl()->byteLength() < length) {
throwVMTypeError(globalObject, scope, "Species construction returns ArrayBuffer which byteLength is less than requested"_s);
return errorResult;
}
return { SpeciesConstructResult::CreatedObject, result };
}
static EncodedJSValue arrayBufferSlice(JSGlobalObject* globalObject, JSValue arrayBufferValue, JSValue startValue, JSValue endValue, ArrayBufferSharingMode mode)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSArrayBuffer* thisObject = jsDynamicCast<JSArrayBuffer*>(vm, arrayBufferValue);
if (!thisObject || (mode != thisObject->impl()->sharingMode()))
return throwVMTypeError(globalObject, scope, makeString("Receiver must be "_s, mode == ArrayBufferSharingMode::Default ? "ArrayBuffer"_s : "SharedArrayBuffer"_s));
if (mode == ArrayBufferSharingMode::Default && thisObject->impl()->isDetached())
return throwVMTypeError(globalObject, scope, "Receiver is detached"_s);
unsigned byteLength = thisObject->impl()->byteLength();
unsigned firstIndex = 0;
double relativeStart = startValue.toIntegerOrInfinity(globalObject);
RETURN_IF_EXCEPTION(scope, encodedJSValue());
if (relativeStart < 0)
firstIndex = static_cast<unsigned>(std::max<double>(byteLength + relativeStart, 0));
else
firstIndex = static_cast<unsigned>(std::min<double>(relativeStart, byteLength));
ASSERT(firstIndex <= byteLength);
unsigned finalIndex = 0;
if (!endValue.isUndefined()) {
double relativeEnd = endValue.toIntegerOrInfinity(globalObject);
RETURN_IF_EXCEPTION(scope, encodedJSValue());
if (relativeEnd < 0)
finalIndex = static_cast<unsigned>(std::max<double>(byteLength + relativeEnd, 0));
else
finalIndex = static_cast<unsigned>(std::min<double>(relativeEnd, byteLength));
} else
finalIndex = thisObject->impl()->byteLength();
ASSERT(finalIndex <= byteLength);
unsigned newLength = (finalIndex >= firstIndex) ? finalIndex - firstIndex : 0;
auto speciesResult = speciesConstructArrayBuffer(globalObject, thisObject, newLength, mode);
EXCEPTION_ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
return { };
if (mode == ArrayBufferSharingMode::Default && thisObject->impl()->isDetached())
return throwVMTypeError(globalObject, scope, "Receiver is detached"_s);
if (LIKELY(speciesResult.first == SpeciesConstructResult::FastPath)) {
ASSERT(!thisObject->impl()->isDetached());
auto newBuffer = thisObject->impl()->sliceWithClampedIndex(firstIndex, finalIndex);
if (!newBuffer)
return JSValue::encode(throwOutOfMemoryError(globalObject, scope));
Structure* structure = globalObject->arrayBufferStructure(newBuffer->sharingMode());
JSArrayBuffer* result = JSArrayBuffer::create(vm, structure, WTFMove(newBuffer));
return JSValue::encode(result);
}
JSArrayBuffer* newObject = speciesResult.second;
ASSERT(!thisObject->impl()->isDetached());
ASSERT(!newObject->impl()->isDetached());
ASSERT(newObject->impl()->byteLength() >= newLength);
memcpy(newObject->impl()->data(), static_cast<const char*>(thisObject->impl()->data()) + firstIndex, newLength);
return JSValue::encode(newObject);
}
static EncodedJSValue arrayBufferByteLength(JSGlobalObject* globalObject, JSValue arrayBufferValue, ArrayBufferSharingMode mode)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto* thisObject = jsDynamicCast<JSArrayBuffer*>(vm, arrayBufferValue);
if (!thisObject || (mode != thisObject->impl()->sharingMode()))
return throwVMTypeError(globalObject, scope, makeString("Receiver must be "_s, mode == ArrayBufferSharingMode::Default ? "ArrayBuffer"_s : "SharedArrayBuffer"_s));
if (mode == ArrayBufferSharingMode::Default && thisObject->impl()->isDetached())
return JSValue::encode(jsNumber(0));
return JSValue::encode(jsNumber(thisObject->impl()->byteLength()));
}
JSC_DEFINE_HOST_FUNCTION(arrayBufferProtoFuncSlice, (JSGlobalObject* globalObject, CallFrame* callFrame))
{
return arrayBufferSlice(globalObject, callFrame->thisValue(), callFrame->argument(0), callFrame->argument(1), ArrayBufferSharingMode::Default);
}
JSC_DEFINE_HOST_FUNCTION(arrayBufferProtoGetterFuncByteLength, (JSGlobalObject* globalObject, CallFrame* callFrame))
{
return arrayBufferByteLength(globalObject, callFrame->thisValue(), ArrayBufferSharingMode::Default);
}
JSC_DEFINE_HOST_FUNCTION(sharedArrayBufferProtoFuncSlice, (JSGlobalObject* globalObject, CallFrame* callFrame))
{
return arrayBufferSlice(globalObject, callFrame->thisValue(), callFrame->argument(0), callFrame->argument(1), ArrayBufferSharingMode::Shared);
}
JSC_DEFINE_HOST_FUNCTION(sharedArrayBufferProtoGetterFuncByteLength, (JSGlobalObject* globalObject, CallFrame* callFrame))
{
return arrayBufferByteLength(globalObject, callFrame->thisValue(), ArrayBufferSharingMode::Shared);
}
const ClassInfo JSArrayBufferPrototype::s_info = {
"ArrayBuffer", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSArrayBufferPrototype)
};
JSArrayBufferPrototype::JSArrayBufferPrototype(VM& vm, Structure* structure)
: Base(vm, structure)
{
}
void JSArrayBufferPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject, ArrayBufferSharingMode sharingMode)
{
Base::finishCreation(vm);
putDirectWithoutTransition(vm, vm.propertyNames->toStringTagSymbol, jsString(vm, arrayBufferSharingModeName(sharingMode)), PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly);
if (sharingMode == ArrayBufferSharingMode::Default) {
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->slice, arrayBufferProtoFuncSlice, static_cast<unsigned>(PropertyAttribute::DontEnum), 2);
JSC_NATIVE_GETTER_WITHOUT_TRANSITION(vm.propertyNames->byteLength, arrayBufferProtoGetterFuncByteLength, PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly);
} else {
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->slice, sharedArrayBufferProtoFuncSlice, static_cast<unsigned>(PropertyAttribute::DontEnum), 2);
JSC_NATIVE_GETTER_WITHOUT_TRANSITION(vm.propertyNames->byteLength, sharedArrayBufferProtoGetterFuncByteLength, PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly);
}
}
JSArrayBufferPrototype* JSArrayBufferPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure, ArrayBufferSharingMode sharingMode)
{
JSArrayBufferPrototype* prototype =
new (NotNull, allocateCell<JSArrayBufferPrototype>(vm.heap))
JSArrayBufferPrototype(vm, structure);
prototype->finishCreation(vm, globalObject, sharingMode);
return prototype;
}
Structure* JSArrayBufferPrototype::createStructure(
VM& vm, JSGlobalObject* globalObject, JSValue prototype)
{
return Structure::create(
vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
}
}