Skip to content

Commit 87c9cf9

Browse files
committed
[GLib] Expose ArrayBuffer in the public API
https://bugs.webkit.org/show_bug.cgi?id=237088 Reviewed by Carlos Garcia Campos. This adds a set of new functions to operate on JSCValue objects which refer to array buffers in the JS side of the world. This allows sharing chunks of memory buffers efficiently with native code, without needing to copy nor encode data back and forth. Source/JavaScriptCore: * API/glib/JSCValue.cpp: (jscArrayBufferDeallocate): (jsc_value_new_array_buffer): (jsc_value_is_array_buffer): (jsc_value_array_buffer_get_data): (jsc_value_array_buffer_get_length): * API/glib/JSCValue.h: * API/glib/docs/jsc-glib-4.0-sections.txt: Tools: * TestWebKitAPI/Tests/JavaScriptCore/glib/TestJSC.cpp: Added test for array buffers. (testJSCArrayBuffer): (main): Canonical link: https://commits.webkit.org/248350@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@291194 268f45cc-cd09-0410-ab3c-d52691b4dbfc
1 parent 7f4060e commit 87c9cf9

6 files changed

Lines changed: 384 additions & 0 deletions

File tree

‎Source/JavaScriptCore/API/glib/JSCValue.cpp‎

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@
2222

2323
#include "APICast.h"
2424
#include "APIUtils.h"
25+
#include "JSArrayBuffer.h"
2526
#include "JSCCallbackFunction.h"
2627
#include "JSCClassPrivate.h"
2728
#include "JSCContextPrivate.h"
2829
#include "JSCInlines.h"
2930
#include "JSCValuePrivate.h"
3031
#include "JSRetainPtr.h"
32+
#include "JSTypedArray.h"
3133
#include "LiteralParser.h"
3234
#include "OpaqueJSString.h"
3335
#include <gobject/gvaluecollector.h>
@@ -1461,6 +1463,183 @@ JSCValue* jsc_value_constructor_callv(JSCValue* value, unsigned parametersCount,
14611463
return jscContextGetOrCreateValue(priv->context.get(), result).leakRef();
14621464
}
14631465

