Skip to content

Commit a024eb2

Browse files
authored
Add allocate/free callbacks to ArrayBuffers (#4801)
Larger buffer allocations will throw error instead of calling jerry_fatal. JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg [email protected]
1 parent d2388e9 commit a024eb2

19 files changed

+1350
-701
lines changed

docs/02.API-REFERENCE.md

Lines changed: 333 additions & 23 deletions
Large diffs are not rendered by default.

jerry-core/api/jerry.c

Lines changed: 146 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5923,7 +5923,8 @@ jerry_value_is_arraybuffer (const jerry_value_t value) /**< value to check if it
59235923
* @return value of the constructed ArrayBuffer object
59245924
*/
59255925
jerry_value_t
5926-
jerry_create_arraybuffer (const jerry_length_t size) /**< size of the ArrayBuffer to create */
5926+
jerry_create_arraybuffer (const jerry_length_t size) /**< size of the backing store allocated
5927+
* for the array buffer in bytes */
59275928
{
59285929
jerry_assert_api_available ();
59295930

@@ -5944,32 +5945,41 @@ jerry_create_arraybuffer (const jerry_length_t size) /**< size of the ArrayBuffe
59445945
* * if the typed arrays are disabled this will return a TypeError.
59455946
* * if the size is zero or buffer_p is a null pointer this will return an empty ArrayBuffer.
59465947
*
5947-
* @return value of the construced ArrayBuffer object
5948+
* @return value of the newly construced array buffer object
59485949
*/
59495950
jerry_value_t
5950-
jerry_create_arraybuffer_external (const jerry_length_t size, /**< size of the buffer to used */
5951-
uint8_t *buffer_p, /**< buffer to use as the ArrayBuffer's backing */
5952-
jerry_value_free_callback_t free_cb) /**< buffer free callback */
5951+
jerry_create_arraybuffer_external (const jerry_length_t size, /**< size of the buffer in bytes */
5952+
uint8_t *buffer_p, /**< the backing store used by the array buffer object */
5953+
void *arraybuffer_user_p) /**< user pointer assigned to the array buffer object */
59535954
{
59545955
jerry_assert_api_available ();
59555956

59565957
#if JERRY_BUILTIN_TYPEDARRAY
5957-
ecma_object_t *arraybuffer;
5958+
ecma_object_t *arraybuffer_p;
59585959

5959-
if (JERRY_UNLIKELY (size == 0 || buffer_p == NULL))
5960+
if (JERRY_UNLIKELY (size == 0))
59605961
{
5961-
arraybuffer = ecma_arraybuffer_new_object (0);
5962+
arraybuffer_p = ecma_arraybuffer_new_object (0);
59625963
}
59635964
else
59645965
{
5965-
arraybuffer = ecma_arraybuffer_new_object_external (size, buffer_p, free_cb);
5966+
arraybuffer_p = ecma_arraybuffer_create_object_with_buffer (ECMA_OBJECT_CLASS_ARRAY_BUFFER, size);
5967+
5968+
ecma_arraybuffer_pointer_t *arraybuffer_pointer_p = (ecma_arraybuffer_pointer_t *) arraybuffer_p;
5969+
arraybuffer_pointer_p->arraybuffer_user_p = arraybuffer_user_p;
5970+
5971+
if (buffer_p != NULL)
5972+
{
5973+
arraybuffer_pointer_p->extended_object.u.cls.u1.array_buffer_flags |= ECMA_ARRAYBUFFER_ALLOCATED;
5974+
arraybuffer_pointer_p->buffer_p = buffer_p;
5975+
}
59665976
}
59675977

5968-
return jerry_return (ecma_make_object_value (arraybuffer));
5978+
return jerry_return (ecma_make_object_value (arraybuffer_p));
59695979
#else /* !JERRY_BUILTIN_TYPEDARRAY */
59705980
JERRY_UNUSED (size);
59715981
JERRY_UNUSED (buffer_p);
5972-
JERRY_UNUSED (free_cb);
5982+
JERRY_UNUSED (arraybuffer_user_p);
59735983
return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_typed_array_not_supported_p)));
59745984
#endif /* JERRY_BUILTIN_TYPEDARRAY */
59755985
} /* jerry_create_arraybuffer_external */
@@ -5999,7 +6009,8 @@ jerry_value_is_shared_arraybuffer (const jerry_value_t value) /**< value to chec
59996009
* @return value of the constructed SharedArrayBuffer object
60006010
*/
60016011
jerry_value_t
6002-
jerry_create_shared_arraybuffer (const jerry_length_t size) /**< size of the SharedArrayBuffer to create */
6012+
jerry_create_shared_arraybuffer (const jerry_length_t size) /**< size of the backing store allocated
6013+
* for the shared array buffer in bytes */
60036014
{
60046015
jerry_assert_api_available ();
60056016

@@ -6020,32 +6031,43 @@ jerry_create_shared_arraybuffer (const jerry_length_t size) /**< size of the Sha
60206031
* * if the typed arrays are disabled this will return a TypeError.
60216032
* * if the size is zero or buffer_p is a null pointer this will return an empty SharedArrayBuffer.
60226033
*
6023-
* @return value of the construced SharedArrayBuffer object
6034+
* @return value of the newly construced shared array buffer object
60246035
*/
60256036
jerry_value_t
6026-
jerry_create_shared_arraybuffer_external (const jerry_length_t size, /**< size of the buffer to used */
6027-
uint8_t *buffer_p, /**< buffer to use as the SharedArrayBuffer's backing */
6028-
jerry_value_free_callback_t free_cb) /**< buffer free callback */
6037+
jerry_create_shared_arraybuffer_external (const jerry_length_t size, /**< size of the buffer in bytes */
6038+
uint8_t *buffer_p, /**< the backing store used by the
6039+
* shared array buffer object */
6040+
void *arraybuffer_user_p) /**< user pointer assigned to the
6041+
* shared array buffer object */
60296042
{
60306043
jerry_assert_api_available ();
60316044

60326045
#if JERRY_BUILTIN_SHAREDARRAYBUFFER
6033-
ecma_object_t *shared_arraybuffer;
6046+
ecma_object_t *shared_arraybuffer_p;
60346047

6035-
if (JERRY_UNLIKELY (size == 0 || buffer_p == NULL))
6048+
if (JERRY_UNLIKELY (size == 0))
60366049
{
6037-
shared_arraybuffer = ecma_shared_arraybuffer_new_object (0);
6050+
shared_arraybuffer_p = ecma_shared_arraybuffer_new_object (0);
60386051
}
60396052
else
60406053
{
6041-
shared_arraybuffer = ecma_shared_arraybuffer_new_object_external (size, buffer_p, free_cb);
6054+
shared_arraybuffer_p = ecma_arraybuffer_create_object_with_buffer (ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER, size);
6055+
6056+
ecma_arraybuffer_pointer_t *shared_arraybuffer_pointer_p = (ecma_arraybuffer_pointer_t *) shared_arraybuffer_p;
6057+
shared_arraybuffer_pointer_p->arraybuffer_user_p = arraybuffer_user_p;
6058+
6059+
if (buffer_p != NULL)
6060+
{
6061+
shared_arraybuffer_pointer_p->extended_object.u.cls.u1.array_buffer_flags |= ECMA_ARRAYBUFFER_ALLOCATED;
6062+
shared_arraybuffer_pointer_p->buffer_p = buffer_p;
6063+
}
60426064
}
60436065

6044-
return jerry_return (ecma_make_object_value (shared_arraybuffer));
6066+
return jerry_return (ecma_make_object_value (shared_arraybuffer_p));
60456067
#else /* !JERRY_BUILTIN_SHAREDARRAYBUFFER */
60466068
JERRY_UNUSED (size);
60476069
JERRY_UNUSED (buffer_p);
6048-
JERRY_UNUSED (free_cb);
6070+
JERRY_UNUSED (arraybuffer_user_p);
60496071
return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_shared_arraybuffer_not_supported_p)));
60506072
#endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */
60516073
} /* jerry_create_shared_arraybuffer_external */
@@ -6073,6 +6095,13 @@ jerry_arraybuffer_write (const jerry_value_t value, /**< target ArrayBuffer or S
60736095
}
60746096

60756097
ecma_object_t *buffer_p = ecma_get_object_from_value (value);
6098+
6099+
if (ECMA_ARRAYBUFFER_CHECK_BUFFER_ERROR (buffer_p))
6100+
{
6101+
jerry_release_value (jcontext_take_exception ());
6102+
return 0;
6103+
}
6104+
60766105
jerry_length_t length = ecma_arraybuffer_get_length (buffer_p);
60776106

60786107
if (offset >= length)
@@ -6122,6 +6151,13 @@ jerry_arraybuffer_read (const jerry_value_t value, /**< ArrayBuffer or SharedArr
61226151
}
61236152

61246153
ecma_object_t *buffer_p = ecma_get_object_from_value (value);
6154+
6155+
if (ECMA_ARRAYBUFFER_CHECK_BUFFER_ERROR (buffer_p))
6156+
{
6157+
jerry_release_value (jcontext_take_exception ());
6158+
return 0;
6159+
}
6160+
61256161
jerry_length_t length = ecma_arraybuffer_get_length (buffer_p);
61266162

61276163
if (offset >= length)
@@ -6191,15 +6227,19 @@ jerry_get_arraybuffer_pointer (const jerry_value_t array_buffer) /**< Array Buff
61916227
jerry_assert_api_available ();
61926228

61936229
#if JERRY_BUILTIN_TYPEDARRAY
6194-
if (ecma_is_value_error_reference (array_buffer)
6195-
|| !(ecma_is_arraybuffer (array_buffer) || ecma_is_shared_arraybuffer (array_buffer)))
6230+
if (!(ecma_is_arraybuffer (array_buffer) || ecma_is_shared_arraybuffer (array_buffer)))
61966231
{
61976232
return NULL;
61986233
}
61996234

62006235
ecma_object_t *buffer_p = ecma_get_object_from_value (array_buffer);
6201-
lit_utf8_byte_t *mem_buffer_p = ecma_arraybuffer_get_buffer (buffer_p);
6202-
return (uint8_t *const) mem_buffer_p;
6236+
6237+
if (!(ECMA_ARRAYBUFFER_GET_FLAGS (buffer_p) & ECMA_ARRAYBUFFER_ALLOCATED))
6238+
{
6239+
return NULL;
6240+
}
6241+
6242+
return (uint8_t *) ecma_arraybuffer_get_buffer (buffer_p);
62036243
#else /* !JERRY_BUILTIN_TYPEDARRAY */
62046244
JERRY_UNUSED (array_buffer);
62056245
#endif /* JERRY_BUILTIN_TYPEDARRAY */
@@ -6222,7 +6262,7 @@ jerry_is_arraybuffer_detachable (const jerry_value_t value) /**< ArrayBuffer */
62226262
if (ecma_is_arraybuffer (value))
62236263
{
62246264
ecma_object_t *buffer_p = ecma_get_object_from_value (value);
6225-
return ecma_arraybuffer_is_detached (buffer_p) ? ECMA_VALUE_FALSE : ECMA_VALUE_TRUE;
6265+
return ecma_make_boolean_value (!ecma_arraybuffer_is_detached (buffer_p));
62266266
}
62276267
#else /* !JERRY_BUILTIN_TYPEDARRAY */
62286268
JERRY_UNUSED (value);
@@ -6233,8 +6273,8 @@ jerry_is_arraybuffer_detachable (const jerry_value_t value) /**< ArrayBuffer */
62336273
/**
62346274
* Detach the underlying data block from ArrayBuffer and set its bytelength to 0.
62356275
*
6236-
* Note: If the ArrayBuffer has been created with `jerry_create_arraybuffer_external`
6237-
* the optional free callback is called on a successful detach operation
6276+
* Note: if the ArrayBuffer has a separate data buffer, the free callback set by
6277+
* jerry_arraybuffer_set_allocation_callbacks is called for this buffer
62386278
*
62396279
* @return null value - if success
62406280
* value marked with error flag - otherwise
@@ -6260,6 +6300,83 @@ jerry_detach_arraybuffer (const jerry_value_t value) /**< ArrayBuffer */
62606300
return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Expected an ArrayBuffer")));
62616301
} /* jerry_detach_arraybuffer */
62626302

