mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
10fd8bdb70
This change resurrects ae5d06dc53
332 lines
9.3 KiB
C
332 lines
9.3 KiB
C
/*
|
|
* QuickJS Javascript Engine
|
|
*
|
|
* Copyright (c) 2017-2021 Fabrice Bellard
|
|
* Copyright (c) 2017-2021 Charlie Gordon
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
#include "libc/str/str.h"
|
|
#include "third_party/quickjs/internal.h"
|
|
#include "third_party/quickjs/libregexp.h"
|
|
#include "third_party/quickjs/quickjs.h"
|
|
|
|
asm(".ident\t\"\\n\\n\
|
|
QuickJS (MIT License)\\n\
|
|
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
|
Copyright (c) 2017-2021 Charlie Gordon\"");
|
|
asm(".include \"libc/disclaimer.inc\"");
|
|
/* clang-format off */
|
|
|
|
/* It is valid to call string_buffer_end() and all string_buffer functions even
|
|
if string_buffer_init() or another string_buffer function returns an error.
|
|
If the error_status is set, string_buffer_end() returns JS_EXCEPTION.
|
|
*/
|
|
int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size, int is_wide)
|
|
{
|
|
s->ctx = ctx;
|
|
s->size = size;
|
|
s->len = 0;
|
|
s->is_wide_char = is_wide;
|
|
s->error_status = 0;
|
|
s->str = js_alloc_string(ctx, size, is_wide);
|
|
if (UNLIKELY(!s->str)) {
|
|
s->size = 0;
|
|
return s->error_status = -1;
|
|
}
|
|
#ifdef DUMP_LEAKS
|
|
/* the StringBuffer may reallocate the JSString, only link it at the end */
|
|
list_del(&s->str->link);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int string_buffer_init(JSContext *ctx, StringBuffer *s, int size)
|
|
{
|
|
return string_buffer_init2(ctx, s, size, 0);
|
|
}
|
|
|
|
void string_buffer_free(StringBuffer *s)
|
|
{
|
|
js_free(s->ctx, s->str);
|
|
s->str = NULL;
|
|
}
|
|
|
|
int string_buffer_set_error(StringBuffer *s)
|
|
{
|
|
js_free(s->ctx, s->str);
|
|
s->str = NULL;
|
|
s->size = 0;
|
|
s->len = 0;
|
|
return s->error_status = -1;
|
|
}
|
|
|
|
int string_buffer_widen(StringBuffer *s, int size)
|
|
{
|
|
JSString *str;
|
|
size_t slack;
|
|
int i;
|
|
if (s->error_status)
|
|
return -1;
|
|
str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack);
|
|
if (!str)
|
|
return string_buffer_set_error(s);
|
|
size += slack >> 1;
|
|
for(i = s->len; i-- > 0;) {
|
|
str->u.str16[i] = str->u.str8[i];
|
|
}
|
|
s->is_wide_char = 1;
|
|
s->size = size;
|
|
s->str = str;
|
|
return 0;
|
|
}
|
|
|
|
int string_buffer_realloc(StringBuffer *s, int new_len, int c)
|
|
{
|
|
JSString *new_str;
|
|
int new_size;
|
|
size_t new_size_bytes, slack;
|
|
if (s->error_status)
|
|
return -1;
|
|
if (new_len > JS_STRING_LEN_MAX) {
|
|
JS_ThrowInternalError(s->ctx, "string too long");
|
|
return string_buffer_set_error(s);
|
|
}
|
|
new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX);
|
|
if (!s->is_wide_char && c >= 0x100) {
|
|
return string_buffer_widen(s, new_size);
|
|
}
|
|
new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char;
|
|
new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack);
|
|
if (!new_str)
|
|
return string_buffer_set_error(s);
|
|
new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX);
|
|
s->size = new_size;
|
|
s->str = new_str;
|
|
return 0;
|
|
}
|
|
|
|
int string_buffer_putc_slow(StringBuffer *s, uint32_t c)
|
|
{
|
|
if (UNLIKELY(s->len >= s->size)) {
|
|
if (string_buffer_realloc(s, s->len + 1, c))
|
|
return -1;
|
|
}
|
|
if (s->is_wide_char) {
|
|
s->str->u.str16[s->len++] = c;
|
|
} else if (c < 0x100) {
|
|
s->str->u.str8[s->len++] = c;
|
|
} else {
|
|
if (string_buffer_widen(s, s->size))
|
|
return -1;
|
|
s->str->u.str16[s->len++] = c;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* 0 <= c <= 0xff */
|
|
int string_buffer_putc8(StringBuffer *s, uint32_t c)
|
|
{
|
|
if (UNLIKELY(s->len >= s->size)) {
|
|
if (string_buffer_realloc(s, s->len + 1, c))
|
|
return -1;
|
|
}
|
|
if (s->is_wide_char) {
|
|
s->str->u.str16[s->len++] = c;
|
|
} else {
|
|
s->str->u.str8[s->len++] = c;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* 0 <= c <= 0xffff */
|
|
int string_buffer_putc16(StringBuffer *s, uint32_t c)
|
|
{
|
|
if (LIKELY(s->len < s->size)) {
|
|
if (s->is_wide_char) {
|
|
s->str->u.str16[s->len++] = c;
|
|
return 0;
|
|
} else if (c < 0x100) {
|
|
s->str->u.str8[s->len++] = c;
|
|
return 0;
|
|
}
|
|
}
|
|
return string_buffer_putc_slow(s, c);
|
|
}
|
|
|
|
/* 0 <= c <= 0x10ffff */
|
|
int string_buffer_putc(StringBuffer *s, uint32_t c)
|
|
{
|
|
if (UNLIKELY(c >= 0x10000)) {
|
|
/* surrogate pair */
|
|
c -= 0x10000;
|
|
if (string_buffer_putc16(s, (c >> 10) + 0xd800))
|
|
return -1;
|
|
c = (c & 0x3ff) + 0xdc00;
|
|
}
|
|
return string_buffer_putc16(s, c);
|
|
}
|
|
|
|
int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len)
|
|
{
|
|
int i;
|
|
if (s->len + len > s->size) {
|
|
if (string_buffer_realloc(s, s->len + len, 0))
|
|
return -1;
|
|
}
|
|
if (s->is_wide_char) {
|
|
for (i = 0; i < len; i++) {
|
|
s->str->u.str16[s->len + i] = p[i];
|
|
}
|
|
s->len += len;
|
|
} else {
|
|
memcpy(&s->str->u.str8[s->len], p, len);
|
|
s->len += len;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len)
|
|
{
|
|
int c = 0, i;
|
|
for (i = 0; i < len; i++) {
|
|
c |= p[i];
|
|
}
|
|
if (s->len + len > s->size) {
|
|
if (string_buffer_realloc(s, s->len + len, c))
|
|
return -1;
|
|
} else if (!s->is_wide_char && c >= 0x100) {
|
|
if (string_buffer_widen(s, s->size))
|
|
return -1;
|
|
}
|
|
if (s->is_wide_char) {
|
|
memcpy(&s->str->u.str16[s->len], p, len << 1);
|
|
s->len += len;
|
|
} else {
|
|
for (i = 0; i < len; i++) {
|
|
s->str->u.str8[s->len + i] = p[i];
|
|
}
|
|
s->len += len;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* appending an ASCII string */
|
|
int string_buffer_puts8(StringBuffer *s, const char *str)
|
|
{
|
|
return string_buffer_write8(s, (const uint8_t *)str, strlen(str));
|
|
}
|
|
|
|
int string_buffer_concat(StringBuffer *s, const JSString *p, uint32_t from, uint32_t to)
|
|
{
|
|
if (to <= from)
|
|
return 0;
|
|
if (p->is_wide_char)
|
|
return string_buffer_write16(s, p->u.str16 + from, to - from);
|
|
else
|
|
return string_buffer_write8(s, p->u.str8 + from, to - from);
|
|
}
|
|
|
|
int string_buffer_concat_value(StringBuffer *s, JSValueConst v)
|
|
{
|
|
JSString *p;
|
|
JSValue v1;
|
|
int res;
|
|
if (s->error_status) {
|
|
/* prevent exception overload */
|
|
return -1;
|
|
}
|
|
if (UNLIKELY(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
|
|
v1 = JS_ToString(s->ctx, v);
|
|
if (JS_IsException(v1))
|
|
return string_buffer_set_error(s);
|
|
p = JS_VALUE_GET_STRING(v1);
|
|
res = string_buffer_concat(s, p, 0, p->len);
|
|
JS_FreeValue(s->ctx, v1);
|
|
return res;
|
|
}
|
|
p = JS_VALUE_GET_STRING(v);
|
|
return string_buffer_concat(s, p, 0, p->len);
|
|
}
|
|
|
|
int string_buffer_concat_value_free(StringBuffer *s, JSValue v)
|
|
{
|
|
JSString *p;
|
|
int res;
|
|
if (s->error_status) {
|
|
/* prevent exception overload */
|
|
JS_FreeValue(s->ctx, v);
|
|
return -1;
|
|
}
|
|
if (UNLIKELY(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
|
|
v = JS_ToStringFree(s->ctx, v);
|
|
if (JS_IsException(v))
|
|
return string_buffer_set_error(s);
|
|
}
|
|
p = JS_VALUE_GET_STRING(v);
|
|
res = string_buffer_concat(s, p, 0, p->len);
|
|
JS_FreeValue(s->ctx, v);
|
|
return res;
|
|
}
|
|
|
|
int string_buffer_fill(StringBuffer *s, int c, int count)
|
|
{
|
|
/* XXX: optimize */
|
|
if (s->len + count > s->size) {
|
|
if (string_buffer_realloc(s, s->len + count, c))
|
|
return -1;
|
|
}
|
|
while (count-- > 0) {
|
|
if (string_buffer_putc16(s, c))
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
JSValue string_buffer_end(StringBuffer *s)
|
|
{
|
|
JSString *str;
|
|
str = s->str;
|
|
if (s->error_status)
|
|
return JS_EXCEPTION;
|
|
if (s->len == 0) {
|
|
js_free(s->ctx, str);
|
|
s->str = NULL;
|
|
return JS_AtomToString(s->ctx, JS_ATOM_empty_string);
|
|
}
|
|
if (s->len < s->size) {
|
|
/* smaller size so js_realloc should not fail, but OK if it does */
|
|
/* XXX: should add some slack to avoid unnecessary calls */
|
|
/* XXX: might need to use malloc+free to ensure smaller size */
|
|
str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) +
|
|
(s->len << s->is_wide_char) + 1 - s->is_wide_char);
|
|
if (str == NULL)
|
|
str = s->str;
|
|
s->str = str;
|
|
}
|
|
if (!s->is_wide_char)
|
|
str->u.str8[s->len] = 0;
|
|
#ifdef DUMP_LEAKS
|
|
list_add_tail(&str->link, &s->ctx->rt->string_list);
|
|
#endif
|
|
str->is_wide_char = s->is_wide_char;
|
|
str->len = s->len;
|
|
s->str = NULL;
|
|
return JS_MKPTR(JS_TAG_STRING, str);
|
|
}
|