Browse Source

Merge branch 'api-push-buffer-object'

Add duk_push_buffer_object() API for pushing buffer objects (e.g.
Node.js Buffer, ArrayBuffer) and buffer views (e.g. Uint32Array)
directly from C code.
cyclic-jx-gh331
Sami Vaarala 9 years ago
parent
commit
cd1d0ab1a3
  1. 3
      RELEASES.rst
  2. 2
      src/duk_api_internal.h
  3. 17
      src/duk_api_public.h.in
  4. 102
      src/duk_api_stack.c
  5. 94
      src/duk_bi_buffer.c
  6. 422
      tests/api/test-push-buffer-object.c
  7. 1
      website/api/duk_get_buffer_data.yaml
  8. 65
      website/api/duk_push_buffer_object.yaml
  9. 1
      website/api/duk_require_buffer.yaml

3
RELEASES.rst

@ -1010,6 +1010,9 @@ Planned
* Add duk_get_buffer_data() and duk_require_buffer_data() API calls which
accept both plain buffer and buffer object values (GH-190)
* Add duk_push_buffer_object() which allows pushing of all buffer object and
buffer view types (GH-190)
* Add explicit 'this' binding for C eval calls so that strict eval code also
gets 'this' bound to the global object (GH-164)

2
src/duk_api_internal.h

@ -142,7 +142,7 @@ DUK_INTERNAL_DECL void duk_push_c_function_noconstruct_noexotic(duk_context *ctx
DUK_INTERNAL_DECL void duk_push_string_funcptr(duk_context *ctx, duk_uint8_t *ptr, duk_size_t sz);
DUK_INTERNAL_DECL void duk_push_lightfunc_name(duk_context *ctx, duk_tval *tv);
DUK_INTERNAL_DECL void duk_push_lightfunc_tostring(duk_context *ctx, duk_tval *tv);
DUK_INTERNAL_DECL duk_hbufferobject *duk_push_bufferobject(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx);
DUK_INTERNAL_DECL duk_hbufferobject *duk_push_bufferobject_raw(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx);
DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx); /* [] -> [val] */
DUK_INTERNAL_DECL duk_bool_t duk_put_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx); /* [val] -> [] */

17
src/duk_api_public.h.in

