Compare commits
1 Commits
warningfix
...
templatefi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5fad52d47 |
@@ -193,8 +193,10 @@ DEF( strict_neq, 1, 2, 1, none)
|
||||
DEF( and, 1, 2, 1, none)
|
||||
DEF( xor, 1, 2, 1, none)
|
||||
DEF( or, 1, 2, 1, none)
|
||||
/* template literal concatenation - pops N parts, pushes concatenated string */
|
||||
DEF(template_concat, 3, 0, 1, npop_u16)
|
||||
/* format template - format_string_cpool_idx(u32), expr_count(u16)
|
||||
Note: n_push=2 ensures stack has room for temp [format_str, arr] pair,
|
||||
even though we only leave 1 value (the result) on the stack. */
|
||||
DEF(format_template, 7, 0, 1, npop_u16)
|
||||
|
||||
/* Upvalue access (closures via outer_frame chain) */
|
||||
DEF( get_up, 4, 0, 1, u8_u16) /* depth:u8, slot:u16 -> value */
|
||||
|
||||
574
source/quickjs.c
574
source/quickjs.c
@@ -2000,6 +2000,77 @@ static inline int pjs_resize_array (void **parray, int elem_size, int *psize, in
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parser pretext - mutable string using system allocator (not JS heap).
|
||||
This avoids GC issues during parsing since GC can move JS heap objects. */
|
||||
typedef struct PPretext {
|
||||
uint32_t *data;
|
||||
int len;
|
||||
int cap;
|
||||
} PPretext;
|
||||
|
||||
/* Forward declarations for ppretext_end */
|
||||
static JSText *js_alloc_string (JSContext *ctx, int max_len);
|
||||
static inline void string_put (JSText *p, int idx, uint32_t c);
|
||||
|
||||
static PPretext *ppretext_init (int capacity) {
|
||||
PPretext *p = pjs_malloc (sizeof (PPretext));
|
||||
if (!p) return NULL;
|
||||
if (capacity <= 0) capacity = 32;
|
||||
p->data = pjs_malloc (capacity * sizeof (uint32_t));
|
||||
if (!p->data) { pjs_free (p); return NULL; }
|
||||
p->len = 0;
|
||||
p->cap = capacity;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void ppretext_free (PPretext *p) {
|
||||
if (p) {
|
||||
pjs_free (p->data);
|
||||
pjs_free (p);
|
||||
}
|
||||
}
|
||||
|
||||
static no_inline PPretext *ppretext_realloc (PPretext *p, int new_cap) {
|
||||
uint32_t *new_data = pjs_realloc (p->data, new_cap * sizeof (uint32_t));
|
||||
if (!new_data) return NULL;
|
||||
p->data = new_data;
|
||||
p->cap = new_cap;
|
||||
return p;
|
||||
}
|
||||
|
||||
static PPretext *ppretext_putc (PPretext *p, uint32_t c) {
|
||||
if (p->len >= p->cap) {
|
||||
int new_cap = max_int (p->len + 1, p->cap * 3 / 2);
|
||||
if (!ppretext_realloc (p, new_cap)) return NULL;
|
||||
}
|
||||
p->data[p->len++] = c;
|
||||
return p;
|
||||
}
|
||||
|
||||
static JSValue ppretext_end (JSContext *ctx, PPretext *p) {
|
||||
if (!p) return JS_EXCEPTION;
|
||||
int len = p->len;
|
||||
if (len == 0) {
|
||||
ppretext_free (p);
|
||||
return JS_KEY_empty;
|
||||
}
|
||||
|
||||
/* Allocate heap string (single allocation) */
|
||||
JSText *str = js_alloc_string (ctx, len);
|
||||
if (!str) {
|
||||
ppretext_free (p);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
for (int i = 0; i < len; i++) {
|
||||
string_put (str, i, p->data[i]);
|
||||
}
|
||||
str->hdr = objhdr_set_cap56 (str->hdr, len);
|
||||
str->hdr = objhdr_set_s (str->hdr, true);
|
||||
|
||||
ppretext_free (p);
|
||||
return JS_MKPTR (str);
|
||||
}
|
||||
|
||||
static no_inline int js_realloc_array (JSContext *ctx, void **parray, int elem_size, int *psize, int req_size) {
|
||||
int new_size;
|
||||
void *new_array;
|
||||
@@ -2080,6 +2151,28 @@ static inline int js_string_value_len (JSValue v) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Append a JSValue string to a PPretext (parser pretext) */
|
||||
static PPretext *ppretext_append_jsvalue (PPretext *p, JSValue str) {
|
||||
int len = js_string_value_len (str);
|
||||
for (int i = 0; i < len; i++) {
|
||||
uint32_t c = js_string_value_get (str, i);
|
||||
p = ppretext_putc (p, c);
|
||||
if (!p) return NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Append an integer to a PPretext */
|
||||
static PPretext *ppretext_append_int (PPretext *p, int n) {
|
||||
char buf[16];
|
||||
int len = snprintf (buf, sizeof (buf), "%d", n);
|
||||
for (int i = 0; i < len; i++) {
|
||||
p = ppretext_putc (p, buf[i]);
|
||||
if (!p) return NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Convert a JSValue string to a property key.
|
||||
For immediates, returns the value as-is (can be used directly as keys).
|
||||
For heap strings, returns interned version. */
|
||||
@@ -8424,44 +8517,118 @@ restart:
|
||||
}
|
||||
BREAK;
|
||||
|
||||
CASE (OP_template_concat) : {
|
||||
int n, i;
|
||||
JSValue out;
|
||||
CASE (OP_format_template) : {
|
||||
int expr_count = get_u16 (pc); pc += 2;
|
||||
uint32_t cpool_idx = get_u32 (pc); pc += 4;
|
||||
|
||||
n = get_u16 (pc);
|
||||
pc += 2;
|
||||
/* Expression values are on the stack. We'll process them in place,
|
||||
building the result string using pretext. */
|
||||
JSText *result = pretext_init (ctx, 64);
|
||||
if (!result) goto exception;
|
||||
|
||||
if (n <= 0) {
|
||||
*sp++ = JS_KEY_empty;
|
||||
BREAK;
|
||||
}
|
||||
/* Re-read format_str after pretext_init (may have triggered GC) */
|
||||
JSValue format_str = b->cpool[cpool_idx];
|
||||
|
||||
JSText *b = pretext_init (ctx, 64);
|
||||
if (!b) goto exception;
|
||||
/* Parse format string and substitute {N} with stringified stack values.
|
||||
Format string is like "hello {0} world {1}" for `hello ${a} world ${b}`.
|
||||
Each {N} refers to stack value at position N (0-indexed from base). */
|
||||
JSValue *expr_base = sp - expr_count;
|
||||
int fmt_len = js_string_value_len (format_str);
|
||||
int pos = 0;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
JSValue v = sp[i - n];
|
||||
JSValue s = js_cell_text (ctx, JS_NULL, 1, &v);
|
||||
if (JS_IsException (s)) {
|
||||
sp -= n;
|
||||
goto exception;
|
||||
while (pos < fmt_len) {
|
||||
/* Re-read format_str in case GC moved the cpool entry */
|
||||
format_str = b->cpool[cpool_idx];
|
||||
|
||||
/* Find next '{' */
|
||||
int brace_start = -1;
|
||||
for (int i = pos; i < fmt_len; i++) {
|
||||
if (js_string_value_get (format_str, i) == '{') {
|
||||
brace_start = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
b = pretext_concat_value (ctx, b, s);
|
||||
if (!b) {
|
||||
sp -= n;
|
||||
goto exception;
|
||||
|
||||
if (brace_start < 0) {
|
||||
/* No more braces, copy rest of string */
|
||||
format_str = b->cpool[cpool_idx]; /* Re-read */
|
||||
for (int i = pos; i < fmt_len; i++) {
|
||||
result = pretext_putc (ctx, result, js_string_value_get (format_str, i));
|
||||
if (!result) goto exception;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Copy text before brace */
|
||||
format_str = b->cpool[cpool_idx]; /* Re-read */
|
||||
for (int i = pos; i < brace_start; i++) {
|
||||
result = pretext_putc (ctx, result, js_string_value_get (format_str, i));
|
||||
if (!result) goto exception;
|
||||
}
|
||||
|
||||
/* Find closing '}' */
|
||||
format_str = b->cpool[cpool_idx]; /* Re-read */
|
||||
int brace_end = -1;
|
||||
for (int i = brace_start + 1; i < fmt_len; i++) {
|
||||
if (js_string_value_get (format_str, i) == '}') {
|
||||
brace_end = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (brace_end < 0) {
|
||||
/* No closing brace, copy '{' and continue */
|
||||
result = pretext_putc (ctx, result, '{');
|
||||
if (!result) goto exception;
|
||||
pos = brace_start + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Parse index from {N} */
|
||||
int idx = 0;
|
||||
format_str = b->cpool[cpool_idx]; /* Re-read */
|
||||
for (int i = brace_start + 1; i < brace_end; i++) {
|
||||
uint32_t c = js_string_value_get (format_str, i);
|
||||
if (c >= '0' && c <= '9') {
|
||||
idx = idx * 10 + (c - '0');
|
||||
} else {
|
||||
idx = -1; /* Invalid */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx >= 0 && idx < expr_count) {
|
||||
/* Valid index, stringify the stack value */
|
||||
JSValue val = expr_base[idx];
|
||||
JSValue str_val = JS_ToString (ctx, val);
|
||||
if (JS_IsException (str_val)) {
|
||||
goto exception;
|
||||
}
|
||||
/* Append stringified value to result */
|
||||
int str_len = js_string_value_len (str_val);
|
||||
for (int i = 0; i < str_len; i++) {
|
||||
result = pretext_putc (ctx, result, js_string_value_get (str_val, i));
|
||||
if (!result) goto exception;
|
||||
}
|
||||
} else {
|
||||
/* Invalid index, keep original {N} */
|
||||
format_str = b->cpool[cpool_idx]; /* Re-read */
|
||||
for (int i = brace_start; i <= brace_end; i++) {
|
||||
result = pretext_putc (ctx, result, js_string_value_get (format_str, i));
|
||||
if (!result) goto exception;
|
||||
}
|
||||
}
|
||||
|
||||
pos = brace_end + 1;
|
||||
}
|
||||
|
||||
out = pretext_end (ctx, b);
|
||||
if (JS_IsException (out)) {
|
||||
sp -= n;
|
||||
goto exception;
|
||||
}
|
||||
/* Finalize result string */
|
||||
JSValue result_str = pretext_end (ctx, result);
|
||||
if (JS_IsException (result_str)) goto exception;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
sp -= n;
|
||||
*sp++ = out;
|
||||
/* Pop expr values, push result */
|
||||
sp -= expr_count;
|
||||
*sp++ = result_str;
|
||||
}
|
||||
BREAK;
|
||||
|
||||
@@ -9329,11 +9496,11 @@ static int js_parse_error_reserved_identifier (JSParseState *s) {
|
||||
static __exception int js_parse_template_part (JSParseState *s,
|
||||
const uint8_t *p) {
|
||||
uint32_t c;
|
||||
JSText *b;
|
||||
PPretext *b;
|
||||
JSValue str;
|
||||
|
||||
/* p points to the first byte of the template part */
|
||||
b = pretext_init (s->ctx, 32);
|
||||
b = ppretext_init (32);
|
||||
if (!b) goto fail;
|
||||
for (;;) {
|
||||
if (p >= s->buf_end) goto unexpected_eof;
|
||||
@@ -9348,7 +9515,7 @@ static __exception int js_parse_template_part (JSParseState *s,
|
||||
break;
|
||||
}
|
||||
if (c == '\\') {
|
||||
b = pretext_putc (s->ctx, b, c);
|
||||
b = ppretext_putc (b, c);
|
||||
if (!b) goto fail;
|
||||
if (p >= s->buf_end) goto unexpected_eof;
|
||||
c = *p++;
|
||||
@@ -9367,10 +9534,10 @@ static __exception int js_parse_template_part (JSParseState *s,
|
||||
}
|
||||
p = p_next;
|
||||
}
|
||||
b = pretext_putc (s->ctx, b, c);
|
||||
b = ppretext_putc (b, c);
|
||||
if (!b) goto fail;
|
||||
}
|
||||
str = pretext_end (s->ctx, b);
|
||||
str = ppretext_end (s->ctx, b);
|
||||
if (JS_IsException (str)) return -1;
|
||||
s->token.val = TOK_TEMPLATE;
|
||||
s->token.u.str.sep = c;
|
||||
@@ -9381,18 +9548,19 @@ static __exception int js_parse_template_part (JSParseState *s,
|
||||
unexpected_eof:
|
||||
js_parse_error (s, "unexpected end of string");
|
||||
fail:
|
||||
ppretext_free (b);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static __exception int js_parse_string (JSParseState *s, int sep, BOOL do_throw, const uint8_t *p, JSToken *token, const uint8_t **pp) {
|
||||
int ret;
|
||||
uint32_t c;
|
||||
JSText *b;
|
||||
PPretext *b;
|
||||
const uint8_t *p_escape;
|
||||
JSValue str;
|
||||
|
||||
/* string */
|
||||
b = pretext_init (s->ctx, 32);
|
||||
b = ppretext_init (32);
|
||||
if (!b) goto fail;
|
||||
for (;;) {
|
||||
if (p >= s->buf_end) goto invalid_char;
|
||||
@@ -9484,10 +9652,10 @@ static __exception int js_parse_string (JSParseState *s, int sep, BOOL do_throw,
|
||||
if (c > 0x10FFFF) goto invalid_utf8;
|
||||
p = p_next;
|
||||
}
|
||||
b = pretext_putc (s->ctx, b, c);
|
||||
b = ppretext_putc (b, c);
|
||||
if (!b) goto fail;
|
||||
}
|
||||
str = pretext_end (s->ctx, b);
|
||||
str = ppretext_end (s->ctx, b);
|
||||
if (JS_IsException (str)) return -1;
|
||||
token->val = TOK_STRING;
|
||||
token->u.str.sep = c;
|
||||
@@ -9501,6 +9669,7 @@ invalid_utf8:
|
||||
invalid_char:
|
||||
if (do_throw) js_parse_error (s, "unexpected end of string");
|
||||
fail:
|
||||
ppretext_free (b);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -9512,17 +9681,17 @@ static inline BOOL token_is_pseudo_keyword (JSParseState *s, JSValue key) {
|
||||
static __exception int js_parse_regexp (JSParseState *s) {
|
||||
const uint8_t *p;
|
||||
BOOL in_class;
|
||||
JSText *b;
|
||||
JSText *b2;
|
||||
PPretext *b = NULL;
|
||||
PPretext *b2 = NULL;
|
||||
uint32_t c;
|
||||
JSValue body_str, flags_str;
|
||||
|
||||
p = s->buf_ptr;
|
||||
p++;
|
||||
in_class = FALSE;
|
||||
b = pretext_init (s->ctx, 32);
|
||||
b = ppretext_init (32);
|
||||
if (!b) return -1;
|
||||
b2 = pretext_init (s->ctx, 1);
|
||||
b2 = ppretext_init (1);
|
||||
if (!b2) goto fail;
|
||||
for (;;) {
|
||||
if (p >= s->buf_end) {
|
||||
@@ -9541,7 +9710,7 @@ static __exception int js_parse_regexp (JSParseState *s) {
|
||||
/* XXX: incorrect as the first character in a class */
|
||||
in_class = FALSE;
|
||||
} else if (c == '\\') {
|
||||
b = pretext_putc (s->ctx, b, c);
|
||||
b = ppretext_putc (b, c);
|
||||
if (!b) goto fail;
|
||||
c = *p++;
|
||||
if (c == '\n' || c == '\r')
|
||||
@@ -9571,7 +9740,7 @@ static __exception int js_parse_regexp (JSParseState *s) {
|
||||
}
|
||||
p = p_next;
|
||||
}
|
||||
b = pretext_putc (s->ctx, b, c);
|
||||
b = ppretext_putc (b, c);
|
||||
if (!b) goto fail;
|
||||
}
|
||||
|
||||
@@ -9587,13 +9756,13 @@ static __exception int js_parse_regexp (JSParseState *s) {
|
||||
}
|
||||
}
|
||||
if (!lre_js_is_ident_next (c)) break;
|
||||
b2 = pretext_putc (s->ctx, b2, c);
|
||||
b2 = ppretext_putc (b2, c);
|
||||
if (!b2) goto fail;
|
||||
p = p_next;
|
||||
}
|
||||
|
||||
body_str = pretext_end (s->ctx, b);
|
||||
flags_str = pretext_end (s->ctx, b2);
|
||||
body_str = ppretext_end (s->ctx, b);
|
||||
flags_str = ppretext_end (s->ctx, b2);
|
||||
if (JS_IsException (body_str) || JS_IsException (flags_str)) {
|
||||
return -1;
|
||||
}
|
||||
@@ -9603,10 +9772,13 @@ static __exception int js_parse_regexp (JSParseState *s) {
|
||||
s->buf_ptr = p;
|
||||
return 0;
|
||||
fail:
|
||||
ppretext_free (b);
|
||||
ppretext_free (b2);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static __exception int ident_realloc (JSContext *ctx, char **pbuf, size_t *psize, char *static_buf) {
|
||||
(void)ctx; /* unused - uses system allocator */
|
||||
char *buf, *new_buf;
|
||||
size_t size, new_size;
|
||||
|
||||
@@ -9617,11 +9789,11 @@ static __exception int ident_realloc (JSContext *ctx, char **pbuf, size_t *psize
|
||||
else
|
||||
new_size = size + (size >> 1);
|
||||
if (buf == static_buf) {
|
||||
new_buf = js_malloc (ctx, new_size);
|
||||
new_buf = pjs_malloc (new_size);
|
||||
if (!new_buf) return -1;
|
||||
memcpy (new_buf, buf, size);
|
||||
} else {
|
||||
new_buf = js_realloc (ctx, buf, new_size);
|
||||
new_buf = pjs_realloc (buf, new_size);
|
||||
if (!new_buf) return -1;
|
||||
}
|
||||
*pbuf = new_buf;
|
||||
@@ -9815,7 +9987,7 @@ static JSValue parse_ident (JSParseState *s, const uint8_t **pp, BOOL *pident_ha
|
||||
/* Create interned JSValue key */
|
||||
str = js_key_new_len (s->ctx, buf, ident_pos);
|
||||
done:
|
||||
if (unlikely (buf != ident_buf)) js_free (s->ctx, buf);
|
||||
if (unlikely (buf != ident_buf)) pjs_free (buf);
|
||||
*pp = p;
|
||||
return str;
|
||||
}
|
||||
@@ -10274,7 +10446,7 @@ static JSValue json_parse_ident (JSParseState *s, const uint8_t **pp, int c) {
|
||||
/* Create interned JSValue key */
|
||||
str = js_key_new_len (s->ctx, buf, ident_pos);
|
||||
done:
|
||||
if (unlikely (buf != ident_buf)) js_free (s->ctx, buf);
|
||||
if (unlikely (buf != ident_buf)) pjs_free (buf);
|
||||
*pp = p;
|
||||
return str;
|
||||
}
|
||||
@@ -10283,9 +10455,9 @@ static int json_parse_string (JSParseState *s, const uint8_t **pp, int sep) {
|
||||
const uint8_t *p, *p_next;
|
||||
int i;
|
||||
uint32_t c;
|
||||
JSText *b;
|
||||
PPretext *b;
|
||||
|
||||
b = pretext_init (s->ctx, 32);
|
||||
b = ppretext_init (32);
|
||||
if (!b) goto fail;
|
||||
|
||||
p = *pp;
|
||||
@@ -10354,18 +10526,19 @@ static int json_parse_string (JSParseState *s, const uint8_t **pp, int sep) {
|
||||
}
|
||||
p = p_next;
|
||||
}
|
||||
b = pretext_putc (s->ctx, b, c);
|
||||
b = ppretext_putc (b, c);
|
||||
if (!b) goto fail;
|
||||
}
|
||||
s->token.val = TOK_STRING;
|
||||
s->token.u.str.sep = sep;
|
||||
s->token.u.str.str = pretext_end (s->ctx, b);
|
||||
s->token.u.str.str = ppretext_end (s->ctx, b);
|
||||
*pp = p;
|
||||
return 0;
|
||||
|
||||
end_of_input:
|
||||
js_parse_error (s, "Unexpected end of JSON input");
|
||||
fail:
|
||||
ppretext_free (b);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -11252,91 +11425,76 @@ static __exception int js_parse_unary (JSParseState *s, int parse_flags);
|
||||
static void push_break_entry (JSFunctionDef *fd, BlockEnv *be, JSValue label_name, int label_break, int label_cont, int drop_count);
|
||||
static void pop_break_entry (JSFunctionDef *fd);
|
||||
|
||||
static __exception int js_parse_template (JSParseState *s, int call, int *argc) {
|
||||
static __exception int js_parse_template (JSParseState *s) {
|
||||
JSContext *ctx = s->ctx;
|
||||
JSValue raw_array, template_object;
|
||||
PPretext *fmt = ppretext_init (64);
|
||||
JSToken cooked;
|
||||
int depth, ret;
|
||||
int expr_count = 0;
|
||||
|
||||
raw_array = JS_NULL; /* avoid warning */
|
||||
template_object = JS_NULL; /* avoid warning */
|
||||
if (call) {
|
||||
/* Create a template object: an array of cooked strings */
|
||||
/* Create an array of raw strings and store it to the raw property */
|
||||
template_object = JS_NewArray (ctx);
|
||||
if (JS_IsException (template_object)) return -1;
|
||||
// pool_idx = s->cur_func->cpool_count;
|
||||
ret = emit_push_const (s, template_object);
|
||||
if (ret) return -1;
|
||||
raw_array = JS_NewArray (ctx);
|
||||
if (JS_IsException (raw_array)) return -1;
|
||||
if (JS_SetPropertyInternal (ctx, template_object, JS_KEY_raw, raw_array)
|
||||
< 0) {
|
||||
if (!fmt) return -1;
|
||||
|
||||
while (s->token.val == TOK_TEMPLATE) {
|
||||
const uint8_t *p = s->token.ptr + 1;
|
||||
|
||||
/* re-parse the string with escape sequences and throw a
|
||||
syntax error if it contains invalid sequences */
|
||||
s->token.u.str.str = JS_NULL;
|
||||
if (js_parse_string (s, '`', TRUE, p, &cooked, &p)) {
|
||||
ppretext_free (fmt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Append cooked string to format string */
|
||||
fmt = ppretext_append_jsvalue (fmt, cooked.u.str.str);
|
||||
if (!fmt) return -1;
|
||||
|
||||
/* Check for end of template */
|
||||
if (s->token.u.str.sep == '`') break;
|
||||
|
||||
/* Append placeholder {N} */
|
||||
fmt = ppretext_putc (fmt, '{');
|
||||
if (!fmt) return -1;
|
||||
fmt = ppretext_append_int (fmt, expr_count);
|
||||
if (!fmt) return -1;
|
||||
fmt = ppretext_putc (fmt, '}');
|
||||
if (!fmt) return -1;
|
||||
|
||||
/* Parse expression */
|
||||
if (next_token (s)) { ppretext_free (fmt); return -1; }
|
||||
if (js_parse_expr (s)) { ppretext_free (fmt); return -1; }
|
||||
expr_count++;
|
||||
|
||||
if (s->token.val != '}') {
|
||||
ppretext_free (fmt);
|
||||
return js_parse_error (s, "expected '}' after template expression");
|
||||
}
|
||||
|
||||
free_token (s, &s->token);
|
||||
s->got_lf = FALSE;
|
||||
if (js_parse_template_part (s, s->buf_ptr)) {
|
||||
ppretext_free (fmt);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
depth = 0;
|
||||
while (s->token.val == TOK_TEMPLATE) {
|
||||
const uint8_t *p = s->token.ptr + 1;
|
||||
cooked = s->token;
|
||||
if (call) {
|
||||
if (JS_SetPropertyUint32 (ctx, raw_array, depth, s->token.u.str.str)
|
||||
< 0) {
|
||||
return -1;
|
||||
}
|
||||
/* re-parse the string with escape sequences but do not throw a
|
||||
syntax error if it contains invalid sequences
|
||||
*/
|
||||
if (js_parse_string (s, '`', FALSE, p, &cooked, &p)) {
|
||||
cooked.u.str.str = JS_NULL;
|
||||
}
|
||||
if (JS_SetPropertyUint32 (ctx, template_object, depth, cooked.u.str.str)
|
||||
< 0) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
JSText *str;
|
||||
/* re-parse the string with escape sequences and throw a
|
||||
syntax error if it contains invalid sequences
|
||||
*/
|
||||
s->token.u.str.str = JS_NULL;
|
||||
if (js_parse_string (s, '`', TRUE, p, &cooked, &p)) return -1;
|
||||
str = JS_VALUE_GET_STRING (cooked.u.str.str);
|
||||
if (JSText_len (str) != 0 || depth == 0) {
|
||||
ret = emit_push_const (s, cooked.u.str.str);
|
||||
if (ret) return -1;
|
||||
/* For single-literal templates, go directly to done1 */
|
||||
if (depth == 0 && s->token.u.str.sep == '`') goto done1;
|
||||
depth++;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
if (s->token.u.str.sep == '`') goto done;
|
||||
if (next_token (s)) return -1;
|
||||
if (js_parse_expr (s)) return -1;
|
||||
depth++;
|
||||
if (s->token.val != '}') {
|
||||
return js_parse_error (s, "expected '}' after template expression");
|
||||
}
|
||||
/* XXX: should convert to string at this stage? */
|
||||
free_token (s, &s->token);
|
||||
/* Resume TOK_TEMPLATE parsing (s->token.line_num and
|
||||
* s->token.ptr are OK) */
|
||||
s->got_lf = FALSE;
|
||||
if (js_parse_template_part (s, s->buf_ptr)) return -1;
|
||||
}
|
||||
return js_parse_expect (s, TOK_TEMPLATE);
|
||||
/* Finalize format string */
|
||||
JSValue format_str = ppretext_end (ctx, fmt);
|
||||
if (JS_IsException (format_str)) return -1;
|
||||
|
||||
done:
|
||||
if (call) {
|
||||
*argc = depth + 1;
|
||||
} else if (depth > 1) {
|
||||
/* Use template_concat opcode to concatenate all parts */
|
||||
emit_op (s, OP_template_concat);
|
||||
emit_u16 (s, depth);
|
||||
int idx = cpool_add (s, format_str);
|
||||
if (idx < 0) return -1;
|
||||
|
||||
if (expr_count == 0) {
|
||||
/* Simple template - just push the string */
|
||||
emit_op (s, OP_push_const);
|
||||
emit_u32 (s, idx);
|
||||
} else {
|
||||
/* Template with expressions - emit u16 first for stack size computation */
|
||||
emit_op (s, OP_format_template);
|
||||
emit_u16 (s, expr_count);
|
||||
emit_u32 (s, idx);
|
||||
}
|
||||
done1:
|
||||
|
||||
return next_token (s);
|
||||
}
|
||||
|
||||
@@ -12206,11 +12364,6 @@ var_error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
typedef enum FuncCallType {
|
||||
FUNC_CALL_NORMAL,
|
||||
FUNC_CALL_TEMPLATE,
|
||||
} FuncCallType;
|
||||
|
||||
static void optional_chain_test (JSParseState *s,
|
||||
int *poptional_chaining_label,
|
||||
int drop_count) {
|
||||
@@ -12230,12 +12383,10 @@ static void optional_chain_test (JSParseState *s,
|
||||
/* allowed parse_flags: PF_POSTFIX_CALL */
|
||||
static __exception int js_parse_postfix_expr (JSParseState *s,
|
||||
int parse_flags) {
|
||||
FuncCallType call_type;
|
||||
int optional_chaining_label;
|
||||
BOOL accept_lparen = (parse_flags & PF_POSTFIX_CALL) != 0;
|
||||
const uint8_t *op_token_ptr;
|
||||
|
||||
call_type = FUNC_CALL_NORMAL;
|
||||
switch (s->token.val) {
|
||||
case TOK_NUMBER: {
|
||||
JSValue val;
|
||||
@@ -12252,7 +12403,7 @@ static __exception int js_parse_postfix_expr (JSParseState *s,
|
||||
if (next_token (s)) return -1;
|
||||
break;
|
||||
case TOK_TEMPLATE:
|
||||
if (js_parse_template (s, 0, NULL)) return -1;
|
||||
if (js_parse_template (s)) return -1;
|
||||
break;
|
||||
case TOK_STRING:
|
||||
if (emit_push_const (s, s->token.u.str.str)) return -1;
|
||||
@@ -12364,14 +12515,6 @@ static __exception int js_parse_postfix_expr (JSParseState *s,
|
||||
} else {
|
||||
goto parse_property;
|
||||
}
|
||||
} else if (s->token.val == TOK_TEMPLATE && call_type == FUNC_CALL_NORMAL) {
|
||||
if (optional_chaining_label >= 0) {
|
||||
return js_parse_error (
|
||||
s, "template literal cannot appear in an optional chain");
|
||||
}
|
||||
call_type = FUNC_CALL_TEMPLATE;
|
||||
op_token_ptr = s->token.ptr; /* XXX: check if right position */
|
||||
goto parse_func_call2;
|
||||
} else if (s->token.val == '(' && accept_lparen) {
|
||||
int opcode, arg_count, drop_count;
|
||||
|
||||
@@ -12380,81 +12523,71 @@ static __exception int js_parse_postfix_expr (JSParseState *s,
|
||||
op_token_ptr = s->token.ptr;
|
||||
if (next_token (s)) return -1;
|
||||
|
||||
if (call_type == FUNC_CALL_NORMAL) {
|
||||
parse_func_call2:
|
||||
switch (opcode = get_prev_opcode (fd)) {
|
||||
case OP_get_field:
|
||||
/* keep the object on the stack */
|
||||
fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
|
||||
drop_count = 2;
|
||||
break;
|
||||
case OP_get_field_opt_chain: {
|
||||
int opt_chain_label, next_label;
|
||||
opt_chain_label
|
||||
= get_u32 (fd->byte_code.buf + fd->last_opcode_pos + 1 + 4 + 1);
|
||||
/* keep the object on the stack */
|
||||
fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
|
||||
fd->byte_code.size = fd->last_opcode_pos + 1 + 4;
|
||||
next_label = emit_goto (s, OP_goto, -1);
|
||||
emit_label (s, opt_chain_label);
|
||||
/* need an additional undefined value for the
|
||||
case where the optional field does not
|
||||
exists */
|
||||
emit_op (s, OP_null);
|
||||
emit_label (s, next_label);
|
||||
drop_count = 2;
|
||||
opcode = OP_get_field;
|
||||
} break;
|
||||
case OP_get_array_el:
|
||||
/* keep the object on the stack */
|
||||
fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
|
||||
drop_count = 2;
|
||||
break;
|
||||
case OP_get_array_el_opt_chain: {
|
||||
int opt_chain_label, next_label;
|
||||
opt_chain_label
|
||||
= get_u32 (fd->byte_code.buf + fd->last_opcode_pos + 1 + 1);
|
||||
/* keep the object on the stack */
|
||||
fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
|
||||
fd->byte_code.size = fd->last_opcode_pos + 1;
|
||||
next_label = emit_goto (s, OP_goto, -1);
|
||||
emit_label (s, opt_chain_label);
|
||||
/* need an additional undefined value for the
|
||||
case where the optional field does not
|
||||
exists */
|
||||
emit_op (s, OP_null);
|
||||
emit_label (s, next_label);
|
||||
drop_count = 2;
|
||||
opcode = OP_get_array_el;
|
||||
} break;
|
||||
case OP_scope_get_var: {
|
||||
JSValue name;
|
||||
int scope;
|
||||
uint32_t idx = get_u32 (fd->byte_code.buf + fd->last_opcode_pos + 1);
|
||||
name = fd->cpool[idx];
|
||||
scope = get_u16 (fd->byte_code.buf + fd->last_opcode_pos + 5);
|
||||
/* verify if function name resolves to a simple
|
||||
get_loc/get_arg: a function call inside a `with`
|
||||
statement can resolve to a method call of the
|
||||
`with` context object
|
||||
*/
|
||||
drop_count = 1;
|
||||
} break;
|
||||
default:
|
||||
opcode = OP_invalid;
|
||||
drop_count = 1;
|
||||
break;
|
||||
}
|
||||
if (has_optional_chain) {
|
||||
optional_chain_test (s, &optional_chaining_label, drop_count);
|
||||
}
|
||||
} else {
|
||||
switch (opcode = get_prev_opcode (fd)) {
|
||||
case OP_get_field:
|
||||
/* keep the object on the stack */
|
||||
fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
|
||||
drop_count = 2;
|
||||
break;
|
||||
case OP_get_field_opt_chain: {
|
||||
int opt_chain_label, next_label;
|
||||
opt_chain_label
|
||||
= get_u32 (fd->byte_code.buf + fd->last_opcode_pos + 1 + 4 + 1);
|
||||
/* keep the object on the stack */
|
||||
fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
|
||||
fd->byte_code.size = fd->last_opcode_pos + 1 + 4;
|
||||
next_label = emit_goto (s, OP_goto, -1);
|
||||
emit_label (s, opt_chain_label);
|
||||
/* need an additional undefined value for the
|
||||
case where the optional field does not
|
||||
exists */
|
||||
emit_op (s, OP_null);
|
||||
emit_label (s, next_label);
|
||||
drop_count = 2;
|
||||
opcode = OP_get_field;
|
||||
} break;
|
||||
case OP_get_array_el:
|
||||
/* keep the object on the stack */
|
||||
fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
|
||||
drop_count = 2;
|
||||
break;
|
||||
case OP_get_array_el_opt_chain: {
|
||||
int opt_chain_label, next_label;
|
||||
opt_chain_label
|
||||
= get_u32 (fd->byte_code.buf + fd->last_opcode_pos + 1 + 1);
|
||||
/* keep the object on the stack */
|
||||
fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
|
||||
fd->byte_code.size = fd->last_opcode_pos + 1;
|
||||
next_label = emit_goto (s, OP_goto, -1);
|
||||
emit_label (s, opt_chain_label);
|
||||
/* need an additional undefined value for the
|
||||
case where the optional field does not
|
||||
exists */
|
||||
emit_op (s, OP_null);
|
||||
emit_label (s, next_label);
|
||||
drop_count = 2;
|
||||
opcode = OP_get_array_el;
|
||||
} break;
|
||||
case OP_scope_get_var: {
|
||||
JSValue name;
|
||||
int scope;
|
||||
uint32_t idx = get_u32 (fd->byte_code.buf + fd->last_opcode_pos + 1);
|
||||
name = fd->cpool[idx];
|
||||
scope = get_u16 (fd->byte_code.buf + fd->last_opcode_pos + 5);
|
||||
/* verify if function name resolves to a simple
|
||||
get_loc/get_arg: a function call inside a `with`
|
||||
statement can resolve to a method call of the
|
||||
`with` context object
|
||||
*/
|
||||
drop_count = 1;
|
||||
} break;
|
||||
default:
|
||||
opcode = OP_invalid;
|
||||
drop_count = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (call_type == FUNC_CALL_TEMPLATE) {
|
||||
if (js_parse_template (s, 1, &arg_count)) return -1;
|
||||
goto emit_func_call;
|
||||
if (has_optional_chain) {
|
||||
optional_chain_test (s, &optional_chaining_label, drop_count);
|
||||
}
|
||||
|
||||
/* parse arguments */
|
||||
@@ -12484,7 +12617,6 @@ static __exception int js_parse_postfix_expr (JSParseState *s,
|
||||
break;
|
||||
}
|
||||
}
|
||||
call_type = FUNC_CALL_NORMAL;
|
||||
} else if (s->token.val == '.') {
|
||||
op_token_ptr = s->token.ptr;
|
||||
if (next_token (s)) return -1;
|
||||
|
||||
Reference in New Issue
Block a user