cosmopolitan/third_party/duktape/duk_bi_symbol.c

171 lines
4.5 KiB
C

/*
* Symbol built-in
*/
#include "third_party/duktape/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_CLASS_NUMBER(h_obj) == DUK_HOBJECT_CLASS_SYMBOL) {
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 */