6303+
/**
6304+
* Checks whether a buffer is currently allocated for an array buffer or typed array.
6305+
*
6306+
* @return true, if a buffer is allocated for an array buffer or typed array
6307+
* false, otherwise
6308+
*/
6309+
bool
6310+
jerry_arraybuffer_has_buffer (const jerry_value_t value) /**< array buffer or typed array value */
6311+
{
6312+
jerry_assert_api_available ();
6313+
6314+
#if JERRY_BUILTIN_TYPEDARRAY
6315+
if (!ecma_is_value_object (value))
6316+
{
6317+
return false;
6318+
}
6319+
6320+
ecma_object_t *object_p = ecma_get_object_from_value (value);
6321+
6322+
if (ecma_object_is_typedarray (object_p))
6323+
{
6324+
object_p = ecma_typedarray_get_arraybuffer (object_p);
6325+
}
6326+
else if (!(ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER)
6327+
|| ecma_object_is_shared_arraybuffer (object_p)))
6328+
{
6329+
return false;
6330+
}
6331+
6332+
return (ECMA_ARRAYBUFFER_GET_FLAGS (object_p) & ECMA_ARRAYBUFFER_ALLOCATED) != 0;
6333+
#else /* !JERRY_BUILTIN_TYPEDARRAY */
6334+
JERRY_UNUSED (value);
6335+
return false;
6336+
#endif /* JERRY_BUILTIN_TYPEDARRAY */
6337+
} /* jerry_arraybuffer_has_buffer */
6338+
6339+
/**
6340+
* Array buffers which size is less or equal than the limit passed to this function are allocated in
6341+
* a single memory block. The allocator callbacks set by jerry_arraybuffer_set_allocation_callbacks
6342+
* are not called for these array buffers. The default limit is 256 bytes.
6343+
*/
6344+
void
6345+
jerry_arraybuffer_set_compact_allocation_limit (const jerry_length_t allocation_limit) /**< maximum size of
6346+
* compact allocation */
6347+
{
6348+
jerry_assert_api_available ();
6349+
6350+
#if JERRY_BUILTIN_TYPEDARRAY
6351+
JERRY_CONTEXT (arraybuffer_compact_allocation_limit) = allocation_limit;
6352+
#else /* !JERRY_BUILTIN_TYPEDARRAY */
6353+
JERRY_UNUSED (allocation_limit);
6354+
#endif /* JERRY_BUILTIN_TYPEDARRAY */
6355+
} /* jerry_arraybuffer_set_compact_allocation_limit */
6356+
6357+
/**
6358+
* Set callbacks for allocating and freeing backing stores for array buffer objects.
6359+
*/
6360+
void
6361+
jerry_arraybuffer_set_allocator_callbacks (jerry_arraybuffer_allocate_t allocate_callback, /**< callback for allocating
6362+
* array buffer memory */
6363+
jerry_arraybuffer_free_t free_callback, /**< callback for freeing
6364+
* array buffer memory */
6365+
void *user_p) /**< user pointer passed to the callbacks */
6366+
{
6367+
jerry_assert_api_available ();
6368+
6369+
#if JERRY_BUILTIN_TYPEDARRAY
6370+
JERRY_CONTEXT (arraybuffer_allocate_callback) = allocate_callback;
6371+
JERRY_CONTEXT (arraybuffer_free_callback) = free_callback;
6372+
JERRY_CONTEXT (arraybuffer_allocate_callback_user_p) = user_p;
6373+
#else /* !JERRY_BUILTIN_TYPEDARRAY */
6374+
JERRY_UNUSED (allocate_callback);
6375+
JERRY_UNUSED (free_callback);
6376+
JERRY_UNUSED (user_p);
6377+
#endif /* JERRY_BUILTIN_TYPEDARRAY */
6378+
} /* jerry_arraybuffer_set_allocator_callbacks */
6379+
62636380
/**
62646381
* DataView related functions
62656382
*/