@ -428,6 +428,23 @@ DUK_EXTERNAL_DECL void *duk_push_buffer_raw(duk_context *ctx, duk_size_t size, d
#define duk_push_external_buffer(ctx) \
((void) duk_push_buffer_raw((ctx), 0, DUK_BUF_FLAG_DYNAMIC | DUK_BUF_FLAG_EXTERNAL))
#define DUK_BUFOBJ_CREATE_ARRBUF (1 << 4) /* internal flag: create backing ArrayBuffer; keep in one byte */
#define DUK_BUFOBJ_DUKTAPE_BUFFER 0
#define DUK_BUFOBJ_NODEJS_BUFFER 1
#define DUK_BUFOBJ_ARRAYBUFFER 2
#define DUK_BUFOBJ_DATAVIEW (3 | DUK_BUFOBJ_CREATE_ARRBUF)
#define DUK_BUFOBJ_INT8ARRAY (4 | DUK_BUFOBJ_CREATE_ARRBUF)
#define DUK_BUFOBJ_UINT8ARRAY (5 | DUK_BUFOBJ_CREATE_ARRBUF)
#define DUK_BUFOBJ_UINT8CLAMPEDARRAY (6 | DUK_BUFOBJ_CREATE_ARRBUF)
#define DUK_BUFOBJ_INT16ARRAY (7 | DUK_BUFOBJ_CREATE_ARRBUF)
#define DUK_BUFOBJ_UINT16ARRAY (8 | DUK_BUFOBJ_CREATE_ARRBUF)
#define DUK_BUFOBJ_INT32ARRAY (9 | DUK_BUFOBJ_CREATE_ARRBUF)
#define DUK_BUFOBJ_UINT32ARRAY (10 | DUK_BUFOBJ_CREATE_ARRBUF)
#define DUK_BUFOBJ_FLOAT32ARRAY (11 | DUK_BUFOBJ_CREATE_ARRBUF)
#define DUK_BUFOBJ_FLOAT64ARRAY (12 | DUK_BUFOBJ_CREATE_ARRBUF)
DUK_EXTERNAL_DECL void duk_push_buffer_object(duk_context *ctx, duk_idx_t idx_buffer, duk_uint_t byte_offset, duk_uint_t byte_length, duk_uint_t flags);
DUK_EXTERNAL_DECL duk_idx_t duk_push_heapptr(duk_context *ctx, void *ptr);
/*

102
src/duk_api_stack.c

@ -2421,11 +2421,11 @@ DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t index) {
h_val = DUK_TVAL_GET_BUFFER(tv);
DUK_ASSERT(h_val != NULL);
h_bufobj = duk_push_bufferobject(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER),
DUK_BIDX_BUFFER_PROTOTYPE);
h_bufobj = duk_push_bufferobject_raw(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER),
DUK_BIDX_BUFFER_PROTOTYPE);
DUK_ASSERT(h_bufobj != NULL);
DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE((duk_hobject *) h_bufobj));
DUK_ASSERT(DUK_HOBJECT_IS_BUFFEROBJECT((duk_hobject *) h_bufobj));
@ -3807,7 +3807,7 @@ DUK_EXTERNAL duk_idx_t duk_push_c_lightfunc(duk_context *ctx, duk_c_function fun
return 0; /* not reached */
}
DUK_INTERNAL duk_hbufferobject *duk_push_bufferobject(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx) {
DUK_INTERNAL duk_hbufferobject *duk_push_bufferobject_raw(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx) {
duk_hthread *thr = (duk_hthread *) ctx;
duk_hbufferobject *obj;
duk_tval *tv_slot;
@ -3836,6 +3836,96 @@ DUK_INTERNAL duk_hbufferobject *duk_push_bufferobject(duk_context *ctx, duk_uint
return obj;
}
/* XXX: There's quite a bit of overlap with buffer creation handling in
* duk_bi_buffer.c. Look for overlap and refactor.
*/
#define DUK__PACK_ARGS(classnum,protobidx,elemtype,elemshift,isview) \
(((classnum) << 24) | ((protobidx) << 16) | ((elemtype) << 8) | ((elemshift) << 4) | (isview))
static const duk_uint32_t duk__bufobj_flags_lookup[] = {
DUK__PACK_ARGS(DUK_HOBJECT_CLASS_BUFFER, DUK_BIDX_BUFFER_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 0), /* DUK_BUFOBJ_DUKTAPE_BUFFER */
DUK__PACK_ARGS(DUK_HOBJECT_CLASS_BUFFER, DUK_BIDX_NODEJS_BUFFER_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 0), /* DUK_BUFOBJ_NODEJS_BUFFER */
DUK__PACK_ARGS(DUK_HOBJECT_CLASS_ARRAYBUFFER, DUK_BIDX_ARRAYBUFFER_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 0), /* DUK_BUFOBJ_ARRAYBUFFER */
DUK__PACK_ARGS(DUK_HOBJECT_CLASS_DATAVIEW, DUK_BIDX_DATAVIEW_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 1), /* DUK_BUFOBJ_DATAVIEW */
DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT8ARRAY, DUK_BIDX_INT8ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_INT8, 0, 1), /* DUK_BUFOBJ_INT8ARRAY */
DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8ARRAY, DUK_BIDX_UINT8ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 1), /* DUK_BUFOBJ_UINT8ARRAY */
DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY, DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED, 0, 1), /* DUK_BUFOBJ_UINT8CLAMPEDARRAY */
DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT16ARRAY, DUK_BIDX_INT16ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_INT16, 1, 1), /* DUK_BUFOBJ_INT16ARRAY */
DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT16ARRAY, DUK_BIDX_UINT16ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT16, 1, 1), /* DUK_BUFOBJ_UINT16ARRAY */
DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT32ARRAY, DUK_BIDX_INT32ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_INT32, 2, 1), /* DUK_BUFOBJ_INT32ARRAY */
DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT32ARRAY, DUK_BIDX_UINT32ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT32, 2, 1), /* DUK_BUFOBJ_UINT32ARRAY */
DUK__PACK_ARGS(DUK_HOBJECT_CLASS_FLOAT32ARRAY, DUK_BIDX_FLOAT32ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_FLOAT32, 2, 1), /* DUK_BUFOBJ_FLOAT32ARRAY */
DUK__PACK_ARGS(DUK_HOBJECT_CLASS_FLOAT64ARRAY, DUK_BIDX_FLOAT64ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_FLOAT64, 3, 1), /* DUK_BUFOBJ_FLOAT64ARRAY */
};
#undef DUK__PACK_ARGS
DUK_EXTERNAL void duk_push_buffer_object(duk_context *ctx, duk_idx_t idx_buffer, duk_uint_t byte_offset, duk_uint_t byte_length, duk_uint_t flags) {
duk_hthread *thr;
duk_hbufferobject *h_bufobj;
duk_hbuffer *h_val;
duk_uint32_t tmp;
duk_uint_t classnum;
duk_uint_t protobidx;
duk_uint_t lookupidx;
DUK_ASSERT_CTX_VALID(ctx);
thr = (duk_hthread *) ctx;
DUK_UNREF(thr);
DUK_ASSERT_DISABLE(flags >= 0); /* flags is unsigned */
lookupidx = flags & 0x0f; /* 4 low bits */
if (lookupidx >= sizeof(duk__bufobj_flags_lookup) / sizeof(duk_uint32_t)) {
DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_INVALID_CALL_ARGS);
return; /* not reached */
}
tmp = duk__bufobj_flags_lookup[lookupidx];
classnum = tmp >> 24;
protobidx = (tmp >> 16) & 0xff;
h_val = duk_require_hbuffer(ctx, idx_buffer);
DUK_ASSERT(h_val != NULL);
h_bufobj = duk_push_bufferobject_raw(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(classnum),
protobidx);
DUK_ASSERT(h_bufobj != NULL);
h_bufobj->buf = h_val;
DUK_HBUFFER_INCREF(thr, h_val);
h_bufobj->offset = byte_offset;
h_bufobj->length = byte_length;
h_bufobj->shift = (tmp >> 4) & 0x0f;
h_bufobj->elem_type = (tmp >> 8) & 0xff;
h_bufobj->is_view = tmp & 0x0f;
/* TypedArray views need an automatic ArrayBuffer which must be
* provided as .buffer property of the view. Just create a new
* ArrayBuffer sharing the same underlying buffer.
*/
if (flags & DUK_BUFOBJ_CREATE_ARRBUF) {
h_bufobj = duk_push_bufferobject_raw(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER),
DUK_BIDX_ARRAYBUFFER_PROTOTYPE);
DUK_ASSERT(h_bufobj != NULL);
h_bufobj->buf = h_val;
DUK_HBUFFER_INCREF(thr, h_val);
h_bufobj->offset = byte_offset;
h_bufobj->length = byte_length;
DUK_ASSERT(h_bufobj->shift == 0);
h_bufobj->elem_type = DUK_HBUFFEROBJECT_ELEM_UINT8;
DUK_ASSERT(h_bufobj->is_view == 0);
duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LC_BUFFER, DUK_PROPDESC_FLAGS_NONE);
duk_compact(ctx, -1);
}
}
DUK_EXTERNAL duk_idx_t duk_push_error_object_va_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap) {
duk_hthread *thr = (duk_hthread *) ctx;
duk_idx_t ret;

94
src/duk_bi_buffer.c

@ -26,7 +26,7 @@ static const duk_uint8_t duk__buffer_class_from_elemtype[9] = {
/* Map DUK_HBUFFEROBJECT_ELEM_xxx to prototype object built-in index.
* Sync with duk_hbufferobject.h.
*/
static const duk_uint16_t duk__buffer_proto_from_elemtype[9] = {
static const duk_uint8_t duk__buffer_proto_from_elemtype[9] = {
DUK_BIDX_UINT8ARRAY_PROTOTYPE,
DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE,
DUK_BIDX_INT8ARRAY_PROTOTYPE,
@ -38,7 +38,7 @@ static const duk_uint16_t duk__buffer_proto_from_elemtype[9] = {
DUK_BIDX_FLOAT64ARRAY_PROTOTYPE
};
/* Map DUK_HBUFFEROBJECT_ELEM_xxx to byte size.
/* Map DUK__FLX_xxx to byte size.
*/
static const duk_uint8_t duk__buffer_nbytes_from_fldtype[6] = {
1, /* DUK__FLD_8BIT */
@ -187,11 +187,11 @@ DUK_LOCAL duk_hbufferobject *duk__push_arraybuffer_with_length(duk_context *ctx,
h_val = (duk_hbuffer *) duk_get_hbuffer(ctx, -1);
DUK_ASSERT(h_val != NULL);
h_bufobj = duk_push_bufferobject(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER),
DUK_BIDX_ARRAYBUFFER_PROTOTYPE);
h_bufobj = duk_push_bufferobject_raw(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER),
DUK_BIDX_ARRAYBUFFER_PROTOTYPE);
DUK_ASSERT(h_bufobj != NULL);
duk__set_bufobj_buffer(ctx, h_bufobj, h_val);
@ -534,11 +534,11 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_constructor(duk_context *ctx) {
h_val = duk_get_hbuffer(ctx, -1);
DUK_ASSERT(h_val != NULL);
h_bufobj = duk_push_bufferobject(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER),
DUK_BIDX_BUFFER_PROTOTYPE);
h_bufobj = duk_push_bufferobject_raw(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER),
DUK_BIDX_BUFFER_PROTOTYPE);
DUK_ASSERT(h_bufobj != NULL);
duk__set_bufobj_buffer(ctx, h_bufobj, h_val);
@ -605,11 +605,11 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_constructor(duk_context *ctx) {
h_buf = duk_get_hbuffer(ctx, -1);
DUK_ASSERT(h_buf != NULL);
h_bufobj = duk_push_bufferobject(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER),
DUK_BIDX_NODEJS_BUFFER_PROTOTYPE);
h_bufobj = duk_push_bufferobject_raw(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER),
DUK_BIDX_NODEJS_BUFFER_PROTOTYPE);
DUK_ASSERT(h_bufobj != NULL);
h_bufobj->buf = h_buf;
@ -656,11 +656,11 @@ DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_constructor(duk_context *ctx) {
DUK_ASSERT(h_val != NULL);
}
h_bufobj = duk_push_bufferobject(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER),
DUK_BIDX_ARRAYBUFFER_PROTOTYPE);
h_bufobj = duk_push_bufferobject_raw(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER),
DUK_BIDX_ARRAYBUFFER_PROTOTYPE);
DUK_ASSERT(h_bufobj != NULL);
duk__set_bufobj_buffer(ctx, h_bufobj, h_val);
@ -791,11 +791,11 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) {
DUK_ASSERT(byte_offset + byte_length <= h_bufarg->length);
DUK_ASSERT((elem_length << shift) == byte_length);
h_bufobj = duk_push_bufferobject(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(class_num),
proto_bidx);
h_bufobj = duk_push_bufferobject_raw(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(class_num),
proto_bidx);
h_val = h_bufarg->buf;
if (h_val == NULL) {
return DUK_RET_TYPE_ERROR;
@ -890,11 +890,11 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) {
DUK_ASSERT(h_val != NULL);
/* Push the resulting view object and attach the ArrayBuffer. */
h_bufobj = duk_push_bufferobject(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(class_num),
proto_bidx);
h_bufobj = duk_push_bufferobject_raw(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(class_num),
proto_bidx);
h_bufobj->buf = h_val;
DUK_HBUFFER_INCREF(thr, h_val);
@ -1034,11 +1034,11 @@ DUK_INTERNAL duk_ret_t duk_bi_dataview_constructor(duk_context *ctx) {
DUK_ASSERT(offset <= h_bufarg->length);
DUK_ASSERT(offset + length <= h_bufarg->length);
h_bufobj = duk_push_bufferobject(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DATAVIEW),
DUK_BIDX_DATAVIEW_PROTOTYPE);
h_bufobj = duk_push_bufferobject_raw(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DATAVIEW),
DUK_BIDX_DATAVIEW_PROTOTYPE);
h_val = h_bufarg->buf;
if (h_val == NULL) {
@ -1861,11 +1861,11 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx) {
*/
res_class_num = DUK_HOBJECT_GET_CLASS_NUMBER((duk_hobject *) h_this);
h_bufobj = duk_push_bufferobject(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(res_class_num),
DUK_BIDX_OBJECT_PROTOTYPE); /* replaced */
h_bufobj = duk_push_bufferobject_raw(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(res_class_num),
DUK_BIDX_OBJECT_PROTOTYPE); /* replaced */
DUK_ASSERT(h_bufobj != NULL);
res_proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_this); /* may be NULL */
DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) h_bufobj, res_proto);
@ -2060,11 +2060,11 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_concat(duk_context *ctx) {
return DUK_RET_RANGE_ERROR;
}
h_bufres = duk_push_bufferobject(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER),
DUK_BIDX_NODEJS_BUFFER_PROTOTYPE);
h_bufres = duk_push_bufferobject_raw(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BUFFEROBJECT |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER),
DUK_BIDX_NODEJS_BUFFER_PROTOTYPE);
DUK_ASSERT(h_bufres != NULL);
p = (duk_uint8_t *) duk_push_fixed_buffer(ctx, total_length);

