diff --git a/quickjs.c b/quickjs.c index 53ef173d6..bbac00c33 100644 --- a/quickjs.c +++ b/quickjs.c @@ -22383,6 +22383,7 @@ typedef struct JSToken { int line_num; /* line number of token start */ int col_num; /* column number of token start */ const uint8_t *ptr; + const uint8_t *line_start; /* first character of the line of token start */ union { struct { JSValue str; @@ -22416,6 +22417,7 @@ typedef struct JSParseState { const uint8_t *buf_start; const uint8_t *buf_ptr; const uint8_t *buf_end; + const uint8_t *line_start; /* first character of the current line */ const uint8_t *eol; // most recently seen end-of-line character const uint8_t *mark; // first token character, invariant: eol < mark @@ -22576,13 +22578,8 @@ int JS_PRINTF_FORMAT_ATTR(2, 3) js_parse_error(JSParseState *s, JS_PRINTF_FORMAT /* s->col_num is not advanced during token scanning, so derive the column as the 1-based offset of the token from the start of its line. */ int err_col_num = s->token.col_num; - if (s->token.ptr && s->token.ptr >= s->buf_start) { - const uint8_t *line_start = s->token.ptr; - while (line_start > s->buf_start && - line_start[-1] != '\n' && line_start[-1] != '\r') - line_start--; - err_col_num = (int)(s->token.ptr - line_start) + 1; - } + if (s->token.ptr && s->token.ptr >= s->token.line_start) + err_col_num = (int)(s->token.ptr - s->token.line_start) + 1; build_backtrace(ctx, ctx->rt->current_exception, JS_UNDEFINED, s->filename, s->token.line_num, err_col_num, backtrace_flags); return -1; @@ -22682,6 +22679,7 @@ static __exception int js_parse_template_part(JSParseState *s, } if (c == '\n') { s->line_num++; + s->line_start = p; s->eol = &p[-1]; s->mark = p; } else if (c >= 0x80) { @@ -22775,6 +22773,7 @@ static __exception int js_parse_string(JSParseState *s, int sep, p++; if (sep != '`') { s->line_num++; + s->line_start = p; s->eol = &p[-1]; s->mark = p; } @@ -23102,6 +23101,7 @@ static __exception int next_token(JSParseState *s) s->token.line_num = s->line_num; s->token.col_num = s->col_num; s->token.ptr = p; + s->token.line_start = s->line_start; c = *p; switch(c) { case 0: @@ -23129,6 +23129,7 @@ static __exception int next_token(JSParseState *s) case '\n': p++; line_terminator: + s->line_start = p; s->eol = &p[-1]; s->mark = p; s->got_lf = true; @@ -23157,10 +23158,12 @@ static __exception int next_token(JSParseState *s) s->line_num++; s->got_lf = true; /* considered as LF for ASI */ s->eol = p++; + s->line_start = p; s->mark = p; } else if (*p == '\r') { s->got_lf = true; /* considered as LF for ASI */ p++; + s->line_start = p; } else if (*p >= 0x80) { c = utf8_decode(p, &p); /* ignore invalid UTF-8 in comments */ @@ -23748,6 +23751,7 @@ static __exception int json_next_token(JSParseState *s) s->token.line_num = s->line_num; s->token.col_num = s->col_num; s->token.ptr = p; + s->token.line_start = s->line_start; c = *p; switch(c) { case 0: @@ -23773,6 +23777,7 @@ static __exception int json_next_token(JSParseState *s) case '\n': s->line_num++; s->eol = p++; + s->line_start = p; s->mark = p; goto redo; case '\f': @@ -25005,6 +25010,7 @@ typedef struct JSParsePos { int col_num; bool got_lf; const uint8_t *ptr; + const uint8_t *line_start; const uint8_t *eol; const uint8_t *mark; } JSParsePos; @@ -25016,6 +25022,7 @@ static int js_parse_get_pos(JSParseState *s, JSParsePos *sp) sp->line_num = s->token.line_num; sp->col_num = s->token.col_num; sp->ptr = s->token.ptr; + sp->line_start = s->line_start; sp->eol = s->eol; sp->mark = s->mark; sp->got_lf = s->got_lf; @@ -25029,6 +25036,7 @@ static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp s->line_num = sp->line_num; s->col_num = sp->col_num; s->buf_ptr = sp->ptr; + s->line_start = sp->line_start; s->eol = sp->eol; s->mark = sp->mark; s->got_lf = sp->got_lf; @@ -27189,13 +27197,8 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) { JSAtom name; int identifier_line_num = s->token.line_num; - const uint8_t *identifier_line_start = s->token.ptr; - while (identifier_line_start > s->buf_start && - identifier_line_start[-1] != '\n' && - identifier_line_start[-1] != '\r') - identifier_line_start--; int identifier_col_num = - (int)(s->token.ptr - identifier_line_start) + 1; + (int)(s->token.ptr - s->line_start) + 1; if (s->token.u.ident.is_reserved) { return js_parse_error_reserved_identifier(s); } @@ -38094,6 +38097,7 @@ static void js_parse_init(JSContext *ctx, JSParseState *s, s->col_num = 1; s->buf_start = s->buf_ptr = (const uint8_t *)input; s->buf_end = s->buf_ptr + input_len; + s->line_start = s->buf_ptr; s->mark = s->buf_ptr + min_int(1, input_len); s->eol = s->buf_ptr; s->token.val = ' '; diff --git a/tests/parse-error-column.js b/tests/parse-error-column.js index 07445aaab..483cd225a 100644 --- a/tests/parse-error-column.js +++ b/tests/parse-error-column.js @@ -24,3 +24,23 @@ assert(errorLocation("a b").col, 3); assert(loc.line, 2); assert(loc.col, 9); } + +/* the offending token starts after a multi-line template literal */ +{ + const loc = errorLocation("`line1\nline2\nline3`; let y = @"); + assert(loc.line, 3); + assert(loc.col, 17); +} + +/* the error is raised while scanning a multi-line token: the location is + the token's start, not where scanning stopped */ +{ + const loc = errorLocation("let x = `abc\ndef"); + assert(loc.line, 1); + assert(loc.col, 9); +} +{ + const loc = errorLocation("let s = 'ab\\\ncd"); + assert(loc.line, 1); + assert(loc.col, 9); +}