blob: 237bd46586f2ff60639c63677611a8b50b68027c [file] [log] [blame]
/*
* Copyright (C) 2016-2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "TypedArrayCTest.h"
#include "JavaScript.h"
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <wtf/Assertions.h>
extern "C" void JSSynchronousGarbageCollectForDebugging(JSContextRef);
static void id(void*, void*) { }
static void freePtr(void* ptr, void*)
{
free(ptr);
}
static constexpr unsigned numLengths = 3;
static const unsigned lengths[numLengths] =
{
0,
1,
10,
};
static const unsigned byteSizes[kJSTypedArrayTypeArrayBuffer] =
{
1, // kJSTypedArrayTypeInt8Array
2, // kJSTypedArrayTypeInt16Array
4, // kJSTypedArrayTypeInt32Array
1, // kJSTypedArrayTypeUint8Array
1, // kJSTypedArrayTypeUint8ClampedArray
2, // kJSTypedArrayTypeUint16Array
4, // kJSTypedArrayTypeUint32Array
4, // kJSTypedArrayTypeFloat32Array
8, // kJSTypedArrayTypeFloat64Array
};
static const char* typeToString[kJSTypedArrayTypeArrayBuffer] =
{
"kJSTypedArrayTypeInt8Array",
"kJSTypedArrayTypeInt16Array",
"kJSTypedArrayTypeInt32Array",
"kJSTypedArrayTypeUint8Array",
"kJSTypedArrayTypeUint8ClampedArray",
"kJSTypedArrayTypeUint16Array",
"kJSTypedArrayTypeUint32Array",
"kJSTypedArrayTypeFloat32Array",
"kJSTypedArrayTypeFloat64Array",
};
inline int unexpectedException(const char* name)
{
fprintf(stderr, "%s FAILED: unexpected exception\n", name);
return 1;
}
static int assertEqualsAsNumber(JSGlobalContextRef context, JSValueRef value, double expectedValue)
{
double number = JSValueToNumber(context, value, nullptr);
if (number != expectedValue && !(isnan(number) && isnan(expectedValue))) {
fprintf(stderr, "assertEqualsAsNumber FAILED: %p, %lf\n", value, expectedValue);
return 1;
}
return 0;
}
static int testAccess(JSGlobalContextRef context, JSObjectRef typedArray, JSTypedArrayType type, unsigned elementLength, void* expectedPtr = nullptr, JSObjectRef expectedBuffer = nullptr, unsigned expectedOffset = 0)
{
JSValueRef exception = nullptr;
// Test typedArray basic functions.
JSTypedArrayType actualType = JSValueGetTypedArrayType(context, typedArray, &exception);
if (type != actualType || exception) {
fprintf(stderr, "TypedArray type FAILED: %p, got: %s, expected: %s\n", typedArray, typeToString[actualType], typeToString[type]);
return 1;
}
unsigned length = JSObjectGetTypedArrayLength(context, typedArray, &exception);
if (elementLength != length || exception) {
fprintf(stderr, "TypedArray length FAILED: %p (%s), got: %d, expected: %d\n", typedArray, typeToString[type], length, elementLength);
return 1;
}
unsigned byteLength = JSObjectGetTypedArrayByteLength(context, typedArray, &exception);
unsigned expectedLength = byteSizes[type] * elementLength;
if (byteLength != expectedLength || exception) {
fprintf(stderr, "TypedArray byteLength FAILED: %p (%s), got: %d, expected: %d\n", typedArray, typeToString[type], byteLength, expectedLength);
return 1;
}
unsigned offset = JSObjectGetTypedArrayByteOffset(context, typedArray, &exception);
if (expectedOffset != offset || exception) {
fprintf(stderr, "TypedArray byteOffset FAILED: %p (%s), got: %d, expected: %d\n", typedArray, typeToString[type], offset, expectedOffset);
return 1;
}
void* ptr = JSObjectGetTypedArrayBytesPtr(context, typedArray, &exception);
if (exception)
return unexpectedException("TypedArray get bytes ptr");
JSObjectRef buffer = JSObjectGetTypedArrayBuffer(context, typedArray, &exception);
if (exception)
return unexpectedException("TypedArray get buffer");
void* bufferPtr = JSObjectGetArrayBufferBytesPtr(context, buffer, &exception);
if (exception)
return unexpectedException("ArrayBuffer get bytes ptr");
if (bufferPtr != ptr) {
fprintf(stderr, "FAIL: TypedArray bytes ptr and ArrayBuffer byte ptr were not the same: %p (%s) TypedArray: %p, ArrayBuffer: %p\n", typedArray, typeToString[type], ptr, bufferPtr);
return 1;
}
if (expectedPtr && ptr != expectedPtr) {
fprintf(stderr, "FAIL: TypedArray bytes ptr and the ptr used to construct the array were not the same: %p (%s) TypedArray: %p, bytes ptr: %p\n", typedArray, typeToString[type], ptr, expectedPtr);
return 1;
}
if (expectedBuffer && expectedBuffer != buffer) {
fprintf(stderr, "FAIL: TypedArray buffer and the ArrayBuffer buffer used to construct the array were not the same: %p (%s) TypedArray buffer: %p, data: %p\n", typedArray, typeToString[type], buffer, expectedBuffer);
return 1;
}
return 0;
}
static int testConstructors(JSGlobalContextRef context, JSTypedArrayType type, unsigned length)
{
int failed = 0;
JSValueRef exception = nullptr;
JSObjectRef typedArray;
// Test create with length.
typedArray = JSObjectMakeTypedArray(context, type, length, &exception);
failed = failed || exception || testAccess(context, typedArray, type, length);
void* ptr = calloc(length, byteSizes[type]); // This is to be freed by data
JSObjectRef data = JSObjectMakeArrayBufferWithBytesNoCopy(context, ptr, length * byteSizes[type], freePtr, nullptr, &exception);
failed = failed || exception;
// Test create with existing ptr.
typedArray = JSObjectMakeTypedArrayWithBytesNoCopy(context, type, ptr, length * byteSizes[type], id, nullptr, &exception);
failed = failed || exception || testAccess(context, typedArray, type, length, ptr);
// Test create with existing ArrayBuffer.
typedArray = JSObjectMakeTypedArrayWithArrayBuffer(context, type, data, &exception);
failed = failed || exception || testAccess(context, typedArray, type, length, ptr, data);
// Test create with existing ArrayBuffer and offset.
typedArray = JSObjectMakeTypedArrayWithArrayBufferAndOffset(context, type, data, 0, length, &exception);
failed = failed || exception || testAccess(context, typedArray, type, length, ptr, data);
typedArray = JSObjectMakeTypedArrayWithArrayBufferAndOffset(context, type, data, byteSizes[type], length-1, &exception);
if (!length)
failed = failed || !exception;
else
failed = failed || testAccess(context, typedArray, type, length-1, ptr, data, byteSizes[type]) || exception;
exception = nullptr;
typedArray = JSObjectMakeTypedArrayWithArrayBufferAndOffset(context, type, data, byteSizes[type], 3, &exception);
if (length < 2)
failed = failed || !exception;
else
failed = failed || testAccess(context, typedArray, type, 3, ptr, data, byteSizes[type]) || exception;
if (byteSizes[type] > 1) {
exception = nullptr;
typedArray = JSObjectMakeTypedArrayWithArrayBufferAndOffset(context, type, data, 1, length-1, &exception);
failed = failed || !exception;
}
typedArray = JSObjectMakeTypedArrayWithArrayBufferAndOffset(context, type, data, byteSizes[type], length, &exception);
failed = failed || !exception;
exception = nullptr;
typedArray = JSObjectMakeTypedArrayWithArrayBufferAndOffset(context, type, data, byteSizes[type], 0, &exception);
if (!length)
failed = failed || !exception;
else
failed = failed || testAccess(context, typedArray, type, 0, ptr, data, byteSizes[type]) || exception;
return failed;
}
template <typename Functor>
static int forEachTypedArrayType(const Functor& functor)
{
int failed = 0;
for (unsigned i = 0; i < kJSTypedArrayTypeArrayBuffer; i++)
failed = failed || functor(static_cast<JSTypedArrayType>(i));
return failed;
}
int testTypedArrayCAPI()
{
int failed = 0;
JSGlobalContextRef context = JSGlobalContextCreate(nullptr);
failed = failed || forEachTypedArrayType([&](JSTypedArrayType type) {
int failed = 0;
for (unsigned i = 0; i < numLengths; i++)
failed = failed || testConstructors(context, type, lengths[i]);
return failed;
});
// Test making a typedArray from scratch length.
volatile JSObjectRef typedArray = JSObjectMakeTypedArray(context, kJSTypedArrayTypeUint32Array, 10, nullptr);
JSObjectRef data = JSObjectGetTypedArrayBuffer(context, typedArray, nullptr);
unsigned* buffer = static_cast<unsigned*>(JSObjectGetArrayBufferBytesPtr(context, data, nullptr));
ASSERT(JSObjectGetTypedArrayLength(context, typedArray, nullptr) == 10);
// Test buffer is connected to typedArray.
buffer[1] = 1;
JSValueRef v = JSObjectGetPropertyAtIndex(context, typedArray, 1, nullptr);
failed = failed || assertEqualsAsNumber(context, v, 1);
// Test passing a buffer from a new array to an old array
typedArray = JSObjectMakeTypedArrayWithBytesNoCopy(context, kJSTypedArrayTypeUint32Array, buffer, 40, id, nullptr, nullptr);
buffer = static_cast<unsigned*>(JSObjectGetTypedArrayBytesPtr(context, typedArray, nullptr));
ASSERT(buffer[1] == 1);
buffer[1] = 20;
ASSERT(((unsigned*)JSObjectGetArrayBufferBytesPtr(context, data, nullptr))[1] == 20);
// Test constructing with data and the data returned are the same even with an offset.
typedArray = JSObjectMakeTypedArrayWithArrayBufferAndOffset(context, kJSTypedArrayTypeUint32Array, data, 4, 9, nullptr);
failed = failed || assertEqualsAsNumber(context, JSObjectGetPropertyAtIndex(context, typedArray, 0, nullptr), 20);
ASSERT(data == JSObjectGetTypedArrayBuffer(context, typedArray, nullptr));
// Test attempting to allocate an array too big for memory.
forEachTypedArrayType([&](JSTypedArrayType type) {
JSValueRef exception = nullptr;
JSObjectMakeTypedArray(context, type, UINT_MAX, &exception);
return !exception;
});
JSGlobalContextRelease(context);
if (!failed)
printf("PASS: Typed Array C API Tests.\n");
else
printf("FAIL: Some Typed Array C API Tests failed.\n");
return failed;
}