422
tests/api/test-push-buffer-object.c

@ -0,0 +1,422 @@
/*
* duk_push_buffer_object() API call
*/
static void register_dump_buffer_info(duk_context *ctx) {
duk_eval_string_noresult(ctx,
"function dumpBufferInfo (v) {\n"
" var p = Object.getPrototypeOf(v);\n"
" var instof = [];\n"
" var prot = [];\n"
" if (v instanceof Duktape.Buffer) { instof.push('Duktape.Buffer'); }\n"
" if (v instanceof Buffer) { instof.push('Buffer'); }\n"
" if (v instanceof ArrayBuffer) { instof.push('ArrayBuffer'); }\n"
" if (v instanceof DataView) { instof.push('DataView'); }\n"
" if (v instanceof Int8Array) { instof.push('Int8Array'); }\n"
" if (v instanceof Uint8Array) { instof.push('Uint8Array'); }\n"
" if (v instanceof Uint8ClampedArray) { instof.push('Uint8ClampedArray'); }\n"
" if (v instanceof Int16Array) { instof.push('Int16Array'); }\n"
" if (v instanceof Uint16Array) { instof.push('Uint16Array'); }\n"
" if (v instanceof Int32Array) { instof.push('Int32Array'); }\n"
" if (v instanceof Uint32Array) { instof.push('Uint32Array'); }\n"
" if (v instanceof Float32Array) { instof.push('Float32Array'); }\n"
" if (v instanceof Float64Array) { instof.push('Float64Array'); }\n"
" if (p === Duktape.Buffer.prototype) { prot.push('Duktape.Buffer.prototype'); }\n"
" if (p === Buffer.prototype) { prot.push('Buffer.prototype'); }\n"
" if (p === ArrayBuffer.prototype) { prot.push('ArrayBuffer.prototype'); }\n"
" if (p === DataView.prototype) { prot.push('DataView.prototype'); }\n"
" if (p === Int8Array.prototype) { prot.push('Int8Array.prototype'); }\n"
" if (p === Uint8Array.prototype) { prot.push('Uint8Array.prototype'); }\n"
" if (p === Uint8ClampedArray.prototype) { prot.push('Uint8ClampedArray.prototype'); }\n"
" if (p === Int16Array.prototype) { prot.push('Int16Array.prototype'); }\n"
" if (p === Uint16Array.prototype) { prot.push('Uint16Array.prototype'); }\n"
" if (p === Int32Array.prototype) { prot.push('Int32Array.prototype'); }\n"
" if (p === Uint32Array.prototype) { prot.push('Uint32Array.prototype'); }\n"
" if (p === Float32Array.prototype) { prot.push('Float32Array.prototype'); }\n"
" if (p === Float64Array.prototype) { prot.push('Float64Array.prototype'); }\n"
" print(typeof v, Object.prototype.toString.call(v), v.length, v.byteOffset, v.byteLength, v.BYTES_PER_ELEMENT, typeof v.buffer);\n"
" print(v instanceof Duktape.Buffer, v instanceof Buffer, v instanceof ArrayBuffer, v instanceof DataView, v instanceof Int8Array, v instanceof Uint8Array, v instanceof Uint8ClampedArray, v instanceof Int16Array, v instanceof Uint16Array, v instanceof Int32Array, v instanceof Uint32Array, v instanceof Float32Array, v instanceof Float64Array, '->', instof.join(','));\n"
" print(p === Duktape.Buffer.prototype, p === Buffer.prototype, p === ArrayBuffer.prototype, p === DataView.prototype, p === Int8Array.prototype, p === Uint8Array.prototype, p === Uint8ClampedArray.prototype, p === Int16Array.prototype, p === Uint16Array.prototype, p === Int32Array.prototype, p === Uint32Array.prototype, p === Float32Array.prototype, p === Float64Array.prototype, '->', prot.join(','));\n"
"}");
}
/*===
*** test_basic (duk_safe_call)
object [object Buffer] 32 128 32 1 undefined
true false false false false false false false false false false false false -> Duktape.Buffer
true false false false false false false false false false false false false -> Duktape.Buffer.prototype
object [object Buffer] 32 128 32 1 undefined
false true false false false false false false false false false false false -> Buffer
false true false false false false false false false false false false false -> Buffer.prototype
object [object ArrayBuffer] 32 128 32 1 undefined
false false true false false false false false false false false false false -> ArrayBuffer
false false true false false false false false false false false false false -> ArrayBuffer.prototype
object [object DataView] 32 128 32 1 object
false false false true false false false false false false false false false -> DataView
false false false true false false false false false false false false false -> DataView.prototype
object [object Int8Array] 32 128 32 1 object
false false false false true false false false false false false false false -> Int8Array
false false false false true false false false false false false false false -> Int8Array.prototype
object [object Uint8Array] 32 128 32 1 object
false false false false false true false false false false false false false -> Uint8Array
false false false false false true false false false false false false false -> Uint8Array.prototype
object [object Uint8ClampedArray] 32 128 32 1 object
false false false false false false true false false false false false false -> Uint8ClampedArray
false false false false false false true false false false false false false -> Uint8ClampedArray.prototype
object [object Int16Array] 16 128 32 2 object
false false false false false false false true false false false false false -> Int16Array
false false false false false false false true false false false false false -> Int16Array.prototype
object [object Uint16Array] 16 128 32 2 object
false false false false false false false false true false false false false -> Uint16Array
false false false false false false false false true false false false false -> Uint16Array.prototype
object [object Int32Array] 8 128 32 4 object
false false false false false false false false false true false false false -> Int32Array
false false false false false false false false false true false false false -> Int32Array.prototype
object [object Uint32Array] 8 128 32 4 object
false false false false false false false false false false true false false -> Uint32Array
false false false false false false false false false false true false false -> Uint32Array.prototype
object [object Float32Array] 8 128 32 4 object
false false false false false false false false false false false true false -> Float32Array
false false false false false false false false false false false true false -> Float32Array.prototype
object [object Float64Array] 4 128 32 8 object
false false false false false false false false false false false false true -> Float64Array
false false false false false false false false false false false false true -> Float64Array.prototype
final top: 0
==> rc=0, result='undefined'
===*/
static duk_ret_t test_basic(duk_context *ctx) {
duk_uint_t test[] = {
DUK_BUFOBJ_DUKTAPE_BUFFER,
DUK_BUFOBJ_NODEJS_BUFFER,
DUK_BUFOBJ_ARRAYBUFFER,
DUK_BUFOBJ_DATAVIEW,
DUK_BUFOBJ_INT8ARRAY,
DUK_BUFOBJ_UINT8ARRAY,
DUK_BUFOBJ_UINT8CLAMPEDARRAY,
DUK_BUFOBJ_INT16ARRAY,
DUK_BUFOBJ_UINT16ARRAY,
DUK_BUFOBJ_INT32ARRAY,
DUK_BUFOBJ_UINT32ARRAY,
DUK_BUFOBJ_FLOAT32ARRAY,
DUK_BUFOBJ_FLOAT64ARRAY,
};
int i;
unsigned char extbuf[256];
for (i = 0; i < sizeof(test) / sizeof(duk_uint_t); i++) {
switch (i % 3) {
case 0:
duk_push_fixed_buffer(ctx, 256);
break;
case 1:
duk_push_dynamic_buffer(ctx, 256);
break;
case 2:
duk_push_external_buffer(ctx);
duk_config_buffer(ctx, -1, (void *) extbuf, 256);
break;
}
duk_push_undefined(ctx); /* dummy */
duk_push_buffer_object(ctx, -2, 128, 32, test[i]);
duk_eval_string(ctx, "dumpBufferInfo");
duk_dup(ctx, -2);
duk_call(ctx, 1);
/* ... plain undefined bufferobject result */
duk_pop_n(ctx, 4);
}
printf("final top: %ld\n", (long) duk_get_top(ctx));
return 0;
}
/*===
*** test_view_buffer_prop (duk_safe_call)
[object Uint8Array]
object [object Uint8Array] 22 16 22 1 object
false false false false false true false false false false false false false -> Uint8Array
false false false false false true false false false false false false false -> Uint8Array.prototype
22
16
22
1
[object ArrayBuffer]
[object ArrayBuffer] false false false
[object ArrayBuffer]
object [object ArrayBuffer] 22 16 22 1 undefined
false false true false false false false false false false false false false -> ArrayBuffer
false false true false false false false false false false false false false -> ArrayBuffer.prototype
22
16
22
1
undefined
123 123
extbuf[16 + 3] = 123
final top: 1
==> rc=0, result='undefined'
===*/
/* The basic test ensures all typed array views get a .buffer property.
* Test the .buffer reference in more detail: check that property
* attributes are correct, check that it backs to the same slice, etc.
*/
static duk_ret_t test_view_buffer_prop(duk_context *ctx) {
unsigned char extbuf[256];
duk_push_external_buffer(ctx);
duk_config_buffer(ctx, -1, (void *) extbuf, 256);
duk_push_buffer_object(ctx, -1, 16, 22, DUK_BUFOBJ_UINT8ARRAY);
duk_eval_string(ctx,
"(function (v) {\n"
" var pd;\n"
" print(Object.prototype.toString.call(v));\n"
" dumpBufferInfo(v);\n"
" print(v.length);\n"
" print(v.byteOffset);\n"
" print(v.byteLength);\n"
" print(v.BYTES_PER_ELEMENT);\n"
" print(v.buffer);\n"
" pd = Object.getOwnPropertyDescriptor(v, 'buffer');\n"
" print(pd.value, pd.writable, pd.enumerable, pd.configurable);\n"
" print(Object.prototype.toString.call(v.buffer));\n"
" dumpBufferInfo(v.buffer);\n"
" print(v.buffer.length);\n" /* some of these are Duktape custom */
" print(v.buffer.byteOffset);\n"
" print(v.buffer.byteLength);\n"
" print(v.buffer.BYTES_PER_ELEMENT);\n"
" print(v.buffer.buffer);\n"
" v[3] = 123; /* check that backing buffer and slice matches */\n"
" print(v[3], new Uint8Array(v.buffer)[3]);\n"
"})");
duk_dup(ctx, -2);
duk_call(ctx, 1);
duk_pop_2(ctx);
printf("extbuf[16 + 3] = %d\n", (int) extbuf[16 + 3]);
printf("final top: %ld\n", (long) duk_get_top(ctx));
return 0;
}
/*===
*** test_unaligned (duk_safe_call)
object [object Uint32Array] 16 7 64 4 object
false false false false false false false false false false true false false -> Uint32Array
false false false false false false false false false false true false false -> Uint32Array.prototype
final top: 0
==> rc=0, result='undefined'
===*/
/* Normally typed array constructors require that the slice begin at a multiple
* of the element size (e.g. 4 bytes for Uint32Array) and that the slice ends
* evenly. The C API doesn't pose such restrictions.
*/
static duk_ret_t test_unaligned(duk_context *ctx) {
duk_push_fixed_buffer(ctx, 256);
duk_push_buffer_object(ctx, -1, 7, 64, DUK_BUFOBJ_UINT32ARRAY);
duk_eval_string(ctx, "dumpBufferInfo");
duk_dup(ctx, -2);
duk_call(ctx, 1);
duk_pop(ctx);
duk_pop_n(ctx, 2);
printf("final top: %ld\n", (long) duk_get_top(ctx));
return 0;
}
/*===
*** test_unaligned_uneven (duk_safe_call)
object [object Uint32Array] 15 7 63 4 object
false false false false false false false false false false true false false -> Uint32Array
false false false false false false false false false false true false false -> Uint32Array.prototype
final top: 0
==> rc=0, result='undefined'
===*/
/* Normally typed array constructors require that the slice begin at a multiple
* of the element size (e.g. 4 bytes for Uint32Array) and that the slice ends
* evenly. The C API doesn't pose such restrictions; the .length (number of
* elements) will become the number of full elements allowed by the slice.
* The .byteOffset and .byteLength will reflect the call arguments, e.g.
* .byteLength is 63 here, which means that .length x .BYTES_PER_ELEMENT
* won't match .byteLength in this case.
*/
static duk_ret_t test_unaligned_uneven(duk_context *ctx) {
/* Start at an unaligned offset, with slice length NOT a multiple
* of 4.
*/
duk_push_fixed_buffer(ctx, 256);
duk_push_buffer_object(ctx, -1, 7, 63, DUK_BUFOBJ_UINT32ARRAY);
duk_eval_string(ctx, "dumpBufferInfo");
duk_dup(ctx, -2);
duk_call(ctx, 1);
duk_pop(ctx);
duk_pop_n(ctx, 2);
printf("final top: %ld\n", (long) duk_get_top(ctx));
return 0;
}
/*===
*** test_uncovered (duk_safe_call)
object [object Uint32Array] 128 7 512 4 object
false false false false false false false false false false true false false -> Uint32Array
false false false false false false false false false false true false false -> Uint32Array.prototype
final top: 0
==> rc=0, result='undefined'
===*/
/* It's not an error for the underlying plain buffer to be too small to
* cover the slice. This is allowed because it may happen for dynamic
* and external buffers at run time anyway. In any case, no memory
* unsafe behavior happens.
*/
static duk_ret_t test_uncovered(duk_context *ctx) {
duk_push_fixed_buffer(ctx, 256);
duk_push_buffer_object(ctx, -1, 7, 512, DUK_BUFOBJ_UINT32ARRAY);
duk_eval_string(ctx, "dumpBufferInfo");
duk_dup(ctx, -2);
duk_call(ctx, 1);
duk_pop(ctx);
duk_eval_string(ctx,
"(function (v) {\n"
" for (var i = 0; i < v.length; i++) { v[i] = 123; }\n"
" for (var i = 0; i < v.length; i++) { var ignore = v[i]; }\n"
"})");
duk_dup(ctx, -2);
duk_call(ctx, 1);
duk_pop(ctx);
duk_pop_n(ctx, 2);
printf("final top: %ld\n", (long) duk_get_top(ctx));
return 0;
}
/*===
*** test_invalid_index1 (duk_safe_call)
==> rc=1, result='TypeError: unexpected type'
*** test_invalid_index2 (duk_safe_call)
==> rc=1, result='TypeError: unexpected type'
===*/
static duk_ret_t test_invalid_index1(duk_context *ctx) {
duk_push_fixed_buffer(ctx, 256);
duk_push_buffer_object(ctx, -2, 7, 512, DUK_BUFOBJ_UINT32ARRAY);
printf("final top: %ld\n", (long) duk_get_top(ctx));
return 0;
}
static duk_ret_t test_invalid_index2(duk_context *ctx) {
duk_push_fixed_buffer(ctx, 256);
duk_push_buffer_object(ctx, DUK_INVALID_INDEX, 7, 512, DUK_BUFOBJ_UINT32ARRAY);
printf("final top: %ld\n", (long) duk_get_top(ctx));
return 0;
}
/*===
*** test_invalid_bufferobject (duk_safe_call)
==> rc=1, result='TypeError: unexpected type'
*** test_invalid_string (duk_safe_call)
==> rc=1, result='TypeError: unexpected type'
*** test_invalid_null (duk_safe_call)
==> rc=1, result='TypeError: unexpected type'
===*/
/* A bufferobject is -not- accepted as the underlying buffer. This also
* prevents issues like a slice being applied to a sliced view.
*/
static duk_ret_t test_invalid_bufferobject(duk_context *ctx) {
duk_eval_string(ctx, "new Uint16Array(123);");
duk_push_buffer_object(ctx, -1, 7, 512, DUK_BUFOBJ_UINT32ARRAY);
printf("final top: %ld\n", (long) duk_get_top(ctx));
return 0;
}
/* A string is not allowed (although this might be useful). */
static duk_ret_t test_invalid_string(duk_context *ctx) {
duk_push_string(ctx, "foobar");
duk_push_buffer_object(ctx, -1, 7, 512, DUK_BUFOBJ_UINT32ARRAY);
printf("final top: %ld\n", (long) duk_get_top(ctx));
return 0;
}
static duk_ret_t test_invalid_null(duk_context *ctx) {
duk_push_null(ctx);
duk_push_buffer_object(ctx, -1, 7, 512, DUK_BUFOBJ_UINT32ARRAY);
printf("final top: %ld\n", (long) duk_get_top(ctx));
return 0;
}
/*===
*** test_invalid_flags1 (duk_safe_call)
final top: 2
==> rc=0, result='undefined'
*** test_invalid_flags2 (duk_safe_call)
==> rc=1, result='TypeError: invalid call args'
*** test_invalid_flags3 (duk_safe_call)
==> rc=1, result='TypeError: invalid call args'
===*/
/* If 'flags' is given as zero, it will match a DUK_BUFOBJ_DUKTAPEBUFFER.
* So this test succeeds which is intentional.
*/
static duk_ret_t test_invalid_flags1(duk_context *ctx) {
duk_push_fixed_buffer(ctx, 256);
duk_push_buffer_object(ctx, -1, 7, 512, 0 /* no type given, but matches DUK_BUFOBJ_DUKTAPEBUFFER */);
printf("final top: %ld\n", (long) duk_get_top(ctx));
return 0;
}
static duk_ret_t test_invalid_flags2(duk_context *ctx) {
duk_push_fixed_buffer(ctx, 256);
duk_push_buffer_object(ctx, -1, 7, 512, (duk_uint_t) 0xdeadbeef /* ERROR: bogus type */);
printf("final top: %ld\n", (long) duk_get_top(ctx));
return 0;
}
/* Boundary case. */
static duk_ret_t test_invalid_flags3(duk_context *ctx) {
duk_push_fixed_buffer(ctx, 256);
duk_push_buffer_object(ctx, -1, 7, 512, (duk_uint_t) (DUK_BUFOBJ_FLOAT64ARRAY + 1) /* ERROR: bogus type, right after last defined */);
printf("final top: %ld\n", (long) duk_get_top(ctx));
return 0;
}
/*
* Main
*/
void test(duk_context *ctx) {
register_dump_buffer_info(ctx);
TEST_SAFE_CALL(test_basic);
TEST_SAFE_CALL(test_view_buffer_prop);
TEST_SAFE_CALL(test_unaligned);
TEST_SAFE_CALL(test_unaligned_uneven);
TEST_SAFE_CALL(test_uncovered);
TEST_SAFE_CALL(test_invalid_index1);
TEST_SAFE_CALL(test_invalid_index2);
TEST_SAFE_CALL(test_invalid_bufferobject);
TEST_SAFE_CALL(test_invalid_string);
TEST_SAFE_CALL(test_invalid_null);
TEST_SAFE_CALL(test_invalid_flags1);
TEST_SAFE_CALL(test_invalid_flags2);
TEST_SAFE_CALL(test_invalid_flags3);
/* XXX: could test for more errors */
}