jerry-core/ecma/base/ecma-gc.c

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "ecma-alloc.h"
2121
#include "ecma-array-object.h"
22+
#include "ecma-arraybuffer-object.h"
2223
#include "ecma-builtin-handlers.h"
2324
#include "ecma-container-object.h"
2425
#include "ecma-function-object.h"
@@ -1854,25 +1855,18 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */
18541855
case ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER:
18551856
#endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */
18561857
{
1857-
uint32_t arraybuffer_length = ext_object_p->u.cls.u3.length;
1858-
1859-
if (ECMA_ARRAYBUFFER_HAS_EXTERNAL_MEMORY (ext_object_p))
1858+
if (!(ECMA_ARRAYBUFFER_GET_FLAGS (ext_object_p) & ECMA_ARRAYBUFFER_HAS_POINTER))
18601859
{
1861-
ext_object_size = sizeof (ecma_arraybuffer_external_info);
1860+
ext_object_size += ext_object_p->u.cls.u3.length;
1861+
break;
1862+
}
18621863

1863-
/* Call external free callback if any. */
1864-
ecma_arraybuffer_external_info *array_p = (ecma_arraybuffer_external_info *) ext_object_p;
1864+
ext_object_size = sizeof (ecma_arraybuffer_pointer_t);
18651865

1866-
if (array_p->free_cb != NULL)
1867-
{
1868-
array_p->free_cb (array_p->buffer_p);
1869-
}
1870-
}
1871-
else
1866+
if (ECMA_ARRAYBUFFER_GET_FLAGS (ext_object_p) & ECMA_ARRAYBUFFER_ALLOCATED)
18721867
{
1873-
ext_object_size += arraybuffer_length;
1868+
ecma_arraybuffer_release_buffer (object_p);
18741869
}
1875-
18761870
break;
18771871
}
18781872
#endif /* JERRY_BUILTIN_TYPEDARRAY */

