From 2fec30517938c3e780b0ce065d91974c5e96a0d1 Mon Sep 17 00:00:00 2001 From: Sami Vaarala Date: Sat, 8 Mar 2014 21:47:47 +0200 Subject: [PATCH] first draft of buffer virtual length/index property implementation, not as trivial as seems because buffer bytes are the first writable virtual properties --- src/duk_api.c | 18 +--- src/duk_bi_buffer.c | 1 + src/duk_hobject.h | 12 ++- src/duk_hobject_enum.c | 32 ++++-- src/duk_hobject_props.c | 223 ++++++++++++++++++++++++++++++++++++---- 5 files changed, 243 insertions(+), 43 deletions(-) diff --git a/src/duk_api.c b/src/duk_api.c index d503f015..07675a1a 100644 --- a/src/duk_api.c +++ b/src/duk_api.c @@ -1837,12 +1837,8 @@ void *duk_to_pointer(duk_context *ctx, int index) { void duk_to_object(duk_context *ctx, int index) { duk_hthread *thr = (duk_hthread *) ctx; duk_tval *tv; - duk_hobject *res; int shared_flags = 0; /* shared flags for a subset of types */ int shared_proto = 0; - int shared_string = 0; - - /* FIXME: rework shared_XXX to fit into one reg? */ DUK_ASSERT(ctx != NULL); @@ -1865,9 +1861,9 @@ void duk_to_object(duk_context *ctx, int index) { } case DUK_TAG_STRING: { shared_flags = DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_SPECIAL_STRINGOBJ | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_STRING); shared_proto = DUK_BIDX_STRING_PROTOTYPE; - shared_string = 1; goto create_object; } case DUK_TAG_OBJECT: { @@ -1876,6 +1872,7 @@ void duk_to_object(duk_context *ctx, int index) { } case DUK_TAG_BUFFER: { shared_flags = DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_SPECIAL_BUFFEROBJ | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER); shared_proto = DUK_BIDX_BUFFER_PROTOTYPE; goto create_object; @@ -1897,22 +1894,17 @@ void duk_to_object(duk_context *ctx, int index) { create_object: (void) duk_push_object_helper(ctx, shared_flags, shared_proto); - res = duk_require_hobject(ctx, -1); - DUK_ASSERT(res != NULL); /* Note: Boolean prototype's internal value property is not writable, * but duk_def_prop_stridx() disregards the write protection. Boolean * instances are immutable. + * + * String and buffer special behaviors are already enabled which is not + * ideal, but a write to the internal value is not affected by them. */ duk_dup(ctx, index); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE); - /* FIXME: fix this check to lookup class from shared_flags to minimize size */ - if (shared_string) { - /* Enable special string behavior only after internal value has been set */ - DUK_HOBJECT_SET_SPECIAL_STRINGOBJ(res); - } - duk_replace(ctx, index); } diff --git a/src/duk_bi_buffer.c b/src/duk_bi_buffer.c index a65f6e08..64ea674d 100644 --- a/src/duk_bi_buffer.c +++ b/src/duk_bi_buffer.c @@ -20,6 +20,7 @@ int duk_bi_buffer_constructor(duk_context *ctx) { if (duk_is_constructor_call(ctx)) { duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_SPECIAL_BUFFEROBJ | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER), DUK_BIDX_BUFFER_PROTOTYPE); diff --git a/src/duk_hobject.h b/src/duk_hobject.h index e63683f1..2c1a15d5 100644 --- a/src/duk_hobject.h +++ b/src/duk_hobject.h @@ -49,7 +49,7 @@ #define DUK_HOBJECT_FLAG_SPECIAL_STRINGOBJ DUK_HEAPHDR_USER_FLAG(14) /* 'String' object, array index special behavior */ #define DUK_HOBJECT_FLAG_SPECIAL_ARGUMENTS DUK_HEAPHDR_USER_FLAG(15) /* 'Arguments' object and has arguments special behavior (non-strict callee) */ #define DUK_HOBJECT_FLAG_SPECIAL_DUKFUNC DUK_HEAPHDR_USER_FLAG(16) /* Duktape/C (nativefunction) object, special 'length' */ -/* bit 17 unused */ +#define DUK_HOBJECT_FLAG_SPECIAL_BUFFEROBJ DUK_HEAPHDR_USER_FLAG(17) /* 'Buffer' object, array index special behavior, virtual 'length' */ /* bit 18 unused */ /* bit 19 unused */ /* bit 20 unused */ @@ -112,7 +112,8 @@ #define DUK_HOBJECT_SPECIAL_BEHAVIOR_FLAGS (DUK_HOBJECT_FLAG_SPECIAL_ARRAY | \ DUK_HOBJECT_FLAG_SPECIAL_ARGUMENTS | \ DUK_HOBJECT_FLAG_SPECIAL_STRINGOBJ | \ - DUK_HOBJECT_FLAG_SPECIAL_DUKFUNC) + DUK_HOBJECT_FLAG_SPECIAL_DUKFUNC | \ + DUK_HOBJECT_FLAG_SPECIAL_BUFFEROBJ) #define DUK_HOBJECT_HAS_SPECIAL_BEHAVIOR(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_SPECIAL_BEHAVIOR_FLAGS) @@ -132,6 +133,7 @@ #define DUK_HOBJECT_HAS_SPECIAL_STRINGOBJ(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_STRINGOBJ) #define DUK_HOBJECT_HAS_SPECIAL_ARGUMENTS(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_ARGUMENTS) #define DUK_HOBJECT_HAS_SPECIAL_DUKFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_DUKFUNC) +#define DUK_HOBJECT_HAS_SPECIAL_BUFFEROBJ(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_BUFFEROBJ) #define DUK_HOBJECT_SET_EXTENSIBLE(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXTENSIBLE) #define DUK_HOBJECT_SET_CONSTRUCTABLE(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CONSTRUCTABLE) @@ -149,6 +151,7 @@ #define DUK_HOBJECT_SET_SPECIAL_STRINGOBJ(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_STRINGOBJ) #define DUK_HOBJECT_SET_SPECIAL_ARGUMENTS(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_ARGUMENTS) #define DUK_HOBJECT_SET_SPECIAL_DUKFUNC(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_DUKFUNC) +#define DUK_HOBJECT_SET_SPECIAL_BUFFEROBJ(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_BUFFEROBJ) #define DUK_HOBJECT_CLEAR_EXTENSIBLE(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXTENSIBLE) #define DUK_HOBJECT_CLEAR_CONSTRUCTABLE(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CONSTRUCTABLE) @@ -166,12 +169,16 @@ #define DUK_HOBJECT_CLEAR_SPECIAL_STRINGOBJ(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_STRINGOBJ) #define DUK_HOBJECT_CLEAR_SPECIAL_ARGUMENTS(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_ARGUMENTS) #define DUK_HOBJECT_CLEAR_SPECIAL_DUKFUNC(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_DUKFUNC) +#define DUK_HOBJECT_CLEAR_SPECIAL_BUFFEROBJ(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_BUFFEROBJ) /* flags used for property attributes in duk_propdesc and packed flags */ #define DUK_PROPDESC_FLAG_WRITABLE (1 << 0) /* E5 Section 8.6.1 */ #define DUK_PROPDESC_FLAG_ENUMERABLE (1 << 1) /* E5 Section 8.6.1 */ #define DUK_PROPDESC_FLAG_CONFIGURABLE (1 << 2) /* E5 Section 8.6.1 */ #define DUK_PROPDESC_FLAG_ACCESSOR (1 << 3) /* accessor */ +#define DUK_PROPDESC_FLAG_VIRTUAL (1 << 4) /* property is virtual: used in duk_propdesc, never stored + * (used by e.g. buffer virtual properties) + */ #define DUK_PROPDESC_FLAGS_MASK (DUK_PROPDESC_FLAG_WRITABLE | \ DUK_PROPDESC_FLAG_ENUMERABLE | \ DUK_PROPDESC_FLAG_CONFIGURABLE | \ @@ -663,6 +670,7 @@ int duk_hobject_object_ownprop_helper(duk_context *ctx, int required_desc_flags) /* internal properties */ int duk_hobject_get_internal_value(duk_heap *heap, duk_hobject *obj, duk_tval *tv); duk_hstring *duk_hobject_get_internal_value_string(duk_heap *heap, duk_hobject *obj); +duk_hbuffer *duk_hobject_get_internal_value_buffer(duk_heap *heap, duk_hobject *obj); /* hobject management functions */ void duk_hobject_compact_props(duk_hthread *thr, duk_hobject *obj); diff --git a/src/duk_hobject_enum.c b/src/duk_hobject_enum.c index 34c7e6f2..bab1fe3f 100644 --- a/src/duk_hobject_enum.c +++ b/src/duk_hobject_enum.c @@ -189,24 +189,36 @@ void duk_hobject_enumerator_create(duk_context *ctx, int enum_flags) { curr = target; while (curr) { - duk_uint32_t i; + duk_uint32_t i, len; /* * Virtual properties. * - * String indices are virtual and always enumerable. String 'length' - * is virtual and non-enumerable. Array and arguments object props - * have special behavior but are concrete. + * String and buffer indices are virtual and always enumerable, + * 'length' is virtual and non-enumerable. Array and arguments + * object props have special behavior but are concrete. */ - if (DUK_HOBJECT_HAS_SPECIAL_STRINGOBJ(curr)) { - duk_hstring *h_val; - - h_val = duk_hobject_get_internal_value_string(thr->heap, curr); - DUK_ASSERT(h_val != NULL); /* string objects must not created without internal value */ + if (DUK_HOBJECT_HAS_SPECIAL_STRINGOBJ(curr) || + DUK_HOBJECT_HAS_SPECIAL_BUFFEROBJ(curr)) { + /* String and buffer enumeration behavior is identical now, + * so use shared handler. + */ + if (DUK_HOBJECT_HAS_SPECIAL_STRINGOBJ(curr)) { + duk_hstring *h_val; + h_val = duk_hobject_get_internal_value_string(thr->heap, curr); + DUK_ASSERT(h_val != NULL); /* string objects must not created without internal value */ + len = DUK_HSTRING_GET_CHARLEN(h_val); + } else { + duk_hbuffer *h_val; + DUK_ASSERT(DUK_HOBJECT_HAS_SPECIAL_BUFFEROBJ(curr)); + h_val = duk_hobject_get_internal_value_buffer(thr->heap, curr); + DUK_ASSERT(h_val != NULL); /* buffer objects must not created without internal value */ + len = DUK_HBUFFER_GET_SIZE(h_val); + } /* FIXME: type for 'i' to match string max len (duk_uint32_t) */ - for (i = 0; i < DUK_HSTRING_GET_CHARLEN(h_val); i++) { + for (i = 0; i < len; i++) { duk_hstring *k; k = duk_heap_string_intern_u32_checked(thr, i); diff --git a/src/duk_hobject_props.c b/src/duk_hobject_props.c index dff391cd..ce43b728 100644 --- a/src/duk_hobject_props.c +++ b/src/duk_hobject_props.c @@ -1076,6 +1076,22 @@ duk_hstring *duk_hobject_get_internal_value_string(duk_heap *heap, duk_hobject * return NULL; } +duk_hbuffer *duk_hobject_get_internal_value_buffer(duk_heap *heap, duk_hobject *obj) { + duk_tval tv; + + DUK_ASSERT(heap != NULL); + DUK_ASSERT(obj != NULL); + + if (duk_hobject_get_internal_value(heap, obj, &tv)) { + duk_hbuffer *h; + DUK_ASSERT(DUK_TVAL_IS_BUFFER(&tv)); + h = DUK_TVAL_GET_BUFFER(&tv); + return h; + } + + return NULL; +} + /* * Arguments handling helpers (argument map mainly). * @@ -1393,7 +1409,8 @@ static int duk__get_own_property_desc_raw(duk_hthread *thr, duk_hobject *obj, du duk_push_hstring(ctx, h_val); duk_substring(ctx, -1, arr_idx, arr_idx + 1); /* [str] -> [substr] */ } - out_desc->flags = DUK_PROPDESC_FLAG_ENUMERABLE; /* E5 Section 15.5.5.2 */ + out_desc->flags = DUK_PROPDESC_FLAG_ENUMERABLE | /* E5 Section 15.5.5.2 */ + DUK_PROPDESC_FLAG_VIRTUAL; DUK_ASSERT(!DUK_HOBJECT_HAS_SPECIAL_ARGUMENTS(obj)); return 1; /* cannot be e.g. arguments special, since special 'traits' are mutually exclusive */ @@ -1411,7 +1428,52 @@ static int duk__get_own_property_desc_raw(duk_hthread *thr, duk_hobject *obj, du if (push_value) { duk_push_number(ctx, (double) DUK_HSTRING_GET_CHARLEN(h_val)); } - out_desc->flags = 0; /* E5 Section 15.5.5.1 */ + out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL; /* E5 Section 15.5.5.1 */ + + DUK_ASSERT(!DUK_HOBJECT_HAS_SPECIAL_ARGUMENTS(obj)); + return 1; /* cannot be arguments special */ + } + } else if (DUK_HOBJECT_HAS_SPECIAL_BUFFEROBJ(obj)) { + DUK_DDDPRINT("buffer object special property get for key: %!O, arr_idx: %d", key, arr_idx); + + if (arr_idx != DUK__NO_ARRAY_INDEX) { + duk_hbuffer *h_val; + + DUK_DDDPRINT("array index exists"); + + h_val = duk_hobject_get_internal_value_buffer(thr->heap, obj); + DUK_ASSERT(h_val); + if (arr_idx < DUK_HBUFFER_GET_SIZE(h_val)) { + DUK_DDDPRINT("-> found, array index inside buffer"); + if (push_value) { + duk_push_int(ctx, ((duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(h_val))[arr_idx]); + } + out_desc->flags = DUK_PROPDESC_FLAG_WRITABLE | + DUK_PROPDESC_FLAG_ENUMERABLE | + DUK_PROPDESC_FLAG_VIRTUAL; + + DUK_ASSERT(!DUK_HOBJECT_HAS_SPECIAL_ARGUMENTS(obj)); + return 1; /* cannot be e.g. arguments special, since special 'traits' are mutually exclusive */ + } else { + /* index is above internal buffer length -> property is fully normal */ + DUK_DDDPRINT("array index outside buffer -> normal property"); + } + } else if (key == DUK_HTHREAD_STRING_LENGTH(thr)) { + duk_hbuffer *h_val; + + DUK_DDDPRINT("-> found, key is 'length', length special behavior"); + + /* FIXME: buffer length should be writable and have special behavior + * like arrays. For now, make it read-only and use explicit methods + * to operate on buffer length. + */ + + h_val = duk_hobject_get_internal_value_buffer(thr->heap, obj); + DUK_ASSERT(h_val != NULL); + if (push_value) { + duk_push_number(ctx, (double) DUK_HBUFFER_GET_SIZE(h_val)); + } + out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL; DUK_ASSERT(!DUK_HOBJECT_HAS_SPECIAL_ARGUMENTS(obj)); return 1; /* cannot be arguments special */ @@ -1426,7 +1488,7 @@ static int duk__get_own_property_desc_raw(duk_hthread *thr, duk_hobject *obj, du duk_int16_t func_nargs = ((duk_hnativefunction *) obj)->nargs; duk_push_int(ctx, func_nargs == DUK_HNATIVEFUNCTION_NARGS_VARARGS ? 0 : func_nargs); } - out_desc->flags = 0; /* not enumerable */ + out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL; /* not enumerable */ DUK_ASSERT(!DUK_HOBJECT_HAS_SPECIAL_ARGUMENTS(obj)); return 1; /* cannot be arguments special */ @@ -1586,13 +1648,14 @@ static duk_tval *duk__shallow_fast_path_array_check_u32(duk_hobject *obj, duk_ui if ((!DUK_HOBJECT_HAS_SPECIAL_ARGUMENTS(obj)) && (!DUK_HOBJECT_HAS_SPECIAL_STRINGOBJ(obj)) && + (!DUK_HOBJECT_HAS_SPECIAL_BUFFEROBJ(obj)) && (DUK_HOBJECT_HAS_ARRAY_PART(obj)) && (key_idx < obj->a_size)) { /* technically required to check, but obj->a_size check covers this */ DUK_ASSERT(key_idx != 0xffffffffU); DUK_DDDPRINT("fast path attempt (key is an array index, no special " - "string/arguments behavior, object has array part, key " + "string/arguments/buffer behavior, object has array part, key " "inside array size)"); DUK_ASSERT(obj->a_size > 0); /* true even for key_idx == 0 */ @@ -1623,11 +1686,12 @@ static duk_tval *duk__shallow_fast_path_array_check_tval(duk_hobject *obj, duk_t if (DUK_TVAL_IS_NUMBER(key_tv) && (!DUK_HOBJECT_HAS_SPECIAL_ARGUMENTS(obj)) && (!DUK_HOBJECT_HAS_SPECIAL_STRINGOBJ(obj)) && + (!DUK_HOBJECT_HAS_SPECIAL_BUFFEROBJ(obj)) && (DUK_HOBJECT_HAS_ARRAY_PART(obj))) { double d; duk_uint32_t idx; - DUK_DDDPRINT("fast path attempt (key is a number, no special string/arguments " + DUK_DDDPRINT("fast path attempt (key is a number, no special string/arguments/buffer " "behavior, object has array part)"); /* FIXME: faster way to do this index coercion and validation? */ @@ -1727,6 +1791,7 @@ int duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key) { case DUK_TAG_STRING: { duk_hstring *h = DUK_TVAL_GET_STRING(tv_obj); + /* FIXME: arr_idx / number handling duplication */ if (DUK_TVAL_IS_NUMBER(tv_key)) { double t = DUK_TVAL_GET_NUMBER(tv_key); duk_uint32_t idx = (duk_uint32_t) t; @@ -1820,11 +1885,62 @@ int duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key) { break; } - /* Buffer doesn't have virtual properties at the moment (indices or length). */ + /* Buffer has virtual properties similar to string, but indexed values + * are numbers, not 1-byte buffers/strings which would perform badly. + */ case DUK_TAG_BUFFER: { + duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv_obj); + + /* FIXME: arr_idx / number handling duplication */ + if (DUK_TVAL_IS_NUMBER(tv_key)) { + double t = DUK_TVAL_GET_NUMBER(tv_key); + duk_uint32_t idx = (duk_uint32_t) t; + + DUK_DDDPRINT("base object is buffer, key is number %!T, int value %d", tv_key, (int) idx); + + if ((double) idx == t && /* is whole and >= 0 */ + idx < DUK_HBUFFER_GET_SIZE(h)) { /* and inside buffer */ + duk_push_int(ctx, ((duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(h))[idx]); + + DUK_DDDPRINT("-> %!T (base is a buffer, key is a whole number " + "inside buffer length -> return byte as number", + duk_get_tval(ctx, -1)); + return 1; + } + } + + duk_push_tval(ctx, tv_key); + duk_to_string(ctx, -1); + key = duk_get_hstring(ctx, -1); + arr_idx = DUK_HSTRING_GET_ARRIDX_FAST(key); + DUK_ASSERT(key != NULL); + + DUK_DDDPRINT("base object buffer, key is a non-fast-path number; after coercion key is %!T, arr_idx %d", duk_get_tval(ctx, -1), arr_idx); + + if (key == DUK_HTHREAD_STRING_LENGTH(thr)) { + duk_pop(ctx); /* [key] -> [] */ + duk_push_number(ctx, (double) DUK_HBUFFER_GET_SIZE(h)); /* [] -> [res] */ + + DUK_DDDPRINT("-> %!T (base is buffer, key is 'length' after coercion -> " + "return buffer length)", + duk_get_tval(ctx, -1)); + return 1; + } + + if (arr_idx != DUK__NO_ARRAY_INDEX && + arr_idx < DUK_HBUFFER_GET_SIZE(h)) { + duk_pop(ctx); /* [key] -> [] */ + duk_push_int(ctx, ((duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(h))[arr_idx]); + + DUK_DDDPRINT("-> %!T (base is buffer, key is an index inside buffer length " + "after coercion -> return byte as number)", + duk_get_tval(ctx, -1)); + return 1; + } + DUK_DDDPRINT("base object is a buffer, start lookup from buffer prototype"); curr = thr->builtins[DUK_BIDX_BUFFER_PROTOTYPE]; - break; + goto lookup; /* avoid double coercion */ } case DUK_TAG_POINTER: { @@ -2424,13 +2540,9 @@ int duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, du switch (DUK_TVAL_GET_TAG(tv_obj)) { case DUK_TAG_UNDEFINED: - case DUK_TAG_NULL: - case DUK_TAG_BUFFER: - case DUK_TAG_POINTER: { - /* FIXME: prototype objects for buffer and perhaps pointer? */ - + case DUK_TAG_NULL: { /* Note: unconditional throw */ - DUK_DDDPRINT("base object is undefined, null, buffer, or pointer -> reject (object=%!iT)", tv_obj); + DUK_DDDPRINT("base object is undefined or null -> reject (object=%!iT)", tv_obj); DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "invalid base reference for property write"); return 0; } @@ -2466,8 +2578,7 @@ int duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, du DUK_DDDPRINT("base object is a string, start lookup from string prototype"); curr = thr->builtins[DUK_BIDX_STRING_PROTOTYPE]; - goto lookup; /* avoid double coercion */ - + goto lookup; /* avoid double coercion */ } case DUK_TAG_OBJECT: { @@ -2478,6 +2589,47 @@ int duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, du break; } + case DUK_TAG_BUFFER: { + duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv_obj); + + /* + * Note: currently no fast path for array writes. + */ + + DUK_ASSERT(key == NULL); + duk_push_tval(ctx, tv_key); + duk_to_string(ctx, -1); + key = duk_get_hstring(ctx, -1); + arr_idx = DUK_HSTRING_GET_ARRIDX_FAST(key); + DUK_ASSERT(key != NULL); + + if (key == DUK_HTHREAD_STRING_LENGTH(thr)) { + goto fail_not_writable; + } + + if (arr_idx != DUK__NO_ARRAY_INDEX && + arr_idx < DUK_HBUFFER_GET_SIZE(h)) { + duk_uint8_t *data; + DUK_DDDPRINT("writing to buffer data at index %d", (int) arr_idx); + data = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(h); + duk_push_tval(ctx, tv_val); + data[arr_idx] = (duk_uint8_t) duk_to_number(ctx, -1); + duk_pop_2(ctx); + DUK_DDDPRINT("result: success (buffer data write)"); + return 1; + } + + DUK_DDDPRINT("base object is a buffer, start lookup from buffer prototype"); + curr = thr->builtins[DUK_BIDX_BUFFER_PROTOTYPE]; + goto lookup; /* avoid double coercion */ + } + + case DUK_TAG_POINTER: { + DUK_DDDPRINT("base object is a pointer, start lookup from pointer prototype"); + curr = thr->builtins[DUK_BIDX_POINTER_PROTOTYPE]; + break; + } + default: { /* number */ DUK_DDDPRINT("base object is a number, start lookup from number prototype"); @@ -2567,10 +2719,36 @@ int duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, du * Found existing own (non-inherited) plain property. * Do an access control check and update in place. */ + if (!(desc.flags & DUK_PROPDESC_FLAG_WRITABLE)) { DUK_DDPRINT("found existing own (non-inherited) plain property, but property is not writable"); goto fail_not_writable; } + if (desc.flags & DUK_PROPDESC_FLAG_VIRTUAL) { + DUK_DDPRINT("found existing own (non-inherited) virtual property, property is writable"); + if (DUK_HOBJECT_HAS_SPECIAL_BUFFEROBJ(curr)) { + duk_hbuffer *h; + + /* FIXME: lots of duplication here too */ + + DUK_DDPRINT("writable virtual property is in buffer object"); + h = duk_hobject_get_internal_value_buffer(thr->heap, curr); + DUK_ASSERT(h != NULL); + + if (arr_idx != DUK__NO_ARRAY_INDEX && + arr_idx < DUK_HBUFFER_GET_SIZE(h)) { + duk_uint8_t *data; + DUK_DDDPRINT("writing to buffer data at index %d", (int) arr_idx); + data = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(h); + duk_push_tval(ctx, tv_val); + data[arr_idx] = (duk_uint8_t) duk_to_number(ctx, -1); + duk_pop(ctx); + goto success_no_arguments_special; + } + } + + goto fail_internal; /* should not happen */ + } DUK_DDPRINT("put to existing own plain property, property is writable"); goto update_old; } @@ -2613,10 +2791,11 @@ int duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, du DUK_ASSERT(orig != NULL); - /* Currently there are no writable virtual properties, - * so the property must be concretely stored in either - * the array or the entry part. + /* Although there are writable virtual properties (e.g. plain buffer + * and buffer object number indices), they are handled before we come + * here. */ + DUK_ASSERT((desc.flags & DUK_PROPDESC_FLAG_VIRTUAL) == 0); DUK_ASSERT(desc.a_idx >= 0 || desc.e_idx >= 0); if (DUK_HOBJECT_HAS_SPECIAL_ARRAY(orig) && @@ -2979,6 +3158,14 @@ int duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, du } duk_pop(ctx); /* remove key */ return 0; + + fail_internal: + DUK_DDDPRINT("result: error, internal"); + if (throw_flag) { + DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "internal error"); + } + duk_pop(ctx); /* remove key */ + return 0; } /*