1
website/api/duk_get_buffer_data.yaml

@ -57,6 +57,7 @@ tags:
- stack
- buffer
- bufferobject
- experimental
seealso:
- duk_require_buffer_data

65
website/api/duk_push_buffer_object.yaml

@ -0,0 +1,65 @@
name: duk_push_buffer
proto: |
void duk_push_buffer_object(duk_context *ctx, duk_idx_t idx_buffer, duk_uint_t byte_offset, duk_uint_t byte_length, duk_uint_t flags);
stack: |
[ ... buffer! ... ] -> [ ... buffer! ... bufobj! ]
summary: |
<p>Push a new buffer object or a buffer view object. An underlying plain
buffer is provided at index <code>idx_buffer</code>. The type of the buffer
or view is given in <code>flags</code> (e.g. <code>DUK_BUFOBJ_UINT16ARRAY</code>).
The active range or "slice" used from the underlying buffer is indicated by
<code>byte_offset</code> and <code>byte_length</code>.</p>
<p>The available buffer types are:</p>
<table>
<tr><th>Define</th><th>Buffer/view type</th></tr>
<tr><td>DUK_BUFOBJ_DUKTAPE_BUFFER</td><td>Duktape.Buffer</td></tr>
<tr><td>DUK_BUFOBJ_NODEJS_BUFFER</td><td>Buffer (Node.js)</td></tr>
<tr><td>DUK_BUFOBJ_ARRAYBUFFER</td><td>ArrayBuffer</td></tr>
<tr><td>DUK_BUFOBJ_DATAVIEW</td><td>DataView</td></tr>
<tr><td>DUK_BUFOBJ_INT8ARRAY</td><td>Int8Array</td></tr>
<tr><td>DUK_BUFOBJ_UINT8ARRAY</td><td>Uint8Array</td></tr>
<tr><td>DUK_BUFOBJ_UINT8CLAMPEDARRAY</td><td>Uint8ClampedArray</td></tr>
<tr><td>DUK_BUFOBJ_INT16ARRAY</td><td>Int16Array</td></tr>
<tr><td>DUK_BUFOBJ_UINT16ARRAY</td><td>Uint16Array</td></tr>
<tr><td>DUK_BUFOBJ_INT32ARRAY</td><td>Int32Array</td></tr>
<tr><td>DUK_BUFOBJ_UINT32ARRAY</td><td>Uint32Array</td></tr>
<tr><td>DUK_BUFOBJ_FLOAT32ARRAY</td><td>Float32Array</td></tr>
<tr><td>DUK_BUFOBJ_FLOAT64ARRAY</td><td>Float64Array</td></tr>
</table>
<p>The underlying plain buffer should cover the range indicated by the
<code>byte_offset</code> and <code>byte_length</code> arguments. Even
if that is not the case, memory safety is guaranteed; e.g. attempt to
read values outside the underlying buffer will return zero. The underlying
buffer size is intentionally not checked when creating a buffer object:
even if the buffer fully covered the byte range during creation, it might
be resized later.</p>
example: |
/* Map byte range [100,150[ of plain buffer at idx_plain_buf into a
* Uint16Array object which will have the following properties:
*
* - length: 25 (length in Uint16 elements)
* - byteLength: 50 (length in bytes)
* - byteOffset: 100 (byte offset to start of slice)
* - BYTES_PER_ELEMENT: 2 (Uint16)
*/
duk_push_buffer_object(ctx, idx_plain_buf, 100, 50, DUK_BUFOBJ_UINT16ARRAY);
tags:
- stack
- bufferobject
- experimental
seealso:
- duk_push_buffer
- duk_push_fixed_buffer
- duk_push_dynamic_buffer
- duk_push_external_buffer
introduced: 1.3.0

1
website/api/duk_require_buffer.yaml

@ -21,6 +21,7 @@ example: |
tags:
- stack
- buffer
- experimental
seealso:
- duk_get_buffer

Loading…
Cancel
Save