jerry-core/ecma/base/ecma-globals.h

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2084,35 +2084,24 @@ typedef enum
20842084
} ecma_typedarray_flag_t;
20852085

20862086
/**
2087-
* ArrayBuffers flags.
2087+
* Array buffer flags.
20882088
*/
20892089
typedef enum
20902090
{
2091-
ECMA_ARRAYBUFFER_INTERNAL_MEMORY = 0u, /* ArrayBuffer memory is handled internally. */
2092-
ECMA_ARRAYBUFFER_EXTERNAL_MEMORY = (1u << 0), /* ArrayBuffer created via jerry_create_arraybuffer_external. */
2093-
ECMA_ARRAYBUFFER_DETACHED = (1u << 1), /* ArrayBuffer has been detached */
2091+
ECMA_ARRAYBUFFER_HAS_POINTER = (1u << 0), /* ArrayBuffer has a buffer pointer. */
2092+
ECMA_ARRAYBUFFER_ALLOCATED = (1u << 1), /* ArrayBuffer memory is allocated */
2093+
ECMA_ARRAYBUFFER_DETACHED = (1u << 2), /* ArrayBuffer has been detached */
20942094
} ecma_arraybuffer_flag_t;
20952095

20962096
/**
2097-
* Check whether the ArrayBuffer has external underlying buffer
2098-
*/
2099-
#define ECMA_ARRAYBUFFER_HAS_EXTERNAL_MEMORY(object_p) \
2100-
((((ecma_extended_object_t *) object_p)->u.cls.u1.array_buffer_flags & ECMA_ARRAYBUFFER_EXTERNAL_MEMORY) != 0)
2101-
2102-
/**
2103-
* Struct to store information for ArrayBuffers with external memory.
2104-
*
2105-
* The following elements are stored in Jerry memory.
2106-
*
2107-
* buffer_p - pointer to the external memory.
2108-
* free_cb - pointer to a callback function which is called when the ArrayBuffer is freed.
2097+
* Structure for array buffers with a backing store pointer.
21092098
*/
21102099
typedef struct
21112100
{
21122101
ecma_extended_object_t extended_object; /**< extended object part */
2113-
void *buffer_p; /**< external buffer pointer */
2114-
jerry_value_free_callback_t free_cb; /**< the free callback for the above buffer pointer */
2115-
} ecma_arraybuffer_external_info;
2102+
void *buffer_p; /**< pointer to the backing store of the array buffer object */
2103+
void *arraybuffer_user_p; /**< user pointer passed to the free callback */
2104+
} ecma_arraybuffer_pointer_t;
21162105

