JSGenericTypedArrayViewPrototypeFunctions.h [plain text]
#ifndef JSGenericTypedArrayViewPrototypeFunctions_h
#define JSGenericTypedArrayViewPrototypeFunctions_h
#include "ArrayPrototype.h"
#include "Error.h"
#include "ExceptionHelpers.h"
#include "JSArrayBufferViewInlines.h"
#include "JSCBuiltins.h"
#include "JSCJSValueInlines.h"
#include "JSFunction.h"
#include "JSGenericTypedArrayViewInlines.h"
#include "JSGenericTypedArrayViewPrototypeInlines.h"
#include "JSStringJoiner.h"
#include "StructureInlines.h"
#include "TypedArrayAdaptors.h"
#include <wtf/StdLibExtras.h>
namespace JSC {
template<typename Functor>
inline JSArrayBufferView* speciesConstruct(ExecState* exec, JSObject* exemplar, MarkedArgumentBuffer& args, const Functor& defaultConstructor)
{
JSValue constructor = exemplar->get(exec, exec->propertyNames().constructor);
if (exec->hadException())
return nullptr;
if (constructor.isUndefined())
return defaultConstructor();
if (!constructor.isObject()) {
throwTypeError(exec, ASCIILiteral("constructor Property should not be null"));
return nullptr;
}
JSValue species = constructor.get(exec, exec->propertyNames().speciesSymbol);
if (exec->hadException())
return nullptr;
if (species.isUndefinedOrNull())
return defaultConstructor();
JSValue result = construct(exec, species, args, "species is not a constructor");
if (exec->hadException())
return nullptr;
if (JSArrayBufferView* view = jsDynamicCast<JSArrayBufferView*>(result))
return view;
throwTypeError(exec, ASCIILiteral("species constructor did not return a TypedArray View"));
return nullptr;
}
inline unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
{
JSValue value = exec->argument(argument);
if (value.isUndefined())
return undefinedValue;
double indexDouble = value.toInteger(exec);
if (indexDouble < 0) {
indexDouble += length;
return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble);
}
return indexDouble > length ? length : static_cast<unsigned>(indexDouble);
}
template<typename ViewClass>
EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncSet(ExecState* exec)
{
ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
if (!exec->argumentCount())
return throwVMTypeError(exec, ASCIILiteral("Expected at least one argument"));
unsigned offset;
if (exec->argumentCount() >= 2) {
double offsetNumber = exec->uncheckedArgument(1).toInteger(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
if (offsetNumber < 0)
return throwVMRangeError(exec, "Offset should not be negative");
offset = static_cast<unsigned>(std::min(offsetNumber, static_cast<double>(std::numeric_limits<unsigned>::max())));
} else
offset = 0;
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
JSObject* sourceArray = jsDynamicCast<JSObject*>(exec->uncheckedArgument(0));
if (!sourceArray)
return throwVMTypeError(exec, ASCIILiteral("First argument should be an object"));
unsigned length;
if (isTypedView(sourceArray->classInfo()->typedArrayStorageType)) {
JSArrayBufferView* sourceView = jsCast<JSArrayBufferView*>(sourceArray);
if (sourceView->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
length = jsCast<JSArrayBufferView*>(sourceArray)->length();
} else
length = sourceArray->get(exec, exec->vm().propertyNames->length).toUInt32(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
thisObject->set(exec, offset, sourceArray, 0, length, CopyType::Unobservable);
return JSValue::encode(jsUndefined());
}
template<typename ViewClass>
EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncCopyWithin(ExecState* exec)
{
VM& vm = exec->vm();
ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
long length = thisObject->length();
long to = argumentClampedIndexFromStartOrEnd(exec, 0, length);
if (vm.exception())
return encodedJSValue();
long from = argumentClampedIndexFromStartOrEnd(exec, 1, length);
if (vm.exception())
return encodedJSValue();
long final = argumentClampedIndexFromStartOrEnd(exec, 2, length, length);
if (vm.exception())
return encodedJSValue();
if (final < from)
return JSValue::encode(exec->thisValue());
long count = std::min(length - std::max(to, from), final - from);
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
typename ViewClass::ElementType* array = thisObject->typedVector();
memmove(array + to, array + from, count * thisObject->elementSize);
return JSValue::encode(exec->thisValue());
}
template<typename ViewClass>
EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncIncludes(ExecState* exec)
{
VM& vm = exec->vm();
ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
unsigned length = thisObject->length();
if (!length)
return JSValue::encode(jsBoolean(false));
JSValue valueToFind = exec->argument(0);
unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length);
if (vm.exception())
return JSValue::encode(jsUndefined());
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
typename ViewClass::ElementType* array = thisObject->typedVector();
auto targetOption = ViewClass::toAdaptorNativeFromValueWithoutCoercion(valueToFind);
if (!targetOption)
return JSValue::encode(jsBoolean(false));
ASSERT(!vm.exception());
RELEASE_ASSERT(!thisObject->isNeutered());
if (std::isnan(static_cast<double>(*targetOption))) {
for (; index < length; ++index) {
if (std::isnan(static_cast<double>(array[index])))
return JSValue::encode(jsBoolean(true));
}
} else {
for (; index < length; ++index) {
if (array[index] == targetOption)
return JSValue::encode(jsBoolean(true));
}
}
return JSValue::encode(jsBoolean(false));
}
template<typename ViewClass>
EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncIndexOf(ExecState* exec)
{
VM& vm = exec->vm();
ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
if (!exec->argumentCount())
return throwVMTypeError(exec, ASCIILiteral("Expected at least one argument"));
unsigned length = thisObject->length();
JSValue valueToFind = exec->argument(0);
unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length);
if (vm.exception())
return encodedJSValue();
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
typename ViewClass::ElementType* array = thisObject->typedVector();
auto targetOption = ViewClass::toAdaptorNativeFromValueWithoutCoercion(valueToFind);
if (!targetOption)
return JSValue::encode(jsNumber(-1));
ASSERT(!vm.exception());
RELEASE_ASSERT(!thisObject->isNeutered());
for (; index < length; ++index) {
if (array[index] == targetOption)
return JSValue::encode(jsNumber(index));
}
return JSValue::encode(jsNumber(-1));
}
template<typename ViewClass>
EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncJoin(ExecState* exec)
{
ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
auto joinWithSeparator = [&] (StringView separator) -> EncodedJSValue {
ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
unsigned length = thisObject->length();
JSStringJoiner joiner(*exec, separator, length);
if (exec->hadException())
return JSValue::encode(jsUndefined());
for (unsigned i = 0; i < length; i++) {
joiner.append(*exec, thisObject->getIndexQuickly(i));
if (exec->hadException())
return JSValue::encode(jsUndefined());
}
return JSValue::encode(joiner.join(*exec));
};
JSValue separatorValue = exec->argument(0);
if (separatorValue.isUndefined()) {
const LChar* comma = reinterpret_cast<const LChar*>(",");
return joinWithSeparator({ comma, 1 });
}
JSString* separatorString = separatorValue.toString(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
return joinWithSeparator(separatorString->view(exec).get());
}
template<typename ViewClass>
EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncLastIndexOf(ExecState* exec)
{
VM& vm = exec->vm();
ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
if (!exec->argumentCount())
return throwVMTypeError(exec, ASCIILiteral("Expected at least one argument"));
unsigned length = thisObject->length();
JSValue valueToFind = exec->argument(0);
int index = length - 1;
if (exec->argumentCount() >= 2) {
JSValue fromValue = exec->uncheckedArgument(1);
double fromDouble = fromValue.toInteger(exec);
if (fromDouble < 0) {
fromDouble += length;
if (fromDouble < 0)
return JSValue::encode(jsNumber(-1));
}
if (fromDouble < length)
index = static_cast<unsigned>(fromDouble);
}
if (vm.exception())
return JSValue::encode(JSValue());
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
auto targetOption = ViewClass::toAdaptorNativeFromValueWithoutCoercion(valueToFind);
if (!targetOption)
return JSValue::encode(jsNumber(-1));
typename ViewClass::ElementType* array = thisObject->typedVector();
ASSERT(!vm.exception());
RELEASE_ASSERT(!thisObject->isNeutered());
for (; index >= 0; --index) {
if (array[index] == targetOption)
return JSValue::encode(jsNumber(index));
}
return JSValue::encode(jsNumber(-1));
}
template<typename ViewClass>
EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoGetterFuncBuffer(ExecState* exec)
{
ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
return JSValue::encode(thisObject->jsBuffer(exec));
}
template<typename ViewClass>
EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoGetterFuncLength(ExecState* exec)
{
ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
return JSValue::encode(jsNumber(thisObject->length()));
}
template<typename ViewClass>
EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoGetterFuncByteLength(ExecState* exec)
{
ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
return JSValue::encode(jsNumber(thisObject->byteLength()));
}
template<typename ViewClass>
EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoGetterFuncByteOffset(ExecState* exec)
{
ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
return JSValue::encode(jsNumber(thisObject->byteOffset()));
}
template<typename ViewClass>
EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncReverse(ExecState* exec)
{
ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
typename ViewClass::ElementType* array = thisObject->typedVector();
std::reverse(array, array + thisObject->length());
return JSValue::encode(thisObject);
}
template<typename ViewClass>
EncodedJSValue JSC_HOST_CALL genericTypedArrayViewPrivateFuncSort(ExecState* exec)
{
ViewClass* thisObject = jsCast<ViewClass*>(exec->argument(0));
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
thisObject->sort();
return JSValue::encode(thisObject);
}
template<typename ViewClass>
EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncSlice(ExecState* exec)
{
VM& vm = exec->vm();
JSFunction* callee = jsCast<JSFunction*>(exec->callee());
ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
unsigned thisLength = thisObject->length();
unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, thisLength);
if (vm.exception())
return encodedJSValue();
unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, thisLength, thisLength);
if (vm.exception())
return encodedJSValue();
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
end = std::max(begin, end);
ASSERT(end >= begin);
unsigned length = end - begin;
MarkedArgumentBuffer args;
args.append(jsNumber(length));
JSArrayBufferView* result = speciesConstruct(exec, thisObject, args, [&]() {
Structure* structure = callee->globalObject()->typedArrayStructure(ViewClass::TypedArrayStorageType);
return ViewClass::createUninitialized(exec, structure, length);
});
if (exec->hadException())
return JSValue::encode(JSValue());
if (!length)
return JSValue::encode(result);
length = std::min(length, result->length());
switch (result->classInfo()->typedArrayStorageType) {
case TypeInt8:
jsCast<JSInt8Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
break;
case TypeInt16:
jsCast<JSInt16Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
break;
case TypeInt32:
jsCast<JSInt32Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
break;
case TypeUint8:
jsCast<JSUint8Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
break;
case TypeUint8Clamped:
jsCast<JSUint8ClampedArray*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
break;
case TypeUint16:
jsCast<JSUint16Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
break;
case TypeUint32:
jsCast<JSUint32Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
break;
case TypeFloat32:
jsCast<JSFloat32Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
break;
case TypeFloat64:
jsCast<JSFloat64Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
break;
default:
RELEASE_ASSERT_NOT_REACHED();
}
return JSValue::encode(result);
}
template<typename ViewClass>
EncodedJSValue JSC_HOST_CALL genericTypedArrayViewPrivateFuncSubarrayCreate(ExecState* exec)
{
VM& vm = exec->vm();
JSFunction* callee = jsCast<JSFunction*>(exec->callee());
ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
unsigned thisLength = thisObject->length();
ASSERT(exec->argument(0).isNumber());
ASSERT(exec->argument(1).isUndefined() || exec->argument(1).isNumber());
unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, thisLength);
ASSERT(!vm.exception());
unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, thisLength, thisLength);
ASSERT(!vm.exception());
RELEASE_ASSERT(!thisObject->isNeutered());
end = std::max(begin, end);
ASSERT(end >= begin);
unsigned offset = begin;
unsigned length = end - begin;
RefPtr<ArrayBuffer> arrayBuffer = thisObject->buffer();
RELEASE_ASSERT(thisLength == thisObject->length());
unsigned newByteOffset = thisObject->byteOffset() + offset * ViewClass::elementSize;
JSObject* defaultConstructor = callee->globalObject()->typedArrayConstructor(ViewClass::TypedArrayStorageType);
JSValue species = exec->uncheckedArgument(2);
if (species == defaultConstructor) {
Structure* structure = callee->globalObject()->typedArrayStructure(ViewClass::TypedArrayStorageType);
return JSValue::encode(ViewClass::create(
exec, structure, arrayBuffer,
thisObject->byteOffset() + offset * ViewClass::elementSize,
length));
}
MarkedArgumentBuffer args;
args.append(vm.m_typedArrayController->toJS(exec, thisObject->globalObject(), thisObject->buffer()));
args.append(jsNumber(newByteOffset));
args.append(jsNumber(length));
JSObject* result = construct(exec, species, args, "species is not a constructor");
if (exec->hadException())
return JSValue::encode(JSValue());
if (jsDynamicCast<JSArrayBufferView*>(result))
return JSValue::encode(result);
throwTypeError(exec, "species constructor did not return a TypedArray View");
return JSValue::encode(JSValue());
}
}
#endif