#include "devs_internal.h" #include bool devs_is_string(devs_ctx_t *ctx, value_t v) { unsigned tag; switch (devs_handle_type(v)) { case DEVS_HANDLE_TYPE_GC_OBJECT: tag = devs_gc_tag(devs_handle_ptr_value(ctx, v)); return tag == DEVS_GC_TAG_STRING || tag == DEVS_GC_TAG_STRING_JMP; case DEVS_HANDLE_TYPE_IMG_BUFFERISH: return !devs_bufferish_is_buffer(v); default: return false; } } bool devs_is_number(value_t v) { return devs_is_tagged_int(v) || devs_handle_type(v) == DEVS_HANDLE_TYPE_FLOAT64; } const devs_utf8_string_t *devs_string_get_utf8_struct(devs_ctx_t *ctx, value_t v) { switch (devs_handle_type(v)) { case DEVS_HANDLE_TYPE_GC_OBJECT: { void *ptr = devs_handle_ptr_value(ctx, v); if (devs_gc_tag(ptr) == DEVS_GC_TAG_STRING_JMP) { devs_string_jmp_t *s = ptr; return &s->inner; } return NULL; } case DEVS_HANDLE_TYPE_IMG_BUFFERISH: { unsigned idx = devs_handle_value(v); unsigned tp = (uint16_t)idx >> DEVS_STRIDX__SHIFT; idx &= (1 << DEVS_STRIDX__SHIFT) - 1; if (tp == DEVS_STRIDX_UTF8) return devs_img_get_string_jmp(ctx->img, idx); return NULL; } default: return NULL; } } const char *devs_string_get_utf8(devs_ctx_t *ctx, value_t v, unsigned *size) { switch (devs_handle_type(v)) { case DEVS_HANDLE_TYPE_GC_OBJECT: { void *ptr = devs_handle_ptr_value(ctx, v); if (devs_gc_tag(ptr) == DEVS_GC_TAG_STRING) { devs_string_t *s = ptr; if (size) *size = s->length; return s->data; } else if (devs_gc_tag(ptr) == DEVS_GC_TAG_STRING_JMP) { devs_string_jmp_t *s = ptr; if (size) *size = s->inner.size; return devs_utf8_string_data(&s->inner); } return NULL; } case DEVS_HANDLE_TYPE_IMG_BUFFERISH: return devs_bufferish_is_buffer(v) ? NULL : devs_get_static_utf8(ctx, devs_handle_value(v), size); default: return NULL; } } value_t devs_builtin_string(unsigned idx) { return devs_value_from_handle(DEVS_HANDLE_TYPE_IMG_BUFFERISH, (DEVS_STRIDX_BUILTIN << DEVS_STRIDX__SHIFT) | idx); } value_t devs_string_vsprintf(devs_ctx_t *ctx, const char *format, va_list ap) { if (strstr(format, "%-s")) JD_PANIC(); va_list ap2; va_copy(ap2, ap); unsigned len; int sz = jd_vsprintf_ext(NULL, 0, format, &len, ap); // len includes final NUL; devs_string_try_alloc() allocates the final NUL, but doesn't count it // in its len len--; sz--; value_t r; char *d = devs_string_prep(ctx, &r, sz, len); if (d) { sz = jd_vsprintf_ext(d, sz + 1, format, &len, ap2); len--; sz--; devs_string_finish(ctx, &r, sz, len); } return r; } value_t devs_string_sprintf(devs_ctx_t *ctx, const char *format, ...) { va_list arg; va_start(arg, format); value_t r = devs_string_vsprintf(ctx, format, arg); va_end(arg); return r; } value_t devs_string_from_utf8(devs_ctx_t *ctx, const uint8_t *utf8, unsigned size) { devs_any_string_t *s = devs_string_try_alloc_init(ctx, (const char *)utf8, size); if (s == NULL) { return devs_undefined; } else { return devs_value_from_gc_obj(ctx, s); } } static value_t buffer_to_string(devs_ctx_t *ctx, value_t v) { unsigned sz; const void *data = devs_bufferish_data(ctx, v, &sz); JD_ASSERT(data != NULL); unsigned maxbuf = 32; if (sz > maxbuf) { return devs_string_sprintf(ctx, "[Buffer[%u] %*p...]", sz, maxbuf, data); } else { return devs_string_sprintf(ctx, "[Buffer[%u] %*p]", sz, sz, data); } } value_t devs_value_to_string(devs_ctx_t *ctx, value_t v) { if (devs_is_string(ctx, v)) return v; uint32_t hv; switch (devs_handle_type(v)) { case DEVS_HANDLE_TYPE_FLOAT64: { char buf[64]; jd_print_double(buf, devs_value_to_double(ctx, v), 7); return devs_string_sprintf(ctx, "%s", buf); } case DEVS_HANDLE_TYPE_SPECIAL: switch ((hv = devs_handle_value(v))) { case DEVS_SPECIAL_NULL: return devs_builtin_string(DEVS_BUILTIN_STRING_NULL); case DEVS_SPECIAL_UNDEFINED: return devs_builtin_string(DEVS_BUILTIN_STRING_UNDEFINED); case DEVS_SPECIAL_FALSE: return devs_builtin_string(DEVS_BUILTIN_STRING_FALSE); case DEVS_SPECIAL_TRUE: return devs_builtin_string(DEVS_BUILTIN_STRING_TRUE); case DEVS_SPECIAL_NAN: return devs_builtin_string(DEVS_BUILTIN_STRING_NAN); case DEVS_SPECIAL_INF: return devs_builtin_string(DEVS_BUILTIN_STRING_INFINITY); case DEVS_SPECIAL_MINF: return devs_builtin_string(DEVS_BUILTIN_STRING_MINFINITY); default: { if (devs_handle_is_builtin(hv)) return devs_string_sprintf(ctx, "[Static Obj: %d]", (int)hv - DEVS_SPECIAL_BUILTIN_OBJ_FIRST); else if (devs_handle_is_throw_jmp(hv)) { return devs_string_sprintf(ctx, "[Throw: %x]", (unsigned)devs_handle_value(v)); } else JD_PANIC(); } } case DEVS_HANDLE_TYPE_FIBER: return devs_string_sprintf(ctx, "[Fiber: %x]", (unsigned)devs_handle_value(v)); case DEVS_HANDLE_TYPE_STATIC_FUNCTION: return devs_string_sprintf(ctx, "[Function: %s]", devs_img_fun_name(ctx->img, devs_handle_value(v))); case DEVS_HANDLE_TYPE_CLOSURE: return devs_string_sprintf(ctx, "[Closure: %s]", devs_img_fun_name(ctx->img, devs_handle_high_value(v))); case DEVS_HANDLE_TYPE_BOUND_FUNCTION: case DEVS_HANDLE_TYPE_BOUND_FUNCTION_STATIC: return devs_string_sprintf(ctx, "[Method: %s]", devs_img_fun_name(ctx->img, devs_handle_high_value(v))); case DEVS_HANDLE_TYPE_GC_OBJECT: switch (devs_gc_tag(devs_handle_ptr_value(ctx, v))) { case DEVS_GC_TAG_ARRAY: return devs_builtin_string(DEVS_BUILTIN_STRING_ARRAY); // TODO stringify array? case DEVS_GC_TAG_BOUND_FUNCTION: return devs_builtin_string(DEVS_BUILTIN_STRING_FUNCTION); // TODO? case DEVS_GC_TAG_BUFFER: return buffer_to_string(ctx, v); case DEVS_GC_TAG_PACKET: { devs_packet_t *pkt = devs_handle_ptr_value(ctx, v); return devs_string_sprintf(ctx, "[Packet: %s cmd=%x sz=%d]", devs_role_name(ctx, pkt->roleidx), pkt->service_command, pkt->payload->length); } case DEVS_GC_TAG_IMAGE: { devs_gimage_t *img = devs_handle_ptr_value(ctx, v); return devs_string_sprintf(ctx, "[Image: %dx%d (%d bpp)]", img->width, img->height, img->bpp); } case DEVS_GC_TAG_SHORT_MAP: case DEVS_GC_TAG_HALF_STATIC_MAP: case DEVS_GC_TAG_MAP: return devs_builtin_string(DEVS_BUILTIN_STRING_MAP); case DEVS_GC_TAG_BUILTIN_PROTO: // can't happen case DEVS_GC_TAG_STRING_JMP: // handled on top case DEVS_GC_TAG_STRING: // handled on top default: JD_PANIC(); } case DEVS_HANDLE_TYPE_IMG_BUFFERISH: JD_ASSERT(devs_bufferish_is_buffer(v)); return buffer_to_string(ctx, v); case DEVS_HANDLE_TYPE_ROLE: return devs_string_sprintf(ctx, "[Role: %s]", devs_role_name(ctx, devs_handle_value(v))); case DEVS_HANDLE_TYPE_ROLE_MEMBER: { unsigned roleidx; const devs_service_spec_t *spec = devs_value_to_service_spec(ctx, v); if (spec) return devs_string_sprintf(ctx, "[ServiceSpec: %s]", devs_img_get_utf8(ctx->img, spec->name_idx, NULL)); const devs_packet_spec_t *pkt = devs_decode_role_packet(ctx, v, &roleidx); if (roleidx == DEVS_ROLE_INVALID) { spec = devs_img_get_service_spec(ctx->img, devs_packet_spec_parent(ctx, pkt)); return devs_string_sprintf(ctx, "[PacketSpec: %s.%s]", devs_img_get_utf8(ctx->img, spec->name_idx, NULL), devs_img_get_utf8(ctx->img, pkt->name_idx, NULL)); } else return devs_string_sprintf(ctx, "[Role: %s.%s]", devs_role_name(ctx, roleidx), devs_img_get_utf8(ctx->img, pkt->name_idx, NULL)); } default: JD_PANIC(); } } static value_t devs_value_to_string_and_pin(devs_ctx_t *ctx, value_t a) { if (!devs_is_string(ctx, a)) { value_t tmp = devs_value_to_string(ctx, a); devs_value_pin(ctx, tmp); devs_value_unpin(ctx, a); return tmp; } else { return a; } } void devs_map_set_string_field(devs_ctx_t *ctx, devs_map_t *m, unsigned builtin_str, value_t msg) { devs_value_pin(ctx, msg); msg = devs_value_to_string_and_pin(ctx, msg); devs_map_set(ctx, m, devs_builtin_string(builtin_str), msg); devs_value_unpin(ctx, msg); } value_t devs_string_concat(devs_ctx_t *ctx, value_t a, value_t b) { bool dup = (a.u64 == b.u64); devs_value_pin(ctx, a); if (!dup) devs_value_pin(ctx, b); a = devs_value_to_string_and_pin(ctx, a); if (dup) b = a; else b = devs_value_to_string_and_pin(ctx, b); const char *ap, *bp; unsigned asz, bsz, alen, blen; ap = devs_string_get_utf8(ctx, a, &asz); bp = devs_string_get_utf8(ctx, b, &bsz); alen = devs_string_length(ctx, a); blen = devs_string_length(ctx, b); value_t r; if (ap == NULL || bp == NULL) { // strange... devs_invalid_program(ctx, 60126); r = devs_undefined; } else if (asz == 0) { r = b; } else if (bsz == 0) { r = a; } else { unsigned sz = asz + bsz; unsigned len = alen + blen; char *p = devs_string_prep(ctx, &r, sz, len); if (p) { memcpy(p, ap, asz); memcpy(p + asz, bp, bsz); devs_string_finish(ctx, &r, sz, len); } } devs_value_unpin(ctx, a); if (!dup) devs_value_unpin(ctx, b); return r; } static int sanitize_idx(int sz, int start) { if (start < 0) { start += sz; if (start < 0) start = 0; } if (start > sz) start = sz; return start; } value_t devs_string_slice(devs_ctx_t *ctx, value_t str, int start, int endp) { unsigned sz; const char *data = devs_string_get_utf8(ctx, str, &sz); if (!data) return devs_undefined; int slen = devs_string_length(ctx, str); start = sanitize_idx(slen, start); endp = sanitize_idx(slen, endp); int len = endp - start; if (len <= 0) return devs_builtin_string(DEVS_BUILTIN_STRING__EMPTY); if (start == 0 && len == slen) return str; start = devs_string_index(ctx, str, start); endp = devs_string_index(ctx, str, endp); if (start < 0) start = sz; // shouldn't happen if (endp < 0) endp = sz; devs_any_string_t *r = devs_string_try_alloc_init(ctx, data + start, endp - start); return devs_value_from_gc_obj(ctx, r); }