21172106
/**
21182107
* Some internal properties of TypedArray object.
@@ -2132,10 +2121,6 @@ typedef struct
21322121
typedef struct
21332122
{
21342123
ecma_object_t *array_buffer_p; /**< pointer to the typedArray's [[ViewedArrayBuffer]] internal slot */
2135-
lit_utf8_byte_t *buffer_p; /**< pointer to the underlying raw data buffer.
2136-
* Note:
2137-
* - This address is increased by the [ByteOffset]] internal property.
2138-
* - This address must be used during indexed read/write operation. */
21392124
ecma_typedarray_type_t id; /**< [[TypedArrayName]] internal slot */
21402125
uint32_t length; /**< [[ByteLength]] internal slot */
21412126
uint32_t offset; /**< [[ByteOffset]] internal slot. */

jerry-core/ecma/base/ecma-init-finalize.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ ecma_init (void)
6363
#if JERRY_ESNEXT
6464
JERRY_CONTEXT (current_new_target_p) = NULL;
6565
#endif /* JERRY_ESNEXT */
66+
67+
#if JERRY_BUILTIN_TYPEDARRAY
68+
JERRY_CONTEXT (arraybuffer_compact_allocation_limit) = 256;
69+
#endif /* JERRY_BUILTIN_TYPEDARRAY */
6670
} /* ecma_init */
6771

6872
/**

0 commit comments

Comments
 (0)