1466+
struct ArrayBufferDeallocatorContext {
1467+
gpointer userData;
1468+
GDestroyNotify destroyNotify;
1469+
};
1470+
WEBKIT_DEFINE_ASYNC_DATA_STRUCT(ArrayBufferDeallocatorContext)
1471+
1472+
/**
1473+
* jsc_value_new_array_buffer:
1474+
* @context: A #JSCContext
1475+
* @data: Pointer to a region of memory.
1476+
* @size: Size in bytes of the memory region.
1477+
* @destroy_notify: (nullable): destroy notifier for @user_data.
1478+
* @user_data: (closure): user data.
1479+
*
1480+
* Creates a new %ArrayBuffer from existing @data in memory.
1481+
*
1482+
* The @data is not copied: while this allows sharing data with JavaScript
1483+
* efficiently, the caller must ensure that the memory region remains valid
1484+
* until the newly created object is released by JSC.
1485+
*
1486+
* Optionally, a @destroy_notify callback can be provided, which will be
1487+
* invoked with @user_data as parameter when the %ArrayBuffer object is
1488+
* released. This is intended to be used for freeing resources related to
1489+
* the memory region which contains the data:
1490+
*
1491+
* |[!<-- language="C" -->
1492+
* GMappedFile *f = g_mapped_file_new (file_path, TRUE, NULL);
1493+
* JSCValue *value = jsc_value_new_array_buffer (context,
1494+
* g_mapped_file_get_contents (f), g_mapped_file_get_length (f),
1495+
* (GDestroyNotify) g_mapped_file_unref, f);
1496+
* ]|
1497+
*
1498+
* Note that the @user_data can be the same value as @data:
1499+
*
1500+
* |[!<-- language="C" -->
1501+
* void *bytes = g_malloc0 (100);
1502+
* JSCValue *value = jsc_value_new_array_buffer (context, bytes, 100, g_free, bytes);
1503+
* ]|
1504+
*
1505+
* Returns: (transfer full) (nullable): A #JSCValue, or %NULL in case of exception.
1506+
*
1507+
* Since: 2.38
1508+
*/
1509+
JSCValue* jsc_value_new_array_buffer(JSCContext* context, void* data, size_t length, GDestroyNotify destroyNotify, gpointer userData)
1510+
{
1511+
g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
1512+
1513+
ArrayBufferDeallocatorContext* deallocatorContext = nullptr;
1514+
if (destroyNotify) {
1515+
deallocatorContext = createArrayBufferDeallocatorContext();
1516+
deallocatorContext->destroyNotify = destroyNotify;
1517+
deallocatorContext->userData = userData;
1518+
}
1519+
1520+
JSValueRef exception = nullptr;
1521+
auto* jsContext = jscContextGetJSContext(context);
1522+
auto* jsArrayBuffer = JSObjectMakeArrayBufferWithBytesNoCopy(jsContext, data, length, [](void*, void* deallocatorContext) {
1523+
if (deallocatorContext) {
1524+
auto* context = static_cast<ArrayBufferDeallocatorContext*>(deallocatorContext);
1525+
context->destroyNotify(context->userData);
1526+
destroyArrayBufferDeallocatorContext(context);
1527+
}
1528+
}, deallocatorContext, &exception);
1529+
1530+
if (jscContextHandleExceptionIfNeeded(context, exception))
1531+
return nullptr;
1532+
1533+
return jscContextGetOrCreateValue(context, jsArrayBuffer).leakRef();
1534+
}
1535+
1536+
/**
1537+
* jsc_value_is_array_buffer:
1538+
* @value: A #JSCValue.
1539+
*
1540+
* Check whether the @value is an %ArrayBuffer.
1541+
*
1542+
* Returns: whether the value is an %ArrayBuffer
1543+
*
1544+
* Since: 2.38
1545+
*/
1546+
gboolean jsc_value_is_array_buffer(JSCValue* value)
1547+
{
1548+
g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
1549+
1550+
using namespace JSC;
1551+
1552+
JSGlobalObject* globalObject = toJS(jscContextGetJSContext(value->priv->context.get()));
1553+
VM& vm = globalObject->vm();
1554+
JSLockHolder locker(vm);
1555+
1556+
JSValue jsValue = toJS(globalObject, value->priv->jsValue);
1557+
if (!jsValue.isObject())
1558+
return FALSE;
1559+
1560+
return !!jsDynamicCast<JSArrayBuffer*>(vm, jsValue.getObject());
1561+
}
1562+
1563+
/**
1564+
* jsc_value_array_buffer_get_data:
1565+
* @value: A #JSCValue
1566+
* @size: (nullable): location where to store the size of the memory region.
1567+
*
1568+
* Gets a pointer to memory that contains the array buffer data.
1569+
*
1570+
* Obtains a pointer to the memory region that holds the contents of the
1571+
* %ArrayBuffer; modifications done to the data will be visible to JavaScript
1572+
* code. If @size is not %NULL, the size in bytes of the memory region
1573+
* will also be stored in the pointed location.
1574+
*
1575+
* Note that the pointer returned by this function is not guaranteed to remain
1576+
* the same after calls to other JSC API functions. If you plan to access the
1577+
* data of the %ArrayBuffer later, you can keep a reference to the @value and
1578+
* obtain the data pointer at a later point. Keep in mind that if JavaScript
1579+
* code has a chance to run, for example due to main loop events that result
1580+
* in JSC being called, the contents of the memory region might be modified in
1581+
* the meantime. Consider taking a copy of the data and using the copy instead
1582+
* in asynchronous code.
1583+
*
1584+
* Returns: (transfer none): pointer to memory.
1585+
*
1586+
* Since: 2.38
1587+
*/
1588+
gpointer jsc_value_array_buffer_get_data(JSCValue* value, gsize* size)
1589+
{
1590+
g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
1591+
1592+
auto* jsContext = jscContextGetJSContext(value->priv->context.get());
1593+
1594+
JSValueRef exception = nullptr;
1595+
auto* jsObject = JSValueToObject(jsContext, value->priv->jsValue, &exception);
1596+
if (jscContextHandleExceptionIfNeeded(value->priv->context.get(), exception))
1597+
return nullptr;
1598+
1599+
void* data = JSObjectGetArrayBufferBytesPtr(jsContext, jsObject, &exception);
1600+
if (jscContextHandleExceptionIfNeeded(value->priv->context.get(), exception))
1601+
return nullptr;
1602+
1603+
if (size) {
1604+
*size = JSObjectGetArrayBufferByteLength(jsContext, jsObject, &exception);
1605+
if (jscContextHandleExceptionIfNeeded(value->priv->context.get(), exception))
1606+
return nullptr;
1607+
}
1608+
1609+
return data;
1610+
}
1611+
1612+
/**
1613+
* jsc_value_array_buffer_get_size:
1614+
* @value: A #JSCValue
1615+
*
1616+
* Gets the size of the array buffer.
1617+
*
1618+
* Obtains the size in bytes of the memory region that holds the contents of
1619+
* an %ArrayBuffer.
1620+
*
1621+
* Returns: size, in bytes.
1622+
*
1623+
* Since: 2.38
1624+
*/
1625+
gsize jsc_value_array_buffer_get_size(JSCValue* value)
1626+
{
1627+
g_return_val_if_fail(JSC_IS_VALUE(value), 0);
1628+
1629+
auto* jsContext = jscContextGetJSContext(value->priv->context.get());
1630+
1631+
JSValueRef exception = nullptr;
1632+
auto* jsObject = JSValueToObject(jsContext, value->priv->jsValue, &exception);
1633+
if (jscContextHandleExceptionIfNeeded(value->priv->context.get(), exception))
1634+
return 0;
1635+
1636+
size_t size = JSObjectGetArrayBufferByteLength(jsContext, jsObject, &exception);
1637+
if (jscContextHandleExceptionIfNeeded(value->priv->context.get(), exception))
1638+
return 0;
1639+
1640+
return size;
1641+
}
1642+
14641643
/**
14651644
* jsc_value_new_from_json:
14661645
* @context: a #JSCContext

‎Source/JavaScriptCore/API/glib/JSCValue.h‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,22 @@ JSC_API JSCValue *
246246
jsc_value_function_callv (JSCValue *value,
247247
guint n_parameters,
248248
JSCValue **parameters) G_GNUC_WARN_UNUSED_RESULT;
249+
JSC_API JSCValue *
250+
jsc_value_new_array_buffer (JSCContext *context,
251+
gpointer data,
252+
gsize size,
253+
GDestroyNotify destroy_notify,
254+
gpointer user_data);
255+
256+
JSC_API gboolean
257+
jsc_value_is_array_buffer (JSCValue *value);
258+
259+
JSC_API gpointer
260+
jsc_value_array_buffer_get_data (JSCValue *value,
261+
gsize *size);
262+
263+
JSC_API gsize
264+
jsc_value_array_buffer_get_size (JSCValue *value);
249265

250266
JSC_API gboolean
251267
jsc_value_is_constructor (JSCValue *value);

‎Source/JavaScriptCore/API/glib/docs/jsc-glib-4.0-sections.txt‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ jsc_value_new_function_variadic
110110
jsc_value_is_function
111111
jsc_value_function_call
112112
jsc_value_function_callv
113+
jsc_value_new_array_buffer
114+
jsc_value_is_array_buffer
115+
jsc_value_array_buffer_get_data
116+
jsc_value_array_buffer_get_size
113117
jsc_value_is_constructor
114118
jsc_value_constructor_call
115119
jsc_value_constructor_callv

‎Source/JavaScriptCore/ChangeLog‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
2022-03-11 Adrian Perez de Castro <aperez@igalia.com>
2+
3+
[GLib] Expose ArrayBuffer in the public API
4+
https://bugs.webkit.org/show_bug.cgi?id=237088
5+
6+
Reviewed by Carlos Garcia Campos.
7+
8+
This adds a set of new functions to operate on JSCValue objects which refer to array
9+
buffers in the JS side of the world. This allows sharing chunks of memory buffers
10+
efficiently with native code, without needing to copy nor encode data back and forth.
11+
12+
* API/glib/JSCValue.cpp:
13+
(jscArrayBufferDeallocate):
14+
(jsc_value_new_array_buffer):
15+
(jsc_value_is_array_buffer):
16+
(jsc_value_array_buffer_get_data):
17+
(jsc_value_array_buffer_get_length):
18+
* API/glib/JSCValue.h:
19+
* API/glib/docs/jsc-glib-4.0-sections.txt:
20+
121
2022-03-11 Mikhail R. Gadelha <mikhail@igalia.com>
222

323
Debug build failure after r246172: ASSERT_UNDER_CONSTEXPR_CONTEXT should work in constexpr contexts

‎Tools/ChangeLog‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
2022-03-11 Adrian Perez de Castro <aperez@igalia.com>
2+
3+
[GLib] Expose ArrayBuffer in the public API
4+
https://bugs.webkit.org/show_bug.cgi?id=237088
5+
6+
Reviewed by Carlos Garcia Campos.
7+
8+
This adds a set of new functions to operate on JSCValue objects which refer to array
9+
buffers in the JS side of the world. This allows sharing chunks of memory buffers
10+
efficiently with native code, without needing to copy nor encode data back and forth.
11+
12+
* TestWebKitAPI/Tests/JavaScriptCore/glib/TestJSC.cpp: Added test for array buffers.
13+
(testJSCArrayBuffer):
14+
(main):
15+
116
2022-03-11 Jonathan Bedard <jbedard@apple.com>
217

318
[Merge-Queue] Add queue triggered by label addition

0 commit comments

Comments
 (0)