mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
10fd8bdb70
This change resurrects ae5d06dc53
2320 lines
70 KiB
C
2320 lines
70 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/assert.h"
|
|
#include "libc/fmt/conv.h"
|
|
#include "libc/fmt/fmt.h"
|
|
#include "libc/runtime/fenv.h"
|
|
#include "libc/str/str.h"
|
|
#include "third_party/gdtoa/gdtoa.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 */
|
|
|
|
static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len)
|
|
{
|
|
int c, i;
|
|
for(i = 0; i < len; i++) {
|
|
c = src1[i] - src2[i];
|
|
if (c != 0)
|
|
return c;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len)
|
|
{
|
|
int c, i;
|
|
for(i = 0; i < len; i++) {
|
|
c = src1[i] - src2[i];
|
|
if (c != 0)
|
|
return c;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Note: the string contents are uninitialized */
|
|
JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char)
|
|
{
|
|
JSString *str;
|
|
str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char);
|
|
if (UNLIKELY(!str))
|
|
return NULL;
|
|
str->header.ref_count = 1;
|
|
str->is_wide_char = is_wide_char;
|
|
str->len = max_len;
|
|
str->atom_type = 0;
|
|
str->hash = 0; /* optional but costless */
|
|
str->hash_next = 0; /* optional */
|
|
#ifdef DUMP_LEAKS
|
|
list_add_tail(&str->link, &rt->string_list);
|
|
#endif
|
|
return str;
|
|
}
|
|
|
|
JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char)
|
|
{
|
|
JSString *p;
|
|
p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char);
|
|
if (UNLIKELY(!p)) {
|
|
JS_ThrowOutOfMemory(ctx);
|
|
return NULL;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p)
|
|
{
|
|
#if 0 /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */
|
|
if (UNLIKELY(i == JS_ATOM_NULL)) {
|
|
p->header.ref_count = INT32_MAX / 2;
|
|
return;
|
|
}
|
|
#endif
|
|
uint32_t i = p->hash_next; /* atom_index */
|
|
if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {
|
|
JSAtomStruct *p0, *p1;
|
|
uint32_t h0;
|
|
h0 = p->hash & (rt->atom_hash_size - 1);
|
|
i = rt->atom_hash[h0];
|
|
p1 = rt->atom_array[i];
|
|
if (p1 == p) {
|
|
rt->atom_hash[h0] = p1->hash_next;
|
|
} else {
|
|
for(;;) {
|
|
assert(i != 0);
|
|
p0 = p1;
|
|
i = p1->hash_next;
|
|
p1 = rt->atom_array[i];
|
|
if (p1 == p) {
|
|
p0->hash_next = p1->hash_next;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* insert in free atom list */
|
|
rt->atom_array[i] = atom_set_free(rt->atom_free_index);
|
|
rt->atom_free_index = i;
|
|
/* free the string structure */
|
|
#ifdef DUMP_LEAKS
|
|
list_del(&p->link);
|
|
#endif
|
|
js_free_rt(rt, p);
|
|
rt->atom_count--;
|
|
assert(rt->atom_count >= 0);
|
|
}
|
|
|
|
/* same as JS_FreeValueRT() but faster */
|
|
void js_free_string(JSRuntime *rt, JSString *str)
|
|
{
|
|
if (--str->header.ref_count <= 0) {
|
|
if (str->atom_type) {
|
|
JS_FreeAtomStruct(rt, str);
|
|
} else {
|
|
#ifdef DUMP_LEAKS
|
|
list_del(&str->link);
|
|
#endif
|
|
js_free_rt(rt, str);
|
|
}
|
|
}
|
|
}
|
|
|
|
JSValue js_new_string8(JSContext *ctx, const uint8_t *buf, int len)
|
|
{
|
|
JSString *str;
|
|
if (len <= 0) {
|
|
return JS_AtomToString(ctx, JS_ATOM_empty_string);
|
|
}
|
|
str = js_alloc_string(ctx, len, 0);
|
|
if (!str)
|
|
return JS_EXCEPTION;
|
|
memcpy(str->u.str8, buf, len);
|
|
str->u.str8[len] = '\0';
|
|
return JS_MKPTR(JS_TAG_STRING, str);
|
|
}
|
|
|
|
JSValue js_new_string16(JSContext *ctx, const uint16_t *buf, int len)
|
|
{
|
|
JSString *str;
|
|
str = js_alloc_string(ctx, len, 1);
|
|
if (!str)
|
|
return JS_EXCEPTION;
|
|
memcpy(str->u.str16, buf, len * 2);
|
|
return JS_MKPTR(JS_TAG_STRING, str);
|
|
}
|
|
|
|
JSValue js_new_string_char(JSContext *ctx, uint16_t c)
|
|
{
|
|
if (c < 0x100) {
|
|
uint8_t ch8 = c;
|
|
return js_new_string8(ctx, &ch8, 1);
|
|
} else {
|
|
uint16_t ch16 = c;
|
|
return js_new_string16(ctx, &ch16, 1);
|
|
}
|
|
}
|
|
|
|
int js_string_memcmp(const JSString *p1, const JSString *p2, int len)
|
|
{
|
|
int res;
|
|
if (LIKELY(!p1->is_wide_char)) {
|
|
if (LIKELY(!p2->is_wide_char))
|
|
res = memcmp(p1->u.str8, p2->u.str8, len);
|
|
else
|
|
res = -memcmp16_8(p2->u.str16, p1->u.str8, len);
|
|
} else {
|
|
if (!p2->is_wide_char)
|
|
res = memcmp16_8(p1->u.str16, p2->u.str8, len);
|
|
else
|
|
res = memcmp16(p1->u.str16, p2->u.str16, len);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* return < 0, 0 or > 0 */
|
|
int js_string_compare(JSContext *ctx, const JSString *p1, const JSString *p2)
|
|
{
|
|
int res, len;
|
|
len = min_int(p1->len, p2->len);
|
|
res = js_string_memcmp(p1, p2, len);
|
|
if (res == 0) {
|
|
if (p1->len == p2->len)
|
|
res = 0;
|
|
else if (p1->len < p2->len)
|
|
res = -1;
|
|
else
|
|
res = 1;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
#ifdef CONFIG_BIGNUM
|
|
|
|
JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix)
|
|
{
|
|
JSValue ret;
|
|
bf_t a_s, *a;
|
|
char *str;
|
|
int saved_sign;
|
|
a = JS_ToBigInt(ctx, &a_s, val);
|
|
if (!a)
|
|
return JS_EXCEPTION;
|
|
saved_sign = a->sign;
|
|
if (a->expn == BF_EXP_ZERO)
|
|
a->sign = 0;
|
|
str = bf_ftoa(NULL, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC |
|
|
BF_FTOA_JS_QUIRKS);
|
|
a->sign = saved_sign;
|
|
JS_FreeBigInt(ctx, a, &a_s);
|
|
if (!str)
|
|
return JS_ThrowOutOfMemory(ctx);
|
|
ret = JS_NewString(ctx, str);
|
|
bf_free(ctx->bf_ctx, str);
|
|
return ret;
|
|
}
|
|
|
|
JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val)
|
|
{
|
|
return js_bigint_to_string1(ctx, val, 10);
|
|
}
|
|
|
|
JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix, limb_t prec, bf_flags_t flags)
|
|
{
|
|
JSValue val, ret;
|
|
bf_t a_s, *a;
|
|
char *str;
|
|
int saved_sign;
|
|
val = JS_ToNumeric(ctx, val1);
|
|
if (JS_IsException(val))
|
|
return val;
|
|
a = JS_ToBigFloat(ctx, &a_s, val);
|
|
saved_sign = a->sign;
|
|
if (a->expn == BF_EXP_ZERO)
|
|
a->sign = 0;
|
|
flags |= BF_FTOA_JS_QUIRKS;
|
|
if ((flags & BF_FTOA_FORMAT_MASK) == BF_FTOA_FORMAT_FREE_MIN) {
|
|
/* Note: for floating point numbers with a radix which is not
|
|
a power of two, the current precision is used to compute
|
|
the number of digits. */
|
|
if ((radix & (radix - 1)) != 0) {
|
|
bf_t r_s, *r = &r_s;
|
|
int prec, flags1;
|
|
/* must round first */
|
|
if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
|
|
prec = ctx->fp_env.prec;
|
|
flags1 = ctx->fp_env.flags &
|
|
(BF_FLAG_SUBNORMAL | (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT));
|
|
} else {
|
|
prec = 53;
|
|
flags1 = bf_set_exp_bits(11) | BF_FLAG_SUBNORMAL;
|
|
}
|
|
bf_init(ctx->bf_ctx, r);
|
|
bf_set(r, a);
|
|
bf_round(r, prec, flags1 | BF_RNDN);
|
|
str = bf_ftoa(NULL, r, radix, prec, flags1 | flags);
|
|
bf_delete(r);
|
|
} else {
|
|
str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags);
|
|
}
|
|
} else {
|
|
str = bf_ftoa(NULL, a, radix, prec, flags);
|
|
}
|
|
a->sign = saved_sign;
|
|
if (a == &a_s)
|
|
bf_delete(a);
|
|
JS_FreeValue(ctx, val);
|
|
if (!str)
|
|
return JS_ThrowOutOfMemory(ctx);
|
|
ret = JS_NewString(ctx, str);
|
|
bf_free(ctx->bf_ctx, str);
|
|
return ret;
|
|
}
|
|
|
|
JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val)
|
|
{
|
|
return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);
|
|
}
|
|
|
|
JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val, limb_t prec, int flags)
|
|
{
|
|
JSValue ret;
|
|
bfdec_t *a;
|
|
char *str;
|
|
int saved_sign;
|
|
a = JS_ToBigDecimal(ctx, val);
|
|
saved_sign = a->sign;
|
|
if (a->expn == BF_EXP_ZERO)
|
|
a->sign = 0;
|
|
str = bfdec_ftoa(NULL, a, prec, flags | BF_FTOA_JS_QUIRKS);
|
|
a->sign = saved_sign;
|
|
if (!str)
|
|
return JS_ThrowOutOfMemory(ctx);
|
|
ret = JS_NewString(ctx, str);
|
|
bf_free(ctx->bf_ctx, str);
|
|
return ret;
|
|
}
|
|
|
|
JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val)
|
|
{
|
|
return js_bigdecimal_to_string1(ctx, val, 0,
|
|
BF_RNDZ | BF_FTOA_FORMAT_FREE);
|
|
}
|
|
|
|
#endif /* CONFIG_BIGNUM */
|
|
|
|
/* 2 <= base <= 36 */
|
|
static char *i64toa(char *buf_end, int64_t n, unsigned int base)
|
|
{
|
|
char *q = buf_end;
|
|
int digit, is_neg;
|
|
is_neg = 0;
|
|
if (n < 0) {
|
|
is_neg = 1;
|
|
n = -n;
|
|
}
|
|
*--q = '\0';
|
|
do {
|
|
digit = (uint64_t)n % base;
|
|
n = (uint64_t)n / base;
|
|
if (digit < 10)
|
|
digit += '0';
|
|
else
|
|
digit += 'a' - 10;
|
|
*--q = digit;
|
|
} while (n != 0);
|
|
if (is_neg)
|
|
*--q = '-';
|
|
return q;
|
|
}
|
|
|
|
/* buf1 contains the printf result */
|
|
static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf,
|
|
int rounding_mode, char *buf1, int buf1_size)
|
|
{
|
|
if (rounding_mode != FE_TONEAREST)
|
|
fesetround(rounding_mode);
|
|
snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d);
|
|
if (rounding_mode != FE_TONEAREST)
|
|
fesetround(FE_TONEAREST);
|
|
*sign = (buf1[0] == '-');
|
|
/* mantissa */
|
|
buf[0] = buf1[1];
|
|
if (n_digits > 1)
|
|
memcpy(buf + 1, buf1 + 3, n_digits - 1);
|
|
buf[n_digits] = '\0';
|
|
/* exponent */
|
|
*decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1;
|
|
}
|
|
|
|
/* maximum buffer size for js_dtoa */
|
|
#define JS_DTOA_BUF_SIZE 128
|
|
|
|
/* needed because ecvt usually limits the number of digits to
|
|
17. Return the number of digits. */
|
|
static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf,
|
|
BOOL is_fixed)
|
|
{
|
|
int rounding_mode;
|
|
char buf_tmp[JS_DTOA_BUF_SIZE];
|
|
if (!is_fixed) {
|
|
unsigned int n_digits_min, n_digits_max;
|
|
/* find the minimum amount of digits (XXX: inefficient but simple) */
|
|
n_digits_min = 1;
|
|
n_digits_max = 17;
|
|
while (n_digits_min < n_digits_max) {
|
|
n_digits = (n_digits_min + n_digits_max) / 2;
|
|
js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST,
|
|
buf_tmp, sizeof(buf_tmp));
|
|
if (strtod(buf_tmp, NULL) == d) {
|
|
/* no need to keep the trailing zeros */
|
|
while (n_digits >= 2 && buf[n_digits - 1] == '0')
|
|
n_digits--;
|
|
n_digits_max = n_digits;
|
|
} else {
|
|
n_digits_min = n_digits + 1;
|
|
}
|
|
}
|
|
n_digits = n_digits_max;
|
|
rounding_mode = FE_TONEAREST;
|
|
} else {
|
|
rounding_mode = FE_TONEAREST;
|
|
#ifdef CONFIG_PRINTF_RNDN
|
|
{
|
|
char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE];
|
|
int decpt1, sign1, decpt2, sign2;
|
|
/* The JS rounding is specified as round to nearest ties away
|
|
from zero (RNDNA), but in printf the "ties" case is not
|
|
specified (for example it is RNDN for glibc, RNDNA for
|
|
Windows), so we must round manually. */
|
|
js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST,
|
|
buf_tmp, sizeof(buf_tmp));
|
|
/* XXX: could use 2 digits to reduce the average running time */
|
|
if (buf1[n_digits] == '5') {
|
|
js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD,
|
|
buf_tmp, sizeof(buf_tmp));
|
|
js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD,
|
|
buf_tmp, sizeof(buf_tmp));
|
|
if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) {
|
|
/* exact result: round away from zero */
|
|
if (sign1)
|
|
rounding_mode = FE_DOWNWARD;
|
|
else
|
|
rounding_mode = FE_UPWARD;
|
|
}
|
|
}
|
|
}
|
|
#endif /* CONFIG_PRINTF_RNDN */
|
|
}
|
|
js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode,
|
|
buf_tmp, sizeof(buf_tmp));
|
|
return n_digits;
|
|
}
|
|
|
|
static int js_fcvt1(char *buf, int buf_size, double d, int n_digits,
|
|
int rounding_mode)
|
|
{
|
|
int n;
|
|
if (rounding_mode != FE_TONEAREST)
|
|
fesetround(rounding_mode);
|
|
n = snprintf(buf, buf_size, "%.*f", n_digits, d);
|
|
if (rounding_mode != FE_TONEAREST)
|
|
fesetround(FE_TONEAREST);
|
|
assert(n < buf_size);
|
|
return n;
|
|
}
|
|
|
|
static void js_fcvt(char *buf, int buf_size, double d, int n_digits)
|
|
{
|
|
int rounding_mode;
|
|
rounding_mode = FE_TONEAREST;
|
|
#ifdef CONFIG_PRINTF_RNDN
|
|
{
|
|
int n1, n2;
|
|
char buf1[JS_DTOA_BUF_SIZE];
|
|
char buf2[JS_DTOA_BUF_SIZE];
|
|
/* The JS rounding is specified as round to nearest ties away from
|
|
zero (RNDNA), but in printf the "ties" case is not specified
|
|
(for example it is RNDN for glibc, RNDNA for Windows), so we
|
|
must round manually. */
|
|
n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_TONEAREST);
|
|
rounding_mode = FE_TONEAREST;
|
|
/* XXX: could use 2 digits to reduce the average running time */
|
|
if (buf1[n1 - 1] == '5') {
|
|
n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_DOWNWARD);
|
|
n2 = js_fcvt1(buf2, sizeof(buf2), d, n_digits + 1, FE_UPWARD);
|
|
if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) {
|
|
/* exact result: round away from zero */
|
|
if (buf1[0] == '-')
|
|
rounding_mode = FE_DOWNWARD;
|
|
else
|
|
rounding_mode = FE_UPWARD;
|
|
}
|
|
}
|
|
}
|
|
#endif /* CONFIG_PRINTF_RNDN */
|
|
js_fcvt1(buf, buf_size, d, n_digits, rounding_mode);
|
|
}
|
|
|
|
/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough.
|
|
XXX: radix != 10 is only supported for small integers
|
|
*/
|
|
static void js_dtoa1(char *buf, double d, int radix, int n_digits, int flags)
|
|
{
|
|
char *q;
|
|
if (!isfinite(d)) {
|
|
if (isnan(d)) {
|
|
strcpy(buf, "NaN");
|
|
} else {
|
|
q = buf;
|
|
if (d < 0)
|
|
*q++ = '-';
|
|
strcpy(q, "Infinity");
|
|
}
|
|
} else if (flags == JS_DTOA_VAR_FORMAT) {
|
|
int64_t i64;
|
|
char buf1[70], *ptr;
|
|
i64 = (int64_t)d;
|
|
if (d != i64 || i64 > MAX_SAFE_INTEGER || i64 < -MAX_SAFE_INTEGER)
|
|
goto generic_conv;
|
|
/* fast path for integers */
|
|
ptr = i64toa(buf1 + sizeof(buf1), i64, radix);
|
|
strcpy(buf, ptr);
|
|
} else {
|
|
if (d == 0.0)
|
|
d = 0.0; /* convert -0 to 0 */
|
|
if (flags == JS_DTOA_FRAC_FORMAT) {
|
|
js_fcvt(buf, JS_DTOA_BUF_SIZE, d, n_digits);
|
|
} else {
|
|
char buf1[JS_DTOA_BUF_SIZE];
|
|
int sign, decpt, k, n, i, p, n_max;
|
|
BOOL is_fixed;
|
|
generic_conv:
|
|
is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT);
|
|
if (is_fixed) {
|
|
n_max = n_digits;
|
|
} else {
|
|
n_max = 21;
|
|
}
|
|
/* the number has k digits (k >= 1) */
|
|
k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed);
|
|
n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */
|
|
q = buf;
|
|
if (sign)
|
|
*q++ = '-';
|
|
if (flags & JS_DTOA_FORCE_EXP)
|
|
goto force_exp;
|
|
if (n >= 1 && n <= n_max) {
|
|
if (k <= n) {
|
|
memcpy(q, buf1, k);
|
|
q += k;
|
|
for(i = 0; i < (n - k); i++)
|
|
*q++ = '0';
|
|
*q = '\0';
|
|
} else {
|
|
/* k > n */
|
|
memcpy(q, buf1, n);
|
|
q += n;
|
|
*q++ = '.';
|
|
for(i = 0; i < (k - n); i++)
|
|
*q++ = buf1[n + i];
|
|
*q = '\0';
|
|
}
|
|
} else if (n >= -5 && n <= 0) {
|
|
*q++ = '0';
|
|
*q++ = '.';
|
|
for(i = 0; i < -n; i++)
|
|
*q++ = '0';
|
|
memcpy(q, buf1, k);
|
|
q += k;
|
|
*q = '\0';
|
|
} else {
|
|
force_exp:
|
|
/* exponential notation */
|
|
*q++ = buf1[0];
|
|
if (k > 1) {
|
|
*q++ = '.';
|
|
for(i = 1; i < k; i++)
|
|
*q++ = buf1[i];
|
|
}
|
|
*q++ = 'e';
|
|
p = n - 1;
|
|
if (p >= 0)
|
|
*q++ = '+';
|
|
sprintf(q, "%d", p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
JSValue js_dtoa(JSContext *ctx, double d, int radix, int n_digits, int flags)
|
|
{
|
|
char buf[JS_DTOA_BUF_SIZE];
|
|
js_dtoa1(buf, d, radix, n_digits, flags);
|
|
return JS_NewString(ctx, buf);
|
|
}
|
|
|
|
JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey)
|
|
{
|
|
uint32_t tag;
|
|
const char *str;
|
|
char buf[32];
|
|
tag = JS_VALUE_GET_NORM_TAG(val);
|
|
switch(tag) {
|
|
case JS_TAG_STRING:
|
|
return JS_DupValue(ctx, val);
|
|
case JS_TAG_INT:
|
|
snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val));
|
|
str = buf;
|
|
goto new_string;
|
|
case JS_TAG_BOOL:
|
|
return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
|
|
JS_ATOM_true : JS_ATOM_false);
|
|
case JS_TAG_NULL:
|
|
return JS_AtomToString(ctx, JS_ATOM_null);
|
|
case JS_TAG_UNDEFINED:
|
|
return JS_AtomToString(ctx, JS_ATOM_undefined);
|
|
case JS_TAG_EXCEPTION:
|
|
return JS_EXCEPTION;
|
|
case JS_TAG_OBJECT:
|
|
{
|
|
JSValue val1, ret;
|
|
val1 = JS_ToPrimitive(ctx, val, HINT_STRING);
|
|
if (JS_IsException(val1))
|
|
return val1;
|
|
ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey);
|
|
JS_FreeValue(ctx, val1);
|
|
return ret;
|
|
}
|
|
break;
|
|
case JS_TAG_FUNCTION_BYTECODE:
|
|
str = "[function bytecode]";
|
|
goto new_string;
|
|
case JS_TAG_SYMBOL:
|
|
if (is_ToPropertyKey) {
|
|
return JS_DupValue(ctx, val);
|
|
} else {
|
|
return JS_ThrowTypeError(ctx, "cannot convert symbol to string");
|
|
}
|
|
case JS_TAG_FLOAT64:
|
|
return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0,
|
|
JS_DTOA_VAR_FORMAT);
|
|
#ifdef CONFIG_BIGNUM
|
|
case JS_TAG_BIG_INT:
|
|
return ctx->rt->bigint_ops.to_string(ctx, val);
|
|
case JS_TAG_BIG_FLOAT:
|
|
return ctx->rt->bigfloat_ops.to_string(ctx, val);
|
|
case JS_TAG_BIG_DECIMAL:
|
|
return ctx->rt->bigdecimal_ops.to_string(ctx, val);
|
|
#endif
|
|
default:
|
|
str = "[unsupported type]";
|
|
new_string:
|
|
return JS_NewString(ctx, str);
|
|
}
|
|
}
|
|
|
|
JSValue JS_ToString(JSContext *ctx, JSValueConst val)
|
|
{
|
|
return JS_ToStringInternal(ctx, val, FALSE);
|
|
}
|
|
|
|
JSValue JS_ToStringFree(JSContext *ctx, JSValue val)
|
|
{
|
|
JSValue ret;
|
|
ret = JS_ToString(ctx, val);
|
|
JS_FreeValue(ctx, val);
|
|
return ret;
|
|
}
|
|
|
|
JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val)
|
|
{
|
|
if (JS_IsUndefined(val) || JS_IsNull(val))
|
|
return JS_ToStringFree(ctx, val);
|
|
return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL);
|
|
}
|
|
|
|
JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val)
|
|
{
|
|
return JS_ToStringInternal(ctx, val, TRUE);
|
|
}
|
|
|
|
int string_getc(const JSString *p, int *pidx)
|
|
{
|
|
int idx, c, c1;
|
|
idx = *pidx;
|
|
if (p->is_wide_char) {
|
|
c = p->u.str16[idx++];
|
|
if (c >= 0xd800 && c < 0xdc00 && idx < p->len) {
|
|
c1 = p->u.str16[idx];
|
|
if (c1 >= 0xdc00 && c1 < 0xe000) {
|
|
c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
|
|
idx++;
|
|
}
|
|
}
|
|
} else {
|
|
c = p->u.str8[idx++];
|
|
}
|
|
*pidx = idx;
|
|
return c;
|
|
}
|
|
|
|
int string_cmp(JSString *p1, JSString *p2, int x1, int x2, int len)
|
|
{
|
|
int i, c1, c2;
|
|
for (i = 0; i < len; i++) {
|
|
if ((c1 = string_get(p1, x1 + i)) != (c2 = string_get(p2, x2 + i)))
|
|
return c1 - c2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int string_indexof_char(JSString *p, int c, int from)
|
|
{
|
|
/* assuming 0 <= from <= p->len */
|
|
int i, len = p->len;
|
|
if (p->is_wide_char) {
|
|
for (i = from; i < len; i++) {
|
|
if (p->u.str16[i] == c)
|
|
return i;
|
|
}
|
|
} else {
|
|
if ((c & ~0xff) == 0) {
|
|
for (i = from; i < len; i++) {
|
|
if (p->u.str8[i] == (uint8_t)c)
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int string_indexof(JSString *p1, JSString *p2, int from)
|
|
{
|
|
/* assuming 0 <= from <= p1->len */
|
|
int c, i, j, len1 = p1->len, len2 = p2->len;
|
|
if (len2 == 0)
|
|
return from;
|
|
for (i = from, c = string_get(p2, 0); i + len2 <= len1; i = j + 1) {
|
|
j = string_indexof_char(p1, c, i);
|
|
if (j < 0 || j + len2 > len1)
|
|
break;
|
|
if (!string_cmp(p1, p2, j + 1, 1, len2 - 1))
|
|
return j;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode)
|
|
{
|
|
if (!unicode || index >= p->len || !p->is_wide_char) {
|
|
index++;
|
|
} else {
|
|
int index32 = (int)index;
|
|
string_getc(p, &index32);
|
|
index = index32;
|
|
}
|
|
return index;
|
|
}
|
|
|
|
JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end)
|
|
{
|
|
int len = end - start;
|
|
if (start == 0 && end == p->len) {
|
|
return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
|
|
}
|
|
if (p->is_wide_char && len > 0) {
|
|
JSString *str;
|
|
int i;
|
|
uint16_t c = 0;
|
|
for (i = start; i < end; i++) {
|
|
c |= p->u.str16[i];
|
|
}
|
|
if (c > 0xFF)
|
|
return js_new_string16(ctx, p->u.str16 + start, len);
|
|
str = js_alloc_string(ctx, len, 0);
|
|
if (!str)
|
|
return JS_EXCEPTION;
|
|
for (i = 0; i < len; i++) {
|
|
str->u.str8[i] = p->u.str16[start + i];
|
|
}
|
|
str->u.str8[len] = '\0';
|
|
return JS_MKPTR(JS_TAG_STRING, str);
|
|
} else {
|
|
return js_new_string8(ctx, p->u.str8 + start, len);
|
|
}
|
|
}
|
|
|
|
static int js_string_get_own_property(JSContext *ctx,
|
|
JSPropertyDescriptor *desc,
|
|
JSValueConst obj, JSAtom prop)
|
|
{
|
|
JSObject *p;
|
|
JSString *p1;
|
|
uint32_t idx, ch;
|
|
|
|
/* This is a class exotic method: obj class_id is JS_CLASS_STRING */
|
|
if (__JS_AtomIsTaggedInt(prop)) {
|
|
p = JS_VALUE_GET_OBJ(obj);
|
|
if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {
|
|
p1 = JS_VALUE_GET_STRING(p->u.object_data);
|
|
idx = __JS_AtomToUInt32(prop);
|
|
if (idx < p1->len) {
|
|
if (desc) {
|
|
if (p1->is_wide_char)
|
|
ch = p1->u.str16[idx];
|
|
else
|
|
ch = p1->u.str8[idx];
|
|
desc->flags = JS_PROP_ENUMERABLE;
|
|
desc->value = js_new_string_char(ctx, ch);
|
|
desc->getter = JS_UNDEFINED;
|
|
desc->setter = JS_UNDEFINED;
|
|
}
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static int js_string_define_own_property(JSContext *ctx,
|
|
JSValueConst this_obj,
|
|
JSAtom prop, JSValueConst val,
|
|
JSValueConst getter,
|
|
JSValueConst setter, int flags)
|
|
{
|
|
uint32_t idx;
|
|
JSObject *p;
|
|
JSString *p1, *p2;
|
|
|
|
if (__JS_AtomIsTaggedInt(prop)) {
|
|
idx = __JS_AtomToUInt32(prop);
|
|
p = JS_VALUE_GET_OBJ(this_obj);
|
|
if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING)
|
|
goto def;
|
|
p1 = JS_VALUE_GET_STRING(p->u.object_data);
|
|
if (idx >= p1->len)
|
|
goto def;
|
|
if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags))
|
|
goto fail;
|
|
/* check that the same value is configured */
|
|
if (flags & JS_PROP_HAS_VALUE) {
|
|
if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
|
|
goto fail;
|
|
p2 = JS_VALUE_GET_STRING(val);
|
|
if (p2->len != 1)
|
|
goto fail;
|
|
if (string_get(p1, idx) != string_get(p2, 0)) {
|
|
fail:
|
|
return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");
|
|
}
|
|
}
|
|
return TRUE;
|
|
} else {
|
|
def:
|
|
return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,
|
|
flags | JS_PROP_NO_EXOTIC);
|
|
}
|
|
}
|
|
|
|
static int js_string_delete_property(JSContext *ctx,
|
|
JSValueConst obj, JSAtom prop)
|
|
{
|
|
uint32_t idx;
|
|
|
|
if (__JS_AtomIsTaggedInt(prop)) {
|
|
idx = __JS_AtomToUInt32(prop);
|
|
if (idx < js_string_obj_get_length(ctx, obj)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static const JSClassExoticMethods js_string_exotic_methods = {
|
|
.get_own_property = js_string_get_own_property,
|
|
.define_own_property = js_string_define_own_property,
|
|
.delete_property = js_string_delete_property,
|
|
};
|
|
|
|
JSValue js_string_constructor(JSContext *ctx, JSValueConst new_target,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue val, obj;
|
|
if (argc == 0) {
|
|
val = JS_AtomToString(ctx, JS_ATOM_empty_string);
|
|
} else {
|
|
if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) {
|
|
JSAtomStruct *p = JS_VALUE_GET_PTR(argv[0]);
|
|
val = JS_ConcatString3(ctx, "Symbol(", JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)), ")");
|
|
} else {
|
|
val = JS_ToString(ctx, argv[0]);
|
|
}
|
|
if (JS_IsException(val))
|
|
return val;
|
|
}
|
|
if (!JS_IsUndefined(new_target)) {
|
|
JSString *p1 = JS_VALUE_GET_STRING(val);
|
|
|
|
obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING);
|
|
if (!JS_IsException(obj)) {
|
|
JS_SetObjectData(ctx, obj, val);
|
|
JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);
|
|
}
|
|
return obj;
|
|
} else {
|
|
return val;
|
|
}
|
|
}
|
|
|
|
static JSValue js_thisStringValue(JSContext *ctx, JSValueConst this_val)
|
|
{
|
|
if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING)
|
|
return JS_DupValue(ctx, this_val);
|
|
|
|
if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
|
|
JSObject *p = JS_VALUE_GET_OBJ(this_val);
|
|
if (p->class_id == JS_CLASS_STRING) {
|
|
if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING)
|
|
return JS_DupValue(ctx, p->u.object_data);
|
|
}
|
|
}
|
|
return JS_ThrowTypeError(ctx, "not a string");
|
|
}
|
|
|
|
static JSValue js_string_fromCharCode(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
int i;
|
|
StringBuffer b_s, *b = &b_s;
|
|
string_buffer_init(ctx, b, argc);
|
|
for(i = 0; i < argc; i++) {
|
|
int32_t c;
|
|
if (JS_ToInt32(ctx, &c, argv[i]) || string_buffer_putc16(b, c & 0xffff)) {
|
|
string_buffer_free(b);
|
|
return JS_EXCEPTION;
|
|
}
|
|
}
|
|
return string_buffer_end(b);
|
|
}
|
|
|
|
static JSValue js_string_fromCodePoint(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
double d;
|
|
int i, c;
|
|
StringBuffer b_s, *b = &b_s;
|
|
/* XXX: could pre-compute string length if all arguments are JS_TAG_INT */
|
|
if (string_buffer_init(ctx, b, argc))
|
|
goto fail;
|
|
for(i = 0; i < argc; i++) {
|
|
if (JS_VALUE_GET_TAG(argv[i]) == JS_TAG_INT) {
|
|
c = JS_VALUE_GET_INT(argv[i]);
|
|
if (c < 0 || c > 0x10ffff)
|
|
goto range_error;
|
|
} else {
|
|
if (JS_ToFloat64(ctx, &d, argv[i]))
|
|
goto fail;
|
|
if (d < 0 || d > 0x10ffff || (c = (int)d) != d)
|
|
goto range_error;
|
|
}
|
|
if (string_buffer_putc(b, c))
|
|
goto fail;
|
|
}
|
|
return string_buffer_end(b);
|
|
range_error:
|
|
JS_ThrowRangeError(ctx, "invalid code point");
|
|
fail:
|
|
string_buffer_free(b);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_string_raw(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
// raw(temp,...a)
|
|
JSValue cooked, val, raw;
|
|
StringBuffer b_s, *b = &b_s;
|
|
int64_t i, n;
|
|
string_buffer_init(ctx, b, 0);
|
|
raw = JS_UNDEFINED;
|
|
cooked = JS_ToObject(ctx, argv[0]);
|
|
if (JS_IsException(cooked))
|
|
goto exception;
|
|
raw = JS_ToObjectFree(ctx, JS_GetProperty(ctx, cooked, JS_ATOM_raw));
|
|
if (JS_IsException(raw))
|
|
goto exception;
|
|
if (js_get_length64(ctx, &n, raw) < 0)
|
|
goto exception;
|
|
for (i = 0; i < n; i++) {
|
|
val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i));
|
|
if (JS_IsException(val))
|
|
goto exception;
|
|
string_buffer_concat_value_free(b, val);
|
|
if (i < n - 1 && i + 1 < argc) {
|
|
if (string_buffer_concat_value(b, argv[i + 1]))
|
|
goto exception;
|
|
}
|
|
}
|
|
JS_FreeValue(ctx, cooked);
|
|
JS_FreeValue(ctx, raw);
|
|
return string_buffer_end(b);
|
|
exception:
|
|
JS_FreeValue(ctx, cooked);
|
|
JS_FreeValue(ctx, raw);
|
|
string_buffer_free(b);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
/* only used in test262 */
|
|
JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
uint32_t start, end, i, n;
|
|
StringBuffer b_s, *b = &b_s;
|
|
if (JS_ToUint32(ctx, &start, argv[0]) ||
|
|
JS_ToUint32(ctx, &end, argv[1]))
|
|
return JS_EXCEPTION;
|
|
end = min_uint32(end, 0x10ffff + 1);
|
|
if (start > end) {
|
|
start = end;
|
|
}
|
|
n = end - start;
|
|
if (end > 0x10000) {
|
|
n += end - max_uint32(start, 0x10000);
|
|
}
|
|
if (string_buffer_init2(ctx, b, n, end >= 0x100))
|
|
return JS_EXCEPTION;
|
|
for(i = start; i < end; i++) {
|
|
string_buffer_putc(b, i);
|
|
}
|
|
return string_buffer_end(b);
|
|
}
|
|
|
|
#if 0
|
|
static JSValue js_string___isSpace(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
int c;
|
|
if (JS_ToInt32(ctx, &c, argv[0]))
|
|
return JS_EXCEPTION;
|
|
return JS_NewBool(ctx, lre_is_space(c));
|
|
}
|
|
#endif
|
|
|
|
static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue val, ret;
|
|
JSString *p;
|
|
int idx, c;
|
|
val = JS_ToStringCheckObject(ctx, this_val);
|
|
if (JS_IsException(val))
|
|
return val;
|
|
p = JS_VALUE_GET_STRING(val);
|
|
if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
|
|
JS_FreeValue(ctx, val);
|
|
return JS_EXCEPTION;
|
|
}
|
|
if (idx < 0 || idx >= p->len) {
|
|
ret = JS_NAN;
|
|
} else {
|
|
if (p->is_wide_char)
|
|
c = p->u.str16[idx];
|
|
else
|
|
c = p->u.str8[idx];
|
|
ret = JS_NewInt32(ctx, c);
|
|
}
|
|
JS_FreeValue(ctx, val);
|
|
return ret;
|
|
}
|
|
|
|
static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue val, ret;
|
|
JSString *p;
|
|
int idx, c;
|
|
val = JS_ToStringCheckObject(ctx, this_val);
|
|
if (JS_IsException(val))
|
|
return val;
|
|
p = JS_VALUE_GET_STRING(val);
|
|
if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
|
|
JS_FreeValue(ctx, val);
|
|
return JS_EXCEPTION;
|
|
}
|
|
if (idx < 0 || idx >= p->len) {
|
|
ret = js_new_string8(ctx, NULL, 0);
|
|
} else {
|
|
if (p->is_wide_char)
|
|
c = p->u.str16[idx];
|
|
else
|
|
c = p->u.str8[idx];
|
|
ret = js_new_string_char(ctx, c);
|
|
}
|
|
JS_FreeValue(ctx, val);
|
|
return ret;
|
|
}
|
|
|
|
static JSValue js_string_codePointAt(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue val, ret;
|
|
JSString *p;
|
|
int idx, c;
|
|
val = JS_ToStringCheckObject(ctx, this_val);
|
|
if (JS_IsException(val))
|
|
return val;
|
|
p = JS_VALUE_GET_STRING(val);
|
|
if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
|
|
JS_FreeValue(ctx, val);
|
|
return JS_EXCEPTION;
|
|
}
|
|
if (idx < 0 || idx >= p->len) {
|
|
ret = JS_UNDEFINED;
|
|
} else {
|
|
c = string_getc(p, &idx);
|
|
ret = JS_NewInt32(ctx, c);
|
|
}
|
|
JS_FreeValue(ctx, val);
|
|
return ret;
|
|
}
|
|
|
|
static JSValue js_string_concat(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue r;
|
|
int i;
|
|
/* XXX: Use more efficient method */
|
|
/* XXX: This method is OK if r has a single refcount */
|
|
/* XXX: should use string_buffer? */
|
|
r = JS_ToStringCheckObject(ctx, this_val);
|
|
for (i = 0; i < argc; i++) {
|
|
if (JS_IsException(r))
|
|
break;
|
|
r = JS_ConcatString(ctx, r, JS_DupValue(ctx, argv[i]));
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv, int lastIndexOf)
|
|
{
|
|
JSValue str, v;
|
|
int i, len, v_len, pos, start, stop, ret, inc;
|
|
JSString *p;
|
|
JSString *p1;
|
|
str = JS_ToStringCheckObject(ctx, this_val);
|
|
if (JS_IsException(str))
|
|
return str;
|
|
v = JS_ToString(ctx, argv[0]);
|
|
if (JS_IsException(v))
|
|
goto fail;
|
|
p = JS_VALUE_GET_STRING(str);
|
|
p1 = JS_VALUE_GET_STRING(v);
|
|
len = p->len;
|
|
v_len = p1->len;
|
|
if (lastIndexOf) {
|
|
pos = len - v_len;
|
|
if (argc > 1) {
|
|
double d;
|
|
if (JS_ToFloat64(ctx, &d, argv[1]))
|
|
goto fail;
|
|
if (!isnan(d)) {
|
|
if (d <= 0)
|
|
pos = 0;
|
|
else if (d < pos)
|
|
pos = d;
|
|
}
|
|
}
|
|
start = pos;
|
|
stop = 0;
|
|
inc = -1;
|
|
} else {
|
|
pos = 0;
|
|
if (argc > 1) {
|
|
if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))
|
|
goto fail;
|
|
}
|
|
start = pos;
|
|
stop = len - v_len;
|
|
inc = 1;
|
|
}
|
|
ret = -1;
|
|
if (len >= v_len && inc * (stop - start) >= 0) {
|
|
for (i = start;; i += inc) {
|
|
if (!string_cmp(p, p1, i, 0, v_len)) {
|
|
ret = i;
|
|
break;
|
|
}
|
|
if (i == stop)
|
|
break;
|
|
}
|
|
}
|
|
JS_FreeValue(ctx, str);
|
|
JS_FreeValue(ctx, v);
|
|
return JS_NewInt32(ctx, ret);
|
|
fail:
|
|
JS_FreeValue(ctx, str);
|
|
JS_FreeValue(ctx, v);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv, int magic)
|
|
{
|
|
JSValue str, v = JS_UNDEFINED;
|
|
int i, len, v_len, pos, start, stop, ret;
|
|
JSString *p;
|
|
JSString *p1;
|
|
str = JS_ToStringCheckObject(ctx, this_val);
|
|
if (JS_IsException(str))
|
|
return str;
|
|
ret = js_is_regexp(ctx, argv[0]);
|
|
if (ret) {
|
|
if (ret > 0)
|
|
JS_ThrowTypeError(ctx, "regex not supported");
|
|
goto fail;
|
|
}
|
|
v = JS_ToString(ctx, argv[0]);
|
|
if (JS_IsException(v))
|
|
goto fail;
|
|
p = JS_VALUE_GET_STRING(str);
|
|
p1 = JS_VALUE_GET_STRING(v);
|
|
len = p->len;
|
|
v_len = p1->len;
|
|
pos = (magic == 2) ? len : 0;
|
|
if (argc > 1 && !JS_IsUndefined(argv[1])) {
|
|
if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))
|
|
goto fail;
|
|
}
|
|
len -= v_len;
|
|
ret = 0;
|
|
if (magic == 0) {
|
|
start = pos;
|
|
stop = len;
|
|
} else {
|
|
if (magic == 1) {
|
|
if (pos > len)
|
|
goto done;
|
|
} else {
|
|
pos -= v_len;
|
|
}
|
|
start = stop = pos;
|
|
}
|
|
if (start >= 0 && start <= stop) {
|
|
for (i = start;; i++) {
|
|
if (!string_cmp(p, p1, i, 0, v_len)) {
|
|
ret = 1;
|
|
break;
|
|
}
|
|
if (i == stop)
|
|
break;
|
|
}
|
|
}
|
|
done:
|
|
JS_FreeValue(ctx, str);
|
|
JS_FreeValue(ctx, v);
|
|
return JS_NewBool(ctx, ret);
|
|
fail:
|
|
JS_FreeValue(ctx, str);
|
|
JS_FreeValue(ctx, v);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static int check_regexp_g_flag(JSContext *ctx, JSValueConst regexp)
|
|
{
|
|
int ret;
|
|
JSValue flags;
|
|
ret = js_is_regexp(ctx, regexp);
|
|
if (ret < 0)
|
|
return -1;
|
|
if (ret) {
|
|
flags = JS_GetProperty(ctx, regexp, JS_ATOM_flags);
|
|
if (JS_IsException(flags))
|
|
return -1;
|
|
if (JS_IsUndefined(flags) || JS_IsNull(flags)) {
|
|
JS_ThrowTypeError(ctx, "cannot convert to object");
|
|
return -1;
|
|
}
|
|
flags = JS_ToStringFree(ctx, flags);
|
|
if (JS_IsException(flags))
|
|
return -1;
|
|
ret = string_indexof_char(JS_VALUE_GET_STRING(flags), 'g', 0);
|
|
JS_FreeValue(ctx, flags);
|
|
if (ret < 0) {
|
|
JS_ThrowTypeError(ctx, "regexp must have the 'g' flag");
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static JSValue js_string_match(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv, int atom)
|
|
{
|
|
// match(rx), search(rx), matchAll(rx)
|
|
// atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll
|
|
JSValueConst O = this_val, regexp = argv[0], args[2];
|
|
JSValue matcher, S, rx, result, str;
|
|
int args_len;
|
|
if (JS_IsUndefined(O) || JS_IsNull(O))
|
|
return JS_ThrowTypeError(ctx, "cannot convert to object");
|
|
if (!JS_IsUndefined(regexp) && !JS_IsNull(regexp)) {
|
|
matcher = JS_GetProperty(ctx, regexp, atom);
|
|
if (JS_IsException(matcher))
|
|
return JS_EXCEPTION;
|
|
if (atom == JS_ATOM_Symbol_matchAll) {
|
|
if (check_regexp_g_flag(ctx, regexp) < 0) {
|
|
JS_FreeValue(ctx, matcher);
|
|
return JS_EXCEPTION;
|
|
}
|
|
}
|
|
if (!JS_IsUndefined(matcher) && !JS_IsNull(matcher)) {
|
|
return JS_CallFree(ctx, matcher, regexp, 1, &O);
|
|
}
|
|
}
|
|
S = JS_ToString(ctx, O);
|
|
if (JS_IsException(S))
|
|
return JS_EXCEPTION;
|
|
args_len = 1;
|
|
args[0] = regexp;
|
|
str = JS_UNDEFINED;
|
|
if (atom == JS_ATOM_Symbol_matchAll) {
|
|
str = JS_NewString(ctx, "g");
|
|
if (JS_IsException(str))
|
|
goto fail;
|
|
args[args_len++] = (JSValueConst)str;
|
|
}
|
|
rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args);
|
|
JS_FreeValue(ctx, str);
|
|
if (JS_IsException(rx)) {
|
|
fail:
|
|
JS_FreeValue(ctx, S);
|
|
return JS_EXCEPTION;
|
|
}
|
|
result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst *)&S);
|
|
JS_FreeValue(ctx, S);
|
|
return result;
|
|
}
|
|
|
|
JSValue js_string___GetSubstitution(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
// GetSubstitution(matched, str, position, captures, namedCaptures, rep)
|
|
JSValueConst matched, str, captures, namedCaptures, rep;
|
|
JSValue capture, name, s;
|
|
uint32_t position, len, matched_len, captures_len;
|
|
int i, j, j0, k, k1;
|
|
int c, c1;
|
|
StringBuffer b_s, *b = &b_s;
|
|
JSString *sp, *rp;
|
|
matched = argv[0];
|
|
str = argv[1];
|
|
captures = argv[3];
|
|
namedCaptures = argv[4];
|
|
rep = argv[5];
|
|
if (!JS_IsString(rep) || !JS_IsString(str))
|
|
return JS_ThrowTypeError(ctx, "not a string");
|
|
sp = JS_VALUE_GET_STRING(str);
|
|
rp = JS_VALUE_GET_STRING(rep);
|
|
string_buffer_init(ctx, b, 0);
|
|
captures_len = 0;
|
|
if (!JS_IsUndefined(captures)) {
|
|
if (js_get_length32(ctx, &captures_len, captures))
|
|
goto exception;
|
|
}
|
|
if (js_get_length32(ctx, &matched_len, matched))
|
|
goto exception;
|
|
if (JS_ToUint32(ctx, &position, argv[2]) < 0)
|
|
goto exception;
|
|
len = rp->len;
|
|
i = 0;
|
|
for(;;) {
|
|
j = string_indexof_char(rp, '$', i);
|
|
if (j < 0 || j + 1 >= len)
|
|
break;
|
|
string_buffer_concat(b, rp, i, j);
|
|
j0 = j++;
|
|
c = string_get(rp, j++);
|
|
if (c == '$') {
|
|
string_buffer_putc8(b, '$');
|
|
} else if (c == '&') {
|
|
if (string_buffer_concat_value(b, matched))
|
|
goto exception;
|
|
} else if (c == '`') {
|
|
string_buffer_concat(b, sp, 0, position);
|
|
} else if (c == '\'') {
|
|
string_buffer_concat(b, sp, position + matched_len, sp->len);
|
|
} else if (c >= '0' && c <= '9') {
|
|
k = c - '0';
|
|
if (j < len) {
|
|
c1 = string_get(rp, j);
|
|
if (c1 >= '0' && c1 <= '9') {
|
|
/* This behavior is specified in ES6 and refined in ECMA 2019 */
|
|
/* ECMA 2019 does not have the extra test, but
|
|
Test262 S15.5.4.11_A3_T1..3 require this behavior */
|
|
k1 = k * 10 + c1 - '0';
|
|
if (k1 >= 1 && k1 < captures_len) {
|
|
k = k1;
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
if (k >= 1 && k < captures_len) {
|
|
s = JS_GetPropertyInt64(ctx, captures, k);
|
|
if (JS_IsException(s))
|
|
goto exception;
|
|
if (!JS_IsUndefined(s)) {
|
|
if (string_buffer_concat_value_free(b, s))
|
|
goto exception;
|
|
}
|
|
} else {
|
|
goto norep;
|
|
}
|
|
} else if (c == '<' && !JS_IsUndefined(namedCaptures)) {
|
|
k = string_indexof_char(rp, '>', j);
|
|
if (k < 0)
|
|
goto norep;
|
|
name = js_sub_string(ctx, rp, j, k);
|
|
if (JS_IsException(name))
|
|
goto exception;
|
|
capture = JS_GetPropertyValue(ctx, namedCaptures, name);
|
|
if (JS_IsException(capture))
|
|
goto exception;
|
|
if (!JS_IsUndefined(capture)) {
|
|
if (string_buffer_concat_value_free(b, capture))
|
|
goto exception;
|
|
}
|
|
j = k + 1;
|
|
} else {
|
|
norep:
|
|
string_buffer_concat(b, rp, j0, j);
|
|
}
|
|
i = j;
|
|
}
|
|
string_buffer_concat(b, rp, i, rp->len);
|
|
return string_buffer_end(b);
|
|
exception:
|
|
string_buffer_free(b);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv,
|
|
int is_replaceAll)
|
|
{
|
|
// replace(rx, rep)
|
|
JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1];
|
|
JSValueConst args[6];
|
|
JSValue str, search_str, replaceValue_str, repl_str;
|
|
JSString *sp, *searchp;
|
|
StringBuffer b_s, *b = &b_s;
|
|
int pos, functionalReplace, endOfLastMatch;
|
|
BOOL is_first;
|
|
if (JS_IsUndefined(O) || JS_IsNull(O))
|
|
return JS_ThrowTypeError(ctx, "cannot convert to object");
|
|
search_str = JS_UNDEFINED;
|
|
replaceValue_str = JS_UNDEFINED;
|
|
repl_str = JS_UNDEFINED;
|
|
if (!JS_IsUndefined(searchValue) && !JS_IsNull(searchValue)) {
|
|
JSValue replacer;
|
|
if (is_replaceAll) {
|
|
if (check_regexp_g_flag(ctx, searchValue) < 0)
|
|
return JS_EXCEPTION;
|
|
}
|
|
replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace);
|
|
if (JS_IsException(replacer))
|
|
return JS_EXCEPTION;
|
|
if (!JS_IsUndefined(replacer) && !JS_IsNull(replacer)) {
|
|
args[0] = O;
|
|
args[1] = replaceValue;
|
|
return JS_CallFree(ctx, replacer, searchValue, 2, args);
|
|
}
|
|
}
|
|
string_buffer_init(ctx, b, 0);
|
|
str = JS_ToString(ctx, O);
|
|
if (JS_IsException(str))
|
|
goto exception;
|
|
search_str = JS_ToString(ctx, searchValue);
|
|
if (JS_IsException(search_str))
|
|
goto exception;
|
|
functionalReplace = JS_IsFunction(ctx, replaceValue);
|
|
if (!functionalReplace) {
|
|
replaceValue_str = JS_ToString(ctx, replaceValue);
|
|
if (JS_IsException(replaceValue_str))
|
|
goto exception;
|
|
}
|
|
sp = JS_VALUE_GET_STRING(str);
|
|
searchp = JS_VALUE_GET_STRING(search_str);
|
|
endOfLastMatch = 0;
|
|
is_first = TRUE;
|
|
for(;;) {
|
|
if (UNLIKELY(searchp->len == 0)) {
|
|
if (is_first)
|
|
pos = 0;
|
|
else if (endOfLastMatch >= sp->len)
|
|
pos = -1;
|
|
else
|
|
pos = endOfLastMatch + 1;
|
|
} else {
|
|
pos = string_indexof(sp, searchp, endOfLastMatch);
|
|
}
|
|
if (pos < 0) {
|
|
if (is_first) {
|
|
string_buffer_free(b);
|
|
JS_FreeValue(ctx, search_str);
|
|
JS_FreeValue(ctx, replaceValue_str);
|
|
return str;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (functionalReplace) {
|
|
args[0] = search_str;
|
|
args[1] = JS_NewInt32(ctx, pos);
|
|
args[2] = str;
|
|
repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_UNDEFINED, 3, args));
|
|
} else {
|
|
args[0] = search_str;
|
|
args[1] = str;
|
|
args[2] = JS_NewInt32(ctx, pos);
|
|
args[3] = JS_UNDEFINED;
|
|
args[4] = JS_UNDEFINED;
|
|
args[5] = replaceValue_str;
|
|
repl_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);
|
|
}
|
|
if (JS_IsException(repl_str))
|
|
goto exception;
|
|
string_buffer_concat(b, sp, endOfLastMatch, pos);
|
|
string_buffer_concat_value_free(b, repl_str);
|
|
endOfLastMatch = pos + searchp->len;
|
|
is_first = FALSE;
|
|
if (!is_replaceAll)
|
|
break;
|
|
}
|
|
string_buffer_concat(b, sp, endOfLastMatch, sp->len);
|
|
JS_FreeValue(ctx, search_str);
|
|
JS_FreeValue(ctx, replaceValue_str);
|
|
JS_FreeValue(ctx, str);
|
|
return string_buffer_end(b);
|
|
exception:
|
|
string_buffer_free(b);
|
|
JS_FreeValue(ctx, search_str);
|
|
JS_FreeValue(ctx, replaceValue_str);
|
|
JS_FreeValue(ctx, str);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_string_split(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
// split(sep, limit)
|
|
JSValueConst O = this_val, separator = argv[0], limit = argv[1];
|
|
JSValueConst args[2];
|
|
JSValue S, A, R, T;
|
|
uint32_t lim, lengthA;
|
|
int64_t p, q, s, r, e;
|
|
JSString *sp, *rp;
|
|
if (JS_IsUndefined(O) || JS_IsNull(O))
|
|
return JS_ThrowTypeError(ctx, "cannot convert to object");
|
|
S = JS_UNDEFINED;
|
|
A = JS_UNDEFINED;
|
|
R = JS_UNDEFINED;
|
|
if (!JS_IsUndefined(separator) && !JS_IsNull(separator)) {
|
|
JSValue splitter;
|
|
splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split);
|
|
if (JS_IsException(splitter))
|
|
return JS_EXCEPTION;
|
|
if (!JS_IsUndefined(splitter) && !JS_IsNull(splitter)) {
|
|
args[0] = O;
|
|
args[1] = limit;
|
|
return JS_CallFree(ctx, splitter, separator, 2, args);
|
|
}
|
|
}
|
|
S = JS_ToString(ctx, O);
|
|
if (JS_IsException(S))
|
|
goto exception;
|
|
A = JS_NewArray(ctx);
|
|
if (JS_IsException(A))
|
|
goto exception;
|
|
lengthA = 0;
|
|
if (JS_IsUndefined(limit)) {
|
|
lim = 0xffffffff;
|
|
} else {
|
|
if (JS_ToUint32(ctx, &lim, limit) < 0)
|
|
goto exception;
|
|
}
|
|
sp = JS_VALUE_GET_STRING(S);
|
|
s = sp->len;
|
|
R = JS_ToString(ctx, separator);
|
|
if (JS_IsException(R))
|
|
goto exception;
|
|
rp = JS_VALUE_GET_STRING(R);
|
|
r = rp->len;
|
|
p = 0;
|
|
if (lim == 0)
|
|
goto done;
|
|
if (JS_IsUndefined(separator))
|
|
goto add_tail;
|
|
if (s == 0) {
|
|
if (r != 0)
|
|
goto add_tail;
|
|
goto done;
|
|
}
|
|
q = p;
|
|
for (q = p; (q += !r) <= s - r - !r; q = p = e + r) {
|
|
e = string_indexof(sp, rp, q);
|
|
if (e < 0)
|
|
break;
|
|
T = js_sub_string(ctx, sp, p, e);
|
|
if (JS_IsException(T))
|
|
goto exception;
|
|
if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0)
|
|
goto exception;
|
|
if (lengthA == lim)
|
|
goto done;
|
|
}
|
|
add_tail:
|
|
T = js_sub_string(ctx, sp, p, s);
|
|
if (JS_IsException(T))
|
|
goto exception;
|
|
if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T,0 ) < 0)
|
|
goto exception;
|
|
done:
|
|
JS_FreeValue(ctx, S);
|
|
JS_FreeValue(ctx, R);
|
|
return A;
|
|
exception:
|
|
JS_FreeValue(ctx, A);
|
|
JS_FreeValue(ctx, S);
|
|
JS_FreeValue(ctx, R);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_string_substring(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue str, ret;
|
|
int a, b, start, end;
|
|
JSString *p;
|
|
str = JS_ToStringCheckObject(ctx, this_val);
|
|
if (JS_IsException(str))
|
|
return str;
|
|
p = JS_VALUE_GET_STRING(str);
|
|
if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, p->len, 0)) {
|
|
JS_FreeValue(ctx, str);
|
|
return JS_EXCEPTION;
|
|
}
|
|
b = p->len;
|
|
if (!JS_IsUndefined(argv[1])) {
|
|
if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, p->len, 0)) {
|
|
JS_FreeValue(ctx, str);
|
|
return JS_EXCEPTION;
|
|
}
|
|
}
|
|
if (a < b) {
|
|
start = a;
|
|
end = b;
|
|
} else {
|
|
start = b;
|
|
end = a;
|
|
}
|
|
ret = js_sub_string(ctx, p, start, end);
|
|
JS_FreeValue(ctx, str);
|
|
return ret;
|
|
}
|
|
|
|
static JSValue js_string_substr(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue str, ret;
|
|
int a, len, n;
|
|
JSString *p;
|
|
str = JS_ToStringCheckObject(ctx, this_val);
|
|
if (JS_IsException(str))
|
|
return str;
|
|
p = JS_VALUE_GET_STRING(str);
|
|
len = p->len;
|
|
if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, len)) {
|
|
JS_FreeValue(ctx, str);
|
|
return JS_EXCEPTION;
|
|
}
|
|
n = len - a;
|
|
if (!JS_IsUndefined(argv[1])) {
|
|
if (JS_ToInt32Clamp(ctx, &n, argv[1], 0, len - a, 0)) {
|
|
JS_FreeValue(ctx, str);
|
|
return JS_EXCEPTION;
|
|
}
|
|
}
|
|
ret = js_sub_string(ctx, p, a, a + n);
|
|
JS_FreeValue(ctx, str);
|
|
return ret;
|
|
}
|
|
|
|
static JSValue js_string_slice(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue str, ret;
|
|
int len, start, end;
|
|
JSString *p;
|
|
str = JS_ToStringCheckObject(ctx, this_val);
|
|
if (JS_IsException(str))
|
|
return str;
|
|
p = JS_VALUE_GET_STRING(str);
|
|
len = p->len;
|
|
if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) {
|
|
JS_FreeValue(ctx, str);
|
|
return JS_EXCEPTION;
|
|
}
|
|
end = len;
|
|
if (!JS_IsUndefined(argv[1])) {
|
|
if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) {
|
|
JS_FreeValue(ctx, str);
|
|
return JS_EXCEPTION;
|
|
}
|
|
}
|
|
ret = js_sub_string(ctx, p, start, max_int(end, start));
|
|
JS_FreeValue(ctx, str);
|
|
return ret;
|
|
}
|
|
|
|
static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv, int padEnd)
|
|
{
|
|
JSValue str, v = JS_UNDEFINED;
|
|
StringBuffer b_s, *b = &b_s;
|
|
JSString *p, *p1 = NULL;
|
|
int n, len, c = ' ';
|
|
str = JS_ToStringCheckObject(ctx, this_val);
|
|
if (JS_IsException(str))
|
|
goto fail1;
|
|
if (JS_ToInt32Sat(ctx, &n, argv[0]))
|
|
goto fail2;
|
|
p = JS_VALUE_GET_STRING(str);
|
|
len = p->len;
|
|
if (len >= n)
|
|
return str;
|
|
if (argc > 1 && !JS_IsUndefined(argv[1])) {
|
|
v = JS_ToString(ctx, argv[1]);
|
|
if (JS_IsException(v))
|
|
goto fail2;
|
|
p1 = JS_VALUE_GET_STRING(v);
|
|
if (p1->len == 0) {
|
|
JS_FreeValue(ctx, v);
|
|
return str;
|
|
}
|
|
if (p1->len == 1) {
|
|
c = string_get(p1, 0);
|
|
p1 = NULL;
|
|
}
|
|
}
|
|
if (n > JS_STRING_LEN_MAX) {
|
|
JS_ThrowInternalError(ctx, "string too long");
|
|
goto fail2;
|
|
}
|
|
if (string_buffer_init(ctx, b, n))
|
|
goto fail3;
|
|
n -= len;
|
|
if (padEnd) {
|
|
if (string_buffer_concat(b, p, 0, len))
|
|
goto fail;
|
|
}
|
|
if (p1) {
|
|
while (n > 0) {
|
|
int chunk = min_int(n, p1->len);
|
|
if (string_buffer_concat(b, p1, 0, chunk))
|
|
goto fail;
|
|
n -= chunk;
|
|
}
|
|
} else {
|
|
if (string_buffer_fill(b, c, n))
|
|
goto fail;
|
|
}
|
|
if (!padEnd) {
|
|
if (string_buffer_concat(b, p, 0, len))
|
|
goto fail;
|
|
}
|
|
JS_FreeValue(ctx, v);
|
|
JS_FreeValue(ctx, str);
|
|
return string_buffer_end(b);
|
|
fail:
|
|
string_buffer_free(b);
|
|
fail3:
|
|
JS_FreeValue(ctx, v);
|
|
fail2:
|
|
JS_FreeValue(ctx, str);
|
|
fail1:
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_string_repeat(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue str;
|
|
StringBuffer b_s, *b = &b_s;
|
|
JSString *p;
|
|
int64_t val;
|
|
int n, len;
|
|
str = JS_ToStringCheckObject(ctx, this_val);
|
|
if (JS_IsException(str))
|
|
goto fail;
|
|
if (JS_ToInt64Sat(ctx, &val, argv[0]))
|
|
goto fail;
|
|
if (val < 0 || val > 2147483647) {
|
|
JS_ThrowRangeError(ctx, "invalid repeat count");
|
|
goto fail;
|
|
}
|
|
n = val;
|
|
p = JS_VALUE_GET_STRING(str);
|
|
len = p->len;
|
|
if (len == 0 || n == 1)
|
|
return str;
|
|
if (val * len > JS_STRING_LEN_MAX) {
|
|
JS_ThrowInternalError(ctx, "string too long");
|
|
goto fail;
|
|
}
|
|
if (string_buffer_init2(ctx, b, n * len, p->is_wide_char))
|
|
goto fail;
|
|
if (len == 1) {
|
|
string_buffer_fill(b, string_get(p, 0), n);
|
|
} else {
|
|
while (n-- > 0) {
|
|
string_buffer_concat(b, p, 0, len);
|
|
}
|
|
}
|
|
JS_FreeValue(ctx, str);
|
|
return string_buffer_end(b);
|
|
|
|
fail:
|
|
JS_FreeValue(ctx, str);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_string_trim(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv, int magic)
|
|
{
|
|
JSValue str, ret;
|
|
int a, b, len;
|
|
JSString *p;
|
|
str = JS_ToStringCheckObject(ctx, this_val);
|
|
if (JS_IsException(str))
|
|
return str;
|
|
p = JS_VALUE_GET_STRING(str);
|
|
a = 0;
|
|
b = len = p->len;
|
|
if (magic & 1) {
|
|
while (a < len && lre_is_space(string_get(p, a)))
|
|
a++;
|
|
}
|
|
if (magic & 2) {
|
|
while (b > a && lre_is_space(string_get(p, b - 1)))
|
|
b--;
|
|
}
|
|
ret = js_sub_string(ctx, p, a, b);
|
|
JS_FreeValue(ctx, str);
|
|
return ret;
|
|
}
|
|
|
|
static JSValue js_string___quote(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
return JS_ToQuotedString(ctx, this_val);
|
|
}
|
|
|
|
/* return 0 if before the first char */
|
|
static int string_prevc(JSString *p, int *pidx)
|
|
{
|
|
int idx, c, c1;
|
|
idx = *pidx;
|
|
if (idx <= 0)
|
|
return 0;
|
|
idx--;
|
|
if (p->is_wide_char) {
|
|
c = p->u.str16[idx];
|
|
if (c >= 0xdc00 && c < 0xe000 && idx > 0) {
|
|
c1 = p->u.str16[idx - 1];
|
|
if (c1 >= 0xd800 && c1 <= 0xdc00) {
|
|
c = (((c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000;
|
|
idx--;
|
|
}
|
|
}
|
|
} else {
|
|
c = p->u.str8[idx];
|
|
}
|
|
*pidx = idx;
|
|
return c;
|
|
}
|
|
|
|
static BOOL test_final_sigma(JSString *p, int sigma_pos)
|
|
{
|
|
int k, c1;
|
|
|
|
/* before C: skip case ignorable chars and check there is
|
|
a cased letter */
|
|
k = sigma_pos;
|
|
for(;;) {
|
|
c1 = string_prevc(p, &k);
|
|
if (!lre_is_case_ignorable(c1))
|
|
break;
|
|
}
|
|
if (!lre_is_cased(c1))
|
|
return FALSE;
|
|
|
|
/* after C: skip case ignorable chars and check there is
|
|
no cased letter */
|
|
k = sigma_pos + 1;
|
|
for(;;) {
|
|
if (k >= p->len)
|
|
return TRUE;
|
|
c1 = string_getc(p, &k);
|
|
if (!lre_is_case_ignorable(c1))
|
|
break;
|
|
}
|
|
return !lre_is_cased(c1);
|
|
}
|
|
|
|
static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue a, b;
|
|
int cmp;
|
|
|
|
a = JS_ToStringCheckObject(ctx, this_val);
|
|
if (JS_IsException(a))
|
|
return JS_EXCEPTION;
|
|
b = JS_ToString(ctx, argv[0]);
|
|
if (JS_IsException(b)) {
|
|
JS_FreeValue(ctx, a);
|
|
return JS_EXCEPTION;
|
|
}
|
|
cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b));
|
|
JS_FreeValue(ctx, a);
|
|
JS_FreeValue(ctx, b);
|
|
return JS_NewInt32(ctx, cmp);
|
|
}
|
|
|
|
static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv, int to_lower)
|
|
{
|
|
JSValue val;
|
|
StringBuffer b_s, *b = &b_s;
|
|
JSString *p;
|
|
int i, c, j, l;
|
|
uint32_t res[LRE_CC_RES_LEN_MAX];
|
|
|
|
val = JS_ToStringCheckObject(ctx, this_val);
|
|
if (JS_IsException(val))
|
|
return val;
|
|
p = JS_VALUE_GET_STRING(val);
|
|
if (p->len == 0)
|
|
return val;
|
|
if (string_buffer_init(ctx, b, p->len))
|
|
goto fail;
|
|
for(i = 0; i < p->len;) {
|
|
c = string_getc(p, &i);
|
|
if (c == 0x3a3 && to_lower && test_final_sigma(p, i - 1)) {
|
|
res[0] = 0x3c2; /* final sigma */
|
|
l = 1;
|
|
} else {
|
|
l = lre_case_conv(res, c, to_lower);
|
|
}
|
|
for(j = 0; j < l; j++) {
|
|
if (string_buffer_putc(b, res[j]))
|
|
goto fail;
|
|
}
|
|
}
|
|
JS_FreeValue(ctx, val);
|
|
return string_buffer_end(b);
|
|
fail:
|
|
JS_FreeValue(ctx, val);
|
|
string_buffer_free(b);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
#ifdef CONFIG_ALL_UNICODE
|
|
|
|
/* return (-1, NULL) if exception, otherwise (len, buf) */
|
|
static int JS_ToUTF32String(JSContext *ctx, uint32_t **pbuf, JSValueConst val1)
|
|
{
|
|
JSValue val;
|
|
JSString *p;
|
|
uint32_t *buf;
|
|
int i, j, len;
|
|
|
|
val = JS_ToString(ctx, val1);
|
|
if (JS_IsException(val))
|
|
return -1;
|
|
p = JS_VALUE_GET_STRING(val);
|
|
len = p->len;
|
|
/* UTF32 buffer length is len minus the number of correct surrogates pairs */
|
|
buf = js_malloc(ctx, sizeof(buf[0]) * max_int(len, 1));
|
|
if (!buf) {
|
|
JS_FreeValue(ctx, val);
|
|
goto fail;
|
|
}
|
|
for(i = j = 0; i < len;)
|
|
buf[j++] = string_getc(p, &i);
|
|
JS_FreeValue(ctx, val);
|
|
*pbuf = buf;
|
|
return j;
|
|
fail:
|
|
*pbuf = NULL;
|
|
return -1;
|
|
}
|
|
|
|
static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len)
|
|
{
|
|
int i;
|
|
StringBuffer b_s, *b = &b_s;
|
|
if (string_buffer_init(ctx, b, len))
|
|
return JS_EXCEPTION;
|
|
for(i = 0; i < len; i++) {
|
|
if (string_buffer_putc(b, buf[i]))
|
|
goto fail;
|
|
}
|
|
return string_buffer_end(b);
|
|
fail:
|
|
string_buffer_free(b);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
const char *form, *p;
|
|
size_t form_len;
|
|
int is_compat, buf_len, out_len;
|
|
UnicodeNormalizationEnum n_type;
|
|
JSValue val;
|
|
uint32_t *buf, *out_buf;
|
|
|
|
val = JS_ToStringCheckObject(ctx, this_val);
|
|
if (JS_IsException(val))
|
|
return val;
|
|
buf_len = JS_ToUTF32String(ctx, &buf, val);
|
|
JS_FreeValue(ctx, val);
|
|
if (buf_len < 0)
|
|
return JS_EXCEPTION;
|
|
|
|
if (argc == 0 || JS_IsUndefined(argv[0])) {
|
|
n_type = UNICODE_NFC;
|
|
} else {
|
|
form = JS_ToCStringLen(ctx, &form_len, argv[0]);
|
|
if (!form)
|
|
goto fail1;
|
|
p = form;
|
|
if (p[0] != 'N' || p[1] != 'F')
|
|
goto bad_form;
|
|
p += 2;
|
|
is_compat = FALSE;
|
|
if (*p == 'K') {
|
|
is_compat = TRUE;
|
|
p++;
|
|
}
|
|
if (*p == 'C' || *p == 'D') {
|
|
n_type = UNICODE_NFC + is_compat * 2 + (*p - 'C');
|
|
if ((p + 1 - form) != form_len)
|
|
goto bad_form;
|
|
} else {
|
|
bad_form:
|
|
JS_FreeCString(ctx, form);
|
|
JS_ThrowRangeError(ctx, "bad normalization form");
|
|
fail1:
|
|
js_free(ctx, buf);
|
|
return JS_EXCEPTION;
|
|
}
|
|
JS_FreeCString(ctx, form);
|
|
}
|
|
|
|
out_len = unicode_normalize(&out_buf, buf, buf_len, n_type,
|
|
ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
|
|
js_free(ctx, buf);
|
|
if (out_len < 0)
|
|
return JS_EXCEPTION;
|
|
val = JS_NewUTF32String(ctx, out_buf, out_len);
|
|
js_free(ctx, out_buf);
|
|
return val;
|
|
}
|
|
#endif /* CONFIG_ALL_UNICODE */
|
|
|
|
/* also used for String.prototype.valueOf */
|
|
static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
return js_thisStringValue(ctx, this_val);
|
|
}
|
|
|
|
#if 0
|
|
static JSValue js_string___toStringCheckObject(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
return JS_ToStringCheckObject(ctx, argv[0]);
|
|
}
|
|
|
|
static JSValue js_string___toString(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
return JS_ToString(ctx, argv[0]);
|
|
}
|
|
|
|
static JSValue js_string___advanceStringIndex(JSContext *ctx, JSValueConst
|
|
this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue str;
|
|
int idx;
|
|
BOOL is_unicode;
|
|
JSString *p;
|
|
|
|
str = JS_ToString(ctx, argv[0]);
|
|
if (JS_IsException(str))
|
|
return str;
|
|
if (JS_ToInt32Sat(ctx, &idx, argv[1])) {
|
|
JS_FreeValue(ctx, str);
|
|
return JS_EXCEPTION;
|
|
}
|
|
is_unicode = JS_ToBool(ctx, argv[2]);
|
|
p = JS_VALUE_GET_STRING(str);
|
|
if (!is_unicode || (unsigned)idx >= p->len || !p->is_wide_char) {
|
|
idx++;
|
|
} else {
|
|
string_getc(p, &idx);
|
|
}
|
|
JS_FreeValue(ctx, str);
|
|
return JS_NewInt32(ctx, idx);
|
|
}
|
|
#endif
|
|
|
|
static JSValue js_string_iterator_next(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv,
|
|
BOOL *pdone, int magic)
|
|
{
|
|
JSArrayIteratorData *it;
|
|
uint32_t idx, c, start;
|
|
JSString *p;
|
|
it = JS_GetOpaque2(ctx, this_val, JS_CLASS_STRING_ITERATOR);
|
|
if (!it) {
|
|
*pdone = FALSE;
|
|
return JS_EXCEPTION;
|
|
}
|
|
if (JS_IsUndefined(it->obj))
|
|
goto done;
|
|
p = JS_VALUE_GET_STRING(it->obj);
|
|
idx = it->idx;
|
|
if (idx >= p->len) {
|
|
JS_FreeValue(ctx, it->obj);
|
|
it->obj = JS_UNDEFINED;
|
|
done:
|
|
*pdone = TRUE;
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
start = idx;
|
|
c = string_getc(p, (int *)&idx);
|
|
it->idx = idx;
|
|
*pdone = FALSE;
|
|
if (c <= 0xffff) {
|
|
return js_new_string_char(ctx, c);
|
|
} else {
|
|
return js_new_string16(ctx, p->u.str16 + start, 2);
|
|
}
|
|
}
|
|
|
|
/* ES6 Annex B 2.3.2 etc. */
|
|
enum {
|
|
magic_string_anchor,
|
|
magic_string_big,
|
|
magic_string_blink,
|
|
magic_string_bold,
|
|
magic_string_fixed,
|
|
magic_string_fontcolor,
|
|
magic_string_fontsize,
|
|
magic_string_italics,
|
|
magic_string_link,
|
|
magic_string_small,
|
|
magic_string_strike,
|
|
magic_string_sub,
|
|
magic_string_sup,
|
|
};
|
|
|
|
static JSValue js_string_CreateHTML(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv, int magic)
|
|
{
|
|
JSValue str;
|
|
const JSString *p;
|
|
StringBuffer b_s, *b = &b_s;
|
|
static struct { const char *tag, *attr; } const defs[] = {
|
|
{ "a", "name" }, { "big", NULL }, { "blink", NULL }, { "b", NULL },
|
|
{ "tt", NULL }, { "font", "color" }, { "font", "size" }, { "i", NULL },
|
|
{ "a", "href" }, { "small", NULL }, { "strike", NULL },
|
|
{ "sub", NULL }, { "sup", NULL },
|
|
};
|
|
str = JS_ToStringCheckObject(ctx, this_val);
|
|
if (JS_IsException(str))
|
|
return JS_EXCEPTION;
|
|
string_buffer_init(ctx, b, 7);
|
|
string_buffer_putc8(b, '<');
|
|
string_buffer_puts8(b, defs[magic].tag);
|
|
if (defs[magic].attr) {
|
|
// r += " " + attr + "=\"" + value + "\"";
|
|
JSValue value;
|
|
int i;
|
|
string_buffer_putc8(b, ' ');
|
|
string_buffer_puts8(b, defs[magic].attr);
|
|
string_buffer_puts8(b, "=\"");
|
|
value = JS_ToStringCheckObject(ctx, argv[0]);
|
|
if (JS_IsException(value)) {
|
|
JS_FreeValue(ctx, str);
|
|
string_buffer_free(b);
|
|
return JS_EXCEPTION;
|
|
}
|
|
p = JS_VALUE_GET_STRING(value);
|
|
for (i = 0; i < p->len; i++) {
|
|
int c = string_get(p, i);
|
|
if (c == '"') {
|
|
string_buffer_puts8(b, """);
|
|
} else {
|
|
string_buffer_putc16(b, c);
|
|
}
|
|
}
|
|
JS_FreeValue(ctx, value);
|
|
string_buffer_putc8(b, '\"');
|
|
}
|
|
// return r + ">" + str + "</" + tag + ">";
|
|
string_buffer_putc8(b, '>');
|
|
string_buffer_concat_value_free(b, str);
|
|
string_buffer_puts8(b, "</");
|
|
string_buffer_puts8(b, defs[magic].tag);
|
|
string_buffer_putc8(b, '>');
|
|
return string_buffer_end(b);
|
|
}
|
|
|
|
static const JSCFunctionListEntry js_string_proto_funcs[] = {
|
|
JS_PROP_INT32_DEF("length", 0, JS_PROP_CONFIGURABLE ),
|
|
JS_CFUNC_DEF("charCodeAt", 1, js_string_charCodeAt ),
|
|
JS_CFUNC_DEF("charAt", 1, js_string_charAt ),
|
|
JS_CFUNC_DEF("concat", 1, js_string_concat ),
|
|
JS_CFUNC_DEF("codePointAt", 1, js_string_codePointAt ),
|
|
JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ),
|
|
JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ),
|
|
JS_CFUNC_MAGIC_DEF("includes", 1, js_string_includes, 0 ),
|
|
JS_CFUNC_MAGIC_DEF("endsWith", 1, js_string_includes, 2 ),
|
|
JS_CFUNC_MAGIC_DEF("startsWith", 1, js_string_includes, 1 ),
|
|
JS_CFUNC_MAGIC_DEF("match", 1, js_string_match, JS_ATOM_Symbol_match ),
|
|
JS_CFUNC_MAGIC_DEF("matchAll", 1, js_string_match, JS_ATOM_Symbol_matchAll ),
|
|
JS_CFUNC_MAGIC_DEF("search", 1, js_string_match, JS_ATOM_Symbol_search ),
|
|
JS_CFUNC_DEF("split", 2, js_string_split ),
|
|
JS_CFUNC_DEF("substring", 2, js_string_substring ),
|
|
JS_CFUNC_DEF("substr", 2, js_string_substr ),
|
|
JS_CFUNC_DEF("slice", 2, js_string_slice ),
|
|
JS_CFUNC_DEF("repeat", 1, js_string_repeat ),
|
|
JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ),
|
|
JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ),
|
|
JS_CFUNC_MAGIC_DEF("padEnd", 1, js_string_pad, 1 ),
|
|
JS_CFUNC_MAGIC_DEF("padStart", 1, js_string_pad, 0 ),
|
|
JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ),
|
|
JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ),
|
|
JS_ALIAS_DEF("trimRight", "trimEnd" ),
|
|
JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ),
|
|
JS_ALIAS_DEF("trimLeft", "trimStart" ),
|
|
JS_CFUNC_DEF("toString", 0, js_string_toString ),
|
|
JS_CFUNC_DEF("valueOf", 0, js_string_toString ),
|
|
JS_CFUNC_DEF("__quote", 1, js_string___quote ),
|
|
JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ),
|
|
JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ),
|
|
JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ),
|
|
JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ),
|
|
JS_CFUNC_MAGIC_DEF("toLocaleUpperCase", 0, js_string_toLowerCase, 0 ),
|
|
JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ),
|
|
/* ES6 Annex B 2.3.2 etc. */
|
|
JS_CFUNC_MAGIC_DEF("anchor", 1, js_string_CreateHTML, magic_string_anchor ),
|
|
JS_CFUNC_MAGIC_DEF("big", 0, js_string_CreateHTML, magic_string_big ),
|
|
JS_CFUNC_MAGIC_DEF("blink", 0, js_string_CreateHTML, magic_string_blink ),
|
|
JS_CFUNC_MAGIC_DEF("bold", 0, js_string_CreateHTML, magic_string_bold ),
|
|
JS_CFUNC_MAGIC_DEF("fixed", 0, js_string_CreateHTML, magic_string_fixed ),
|
|
JS_CFUNC_MAGIC_DEF("fontcolor", 1, js_string_CreateHTML, magic_string_fontcolor ),
|
|
JS_CFUNC_MAGIC_DEF("fontsize", 1, js_string_CreateHTML, magic_string_fontsize ),
|
|
JS_CFUNC_MAGIC_DEF("italics", 0, js_string_CreateHTML, magic_string_italics ),
|
|
JS_CFUNC_MAGIC_DEF("link", 1, js_string_CreateHTML, magic_string_link ),
|
|
JS_CFUNC_MAGIC_DEF("small", 0, js_string_CreateHTML, magic_string_small ),
|
|
JS_CFUNC_MAGIC_DEF("strike", 0, js_string_CreateHTML, magic_string_strike ),
|
|
JS_CFUNC_MAGIC_DEF("sub", 0, js_string_CreateHTML, magic_string_sub ),
|
|
JS_CFUNC_MAGIC_DEF("sup", 0, js_string_CreateHTML, magic_string_sup ),
|
|
};
|
|
|
|
static const JSCFunctionListEntry js_string_iterator_proto_funcs[] = {
|
|
JS_ITERATOR_NEXT_DEF("next", 0, js_string_iterator_next, 0 ),
|
|
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "String Iterator", JS_PROP_CONFIGURABLE ),
|
|
};
|
|
|
|
#ifdef CONFIG_ALL_UNICODE
|
|
static const JSCFunctionListEntry js_string_proto_normalize[] = {
|
|
JS_CFUNC_DEF("normalize", 0, js_string_normalize ),
|
|
};
|
|
#endif
|
|
|
|
void JS_AddIntrinsicStringNormalize(JSContext *ctx)
|
|
{
|
|
#ifdef CONFIG_ALL_UNICODE
|
|
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_normalize,
|
|
countof(js_string_proto_normalize));
|
|
#endif
|
|
}
|
|
|
|
static const JSCFunctionListEntry js_string_funcs[] = {
|
|
JS_CFUNC_DEF("fromCharCode", 1, js_string_fromCharCode ),
|
|
JS_CFUNC_DEF("fromCodePoint", 1, js_string_fromCodePoint ),
|
|
JS_CFUNC_DEF("raw", 1, js_string_raw ),
|
|
//JS_CFUNC_DEF("__toString", 1, js_string___toString ),
|
|
//JS_CFUNC_DEF("__isSpace", 1, js_string___isSpace ),
|
|
//JS_CFUNC_DEF("__toStringCheckObject", 1, js_string___toStringCheckObject ),
|
|
//JS_CFUNC_DEF("__advanceStringIndex", 3, js_string___advanceStringIndex ),
|
|
//JS_CFUNC_DEF("__GetSubstitution", 6, js_string___GetSubstitution ),
|
|
};
|
|
|
|
void JS_AddIntrinsicString(JSContext *ctx)
|
|
{
|
|
JSValueConst obj;
|
|
ctx->class_proto[JS_CLASS_STRING] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
|
|
JS_CLASS_STRING);
|
|
JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_STRING], JS_AtomToString(ctx, JS_ATOM_empty_string));
|
|
obj = JS_NewGlobalCConstructor(ctx, "String", js_string_constructor, 1,
|
|
ctx->class_proto[JS_CLASS_STRING]);
|
|
JS_SetPropertyFunctionList(ctx, obj, js_string_funcs,
|
|
countof(js_string_funcs));
|
|
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_funcs,
|
|
countof(js_string_proto_funcs));
|
|
ctx->class_proto[JS_CLASS_STRING_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
|
|
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING_ITERATOR],
|
|
js_string_iterator_proto_funcs,
|
|
countof(js_string_iterator_proto_funcs));
|
|
}
|