|
|
|
/*
|
|
|
|
* Symbol built-in
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "duk_internal.h"
|
|
|
|
|
|
|
|
#if defined(DUK_USE_SYMBOL_BUILTIN)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Constructor
|
|
|
|
*/
|
|
|
|
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_symbol_constructor_shared(duk_hthread *thr) {
|
|
|
|
const duk_uint8_t *desc;
|
|
|
|
duk_size_t len;
|
|
|
|
duk_uint8_t *buf;
|
|
|
|
duk_uint8_t *p;
|
|
|
|
duk_int_t magic;
|
|
|
|
|
|
|
|
magic = duk_get_current_magic(thr);
|
|
|
|
if (duk_is_undefined(thr, 0) && (magic == 0)) {
|
|
|
|
/* Symbol() accepts undefined and empty string, but they are
|
|
|
|
* treated differently.
|
|
|
|
*/
|
|
|
|
desc = NULL;
|
|
|
|
len = 0;
|
|
|
|
} else {
|
|
|
|
/* Symbol.for() coerces undefined to 'undefined' */
|
|
|
|
desc = (const duk_uint8_t *) duk_to_lstring(thr, 0, &len);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Maximum symbol data length:
|
|
|
|
* +1 initial byte (0x80 or 0x81)
|
|
|
|
* +len description
|
|
|
|
* +1 0xff after description, before unique suffix
|
|
|
|
* +17 autogenerated unique suffix: 'ffffffff-ffffffff' is longest
|
|
|
|
* +1 0xff after unique suffix for symbols with undefined description
|
|
|
|
*/
|
|
|
|
buf = (duk_uint8_t *) duk_push_fixed_buffer(thr, 1 + len + 1 + 17 + 1);
|
|
|
|
DUK_ASSERT(buf != NULL);
|
|
|
|
p = buf + 1;
|
|
|
|
DUK_ASSERT(desc != NULL || len == 0); /* may be NULL if len is 0 */
|
|
|
|
duk_memcpy_unsafe((void *) p, (const void *) desc, len);
|
|
|
|
p += len;
|
|
|
|
if (magic == 0) {
|
|
|
|
/* Symbol(): create unique symbol. Use two 32-bit values
|
|
|
|
* to avoid dependency on 64-bit types and 64-bit integer
|
|
|
|
* formatting (at least for now).
|
|
|
|
*/
|
|
|
|
if (++thr->heap->sym_counter[0] == 0) {
|
|
|
|
thr->heap->sym_counter[1]++;
|
|
|
|
}
|
|
|
|
p += DUK_SPRINTF((char *) p,
|
|
|
|
"\xFF"
|
|
|
|
"%lx-%lx",
|
|
|
|
(unsigned long) thr->heap->sym_counter[1],
|
|
|
|
(unsigned long) thr->heap->sym_counter[0]);
|
|
|
|
if (desc == NULL) {
|
|
|
|
/* Special case for 'undefined' description, trailing
|
|
|
|
* 0xff distinguishes from empty string description,
|
|
|
|
* but needs minimal special case handling elsewhere.
|
|
|
|
*/
|
|
|
|
*p++ = 0xff;
|
|
|
|
}
|
|
|
|
buf[0] = 0x81;
|
|
|
|
} else {
|
|
|
|
/* Symbol.for(): create a global symbol */
|
|
|
|
buf[0] = 0x80;
|
|
|
|
}
|
|
|
|
|
|
|
|
duk_push_lstring(thr, (const char *) buf, (duk_size_t) (p - buf));
|
|
|
|
DUK_DDD(DUK_DDDPRINT("created symbol: %!T", duk_get_tval(thr, -1)));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
DUK_LOCAL duk_hstring *duk__auto_unbox_symbol(duk_hthread *thr, duk_tval *tv_arg) {
|
|
|
|
duk_tval *tv;
|
|
|
|
duk_hobject *h_obj;
|
|
|
|
duk_hstring *h_str;
|
|
|
|
|
|
|
|
DUK_ASSERT(tv_arg != NULL);
|
|
|
|
|
|
|
|
/* XXX: add internal helper: duk_auto_unbox_tval(thr, tv, mask); */
|
|
|
|
/* XXX: add internal helper: duk_auto_unbox(thr, tv, idx); */
|
|
|
|
|
|
|
|
tv = tv_arg;
|
|
|
|
if (DUK_TVAL_IS_OBJECT(tv)) {
|
|
|
|
h_obj = DUK_TVAL_GET_OBJECT(tv);
|
|
|
|
DUK_ASSERT(h_obj != NULL);
|
|
|
|
if (DUK_HOBJECT_GET_HTYPE(h_obj) == DUK_HTYPE_SYMBOL_OBJECT) {
|
|
|
|
tv = duk_hobject_get_internal_value_tval_ptr(thr->heap, h_obj);
|
|
|
|
if (tv == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!DUK_TVAL_IS_STRING(tv)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
h_str = DUK_TVAL_GET_STRING(tv);
|
|
|
|
DUK_ASSERT(h_str != NULL);
|
|
|
|
|
|
|
|
/* Here symbol is more expected than not. */
|
|
|
|
if (DUK_UNLIKELY(!DUK_HSTRING_HAS_SYMBOL(h_str))) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return h_str;
|
|
|
|
}
|
|
|
|
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_symbol_tostring_shared(duk_hthread *thr) {
|
|
|
|
duk_hstring *h_str;
|
|
|
|
|
|
|
|
h_str = duk__auto_unbox_symbol(thr, DUK_HTHREAD_THIS_PTR(thr));
|
|
|
|
if (h_str == NULL) {
|
|
|
|
return DUK_RET_TYPE_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (duk_get_current_magic(thr) == 0) {
|
|
|
|
/* .toString() */
|
|
|
|
duk_push_symbol_descriptive_string(thr, h_str);
|
|
|
|
} else {
|
|
|
|
/* .valueOf() */
|
|
|
|
duk_push_hstring(thr, h_str);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_symbol_key_for(duk_hthread *thr) {
|
|
|
|
duk_hstring *h;
|
|
|
|
const duk_uint8_t *p;
|
|
|
|
|
|
|
|
/* Argument must be a symbol but not checked here. The initial byte
|
|
|
|
* check will catch non-symbol strings.
|
|
|
|
*/
|
|
|
|
h = duk_require_hstring(thr, 0);
|
|
|
|
DUK_ASSERT(h != NULL);
|
|
|
|
|
|
|
|
p = (const duk_uint8_t *) duk_hstring_get_data(h);
|
|
|
|
DUK_ASSERT(p != NULL);
|
|
|
|
|
|
|
|
/* Even for zero length strings there's at least one NUL byte so
|
|
|
|
* we can safely check the initial byte.
|
|
|
|
*/
|
|
|
|
if (p[0] == 0x80) {
|
|
|
|
/* Global symbol, return its key (bytes just after the initial byte). */
|
|
|
|
duk_push_lstring(thr, (const char *) (p + 1), (duk_size_t) (duk_hstring_get_bytelen(h) - 1));
|
|
|
|
return 1;
|
|
|
|
} else if (p[0] == 0x81 || p[0] == 0x82 || p[0] == 0xff) {
|
|
|
|
/* Local symbol or hidden symbol, return undefined. */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Covers normal strings and unknown initial bytes. */
|
|
|
|
return DUK_RET_TYPE_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_symbol_toprimitive(duk_hthread *thr) {
|
|
|
|
duk_hstring *h_str;
|
|
|
|
|
|
|
|
h_str = duk__auto_unbox_symbol(thr, DUK_HTHREAD_THIS_PTR(thr));
|
|
|
|
if (h_str == NULL) {
|
|
|
|
return DUK_RET_TYPE_ERROR;
|
|
|
|
}
|
|
|
|
duk_push_hstring(thr, h_str);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* DUK_USE_SYMBOL_BUILTIN */
|