diff --git a/cutils.c b/cutils.c
index a02fb768..3c0e47a6 100644
@@ -303,6 +303,16 @@ int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp)
return c;
}
+int utf8_str_len(const uint8_t *p_start, const uint8_t *p_end) {
+ int count = 0;
+ while (p_start < p_end) {
+ if (!unicode_from_utf8(p_start, UTF8_CHAR_LEN_MAX, &p_start))
+ break;
+ count += 1;
+ }
+ return count;
+}
+
#if 0
#if defined(EMSCRIPTEN) || defined(__ANDROID__)
diff --git a/cutils.h b/cutils.h
index 31f7cd84..4e0519c9 100644
@@ -277,6 +277,7 @@ static inline void dbuf_set_error(DynBuf *s)
int unicode_to_utf8(uint8_t *buf, unsigned int c);
int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp);
+int utf8_str_len(const uint8_t *p_start, const uint8_t *p_end);
static inline int from_hex(int c)
{
diff --git a/quickjs-atom.h b/quickjs-atom.h
index 4c227945..d7fe9ced 100644
@@ -81,6 +81,7 @@ DEF(empty_string, "")
DEF(length, "length")
DEF(fileName, "fileName")
DEF(lineNumber, "lineNumber")
+DEF(columnNumber, "columnNumber")
DEF(message, "message")
DEF(errors, "errors")
DEF(stack, "stack")
diff --git a/quickjs-opcode.h b/quickjs-opcode.h
index c731a14a..458a8828 100644
@@ -284,6 +284,7 @@ def(scope_put_private_field, 7, 1, 1, atom_u16) /* obj value ->, emitted in phas
def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */
def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */
+def( column_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */
#if SHORT_OPCODES
DEF( push_minus1, 1, 0, 1, none_int)
diff --git a/quickjs.c b/quickjs.c
index 79160139..3fb3c09e 100644
@@ -574,6 +574,10 @@ typedef struct JSVarDef {
#define PC2LINE_RANGE 5
#define PC2LINE_OP_FIRST 1
#define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE)
+#define PC2COLUMN_BASE (-1)
+#define PC2COLUMN_RANGE 5
+#define PC2COLUMN_OP_FIRST 1
+#define PC2COLUMN_DIFF_PC_MAX ((255 - PC2COLUMN_OP_FIRST) / PC2COLUMN_RANGE)
typedef enum JSFunctionKindEnum {
JS_FUNC_NORMAL = 0,
@@ -616,9 +620,12 @@ typedef struct JSFunctionBytecode {
/* debug info, move to separate structure to save memory? */
JSAtom filename;
int line_num;
+ int column_num;
int source_len;
int pc2line_len;
+ int pc2column_len;
uint8_t *pc2line_buf;
+ uint8_t *pc2column_buf;
char *source;
} debug;
} JSFunctionBytecode;
@@ -5829,6 +5836,8 @@ typedef struct JSMemoryUsage_helper {
int64_t js_func_code_size;
int64_t js_func_pc2line_count;
int64_t js_func_pc2line_size;
+ int64_t js_func_pc2column_count;
+ int64_t js_func_pc2column_size;
} JSMemoryUsage_helper;
static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp);
@@ -5876,6 +5885,11 @@ static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *h
hp->js_func_pc2line_count += 1;
hp->js_func_pc2line_size += b->debug.pc2line_len;
}
+ if (b->debug.pc2column_len) {
+ memory_used_count++;
+ hp->js_func_pc2column_count += 1;
+ hp->js_func_pc2column_size += b->debug.pc2column_len;
+ }
}
hp->js_func_size += js_func_size;
hp->js_func_count += 1;
@@ -6179,13 +6193,17 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
s->js_func_code_size = mem.js_func_code_size;
s->js_func_pc2line_count = mem.js_func_pc2line_count;
s->js_func_pc2line_size = mem.js_func_pc2line_size;
+ s->js_func_pc2column_count = mem.js_func_pc2column_count;
+ s->js_func_pc2column_size = mem.js_func_pc2column_size;
s->memory_used_count += round(mem.memory_used_count) +
s->atom_count + s->str_count +
s->obj_count + s->shape_count +
- s->js_func_count + s->js_func_pc2line_count;
+ s->js_func_count + s->js_func_pc2line_count +
+ s->js_func_pc2column_count;
s->memory_used_size += s->atom_size + s->str_size +
s->obj_size + s->prop_size + s->shape_size +
- s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size;
+ s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size +
+ s->js_func_pc2column_size;
}
void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)
@@ -6297,6 +6315,12 @@ void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)
s->js_func_pc2line_size,
(double)s->js_func_pc2line_size / s->js_func_pc2line_count);
}
+ if(s->js_func_pc2column_count) {
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",
+ " pc2column", s->js_func_pc2column_count,
+ s->js_func_pc2column_size,
+ (double)s->js_func_pc2column_size / s->js_func_pc2column_count);
+ }
}
if (s->c_func_count) {
fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count);
@@ -6441,6 +6465,55 @@ static int find_line_num(JSContext *ctx, JSFunctionBytecode *b,
return line_num;
}
+int find_column_num(JSContext* ctx, JSFunctionBytecode* b,
+ uint32_t pc_value)
+{
+ const uint8_t* p_end, *p;
+ int new_column_num, column_num, pc, v, ret;
+ unsigned int op;
+
+ if (!b->has_debug || !b->debug.pc2column_buf) {
+ /* function was stripped */
+ return -1;
+ }
+
+ pc = 0;
+ p = b->debug.pc2column_buf;
+ p_end = p + b->debug.pc2column_len;
+ column_num = b->debug.column_num;
+ while (p < p_end) {
+ op = *p++;
+ if (op == 0) {
+ uint32_t val;
+ ret = get_leb128(&val, p, p_end);
+ if (ret < 0)
+ goto fail;
+ pc += val;
+ p += ret;
+ ret = get_sleb128(&v, p, p_end);
+ if (ret < 0) {
+ fail:
+ /* should never happen */
+ return b->debug.column_num;
+ }
+ p += ret;
+ new_column_num = column_num + v;
+ } else {
+ op -= PC2COLUMN_OP_FIRST;
+ pc += (op / PC2COLUMN_RANGE);
+ new_column_num = column_num + (op % PC2COLUMN_RANGE) + PC2COLUMN_BASE;
+ }
+
+ if (pc_value < pc) {
+ return column_num;
+ }
+
+ column_num = new_column_num;
+ }
+
+ return column_num;
+}
+
/* in order to avoid executing arbitrary code during the stack trace
generation, we only look at simple 'name' properties containing a
string. */
@@ -6471,7 +6544,7 @@ static const char *get_func_name(JSContext *ctx, JSValueConst func)
and line number information (used for parse error). */
static void build_backtrace(JSContext *ctx, JSValueConst error_obj,
const char *filename, int line_num,
- int backtrace_flags)
+ int column_num, int backtrace_flags)
{
JSStackFrame *sf;
JSValue str;
@@ -6480,18 +6553,24 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_obj,
const char *str1;
JSObject *p;
BOOL backtrace_barrier;
+ int latest_line_num = -1;
+ int latest_column_num = -1;
js_dbuf_init(ctx, &dbuf);
if (filename) {
dbuf_printf(&dbuf, " at %s", filename);
- if (line_num != -1)
+ if (line_num != -1) {
+ latest_line_num = line_num;
dbuf_printf(&dbuf, ":%d", line_num);
+ }
+ if (column_num != -1) {
+ latest_column_num = column_num;
+ dbuf_printf(&dbuf, ":%d", column_num);
+ }
dbuf_putc(&dbuf, '\n');
str = JS_NewString(ctx, filename);
JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str,
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
- JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num),
- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
if (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL)
goto done;
}
@@ -6513,19 +6592,33 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_obj,
if (js_class_has_bytecode(p->class_id)) {
JSFunctionBytecode *b;
const char *atom_str;
- int line_num1;
b = p->u.func.function_bytecode;
backtrace_barrier = b->backtrace_barrier;
if (b->has_debug) {
- line_num1 = find_line_num(ctx, b,
- sf->cur_pc - b->byte_code_buf - 1);
+ line_num = find_line_num(ctx, b, sf->cur_pc - b->byte_code_buf - 1);
+ column_num = find_column_num(ctx, b, sf->cur_pc - b->byte_code_buf - 1);
+ line_num = line_num == -1 ? b->debug.line_num : line_num;
+ column_num = column_num == -1 ? b->debug.column_num : column_num;
+ if (column_num != -1) {
+ column_num += 1;
+ }
+ if (latest_line_num == -1) {
+ latest_line_num = line_num;
+ }
+ if (latest_column_num == -1) {
+ latest_column_num = column_num;
+ }
atom_str = JS_AtomToCString(ctx, b->debug.filename);
dbuf_printf(&dbuf, " (%s",
atom_str ? atom_str : "<null>");
JS_FreeCString(ctx, atom_str);
- if (line_num1 != -1)
- dbuf_printf(&dbuf, ":%d", line_num1);
+ if (line_num != -1) {
+ dbuf_printf(&dbuf, ":%d", line_num);
+ if (column_num != -1) {
+ dbuf_printf(&dbuf, ":%d", column_num);
+ }
+ }
dbuf_putc(&dbuf, ')');
}
} else {
@@ -6545,6 +6638,15 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_obj,
dbuf_free(&dbuf);
JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str,
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ if (line_num != -1) {
+ JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, latest_line_num),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+
+ if (column_num != -1) {
+ JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_columnNumber, JS_NewInt32(ctx, latest_column_num),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ }
+ }
}
/* Note: it is important that no exception is returned by this function */
@@ -6584,7 +6686,7 @@ static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num,
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
}
if (add_backtrace) {
- build_backtrace(ctx, obj, NULL, 0, 0);
+ build_backtrace(ctx, obj, NULL, 0, 0, 0);
}
ret = JS_Throw(ctx, obj);
return ret;
@@ -14921,6 +15023,14 @@ static JSValue js_function_proto_lineNumber(JSContext *ctx,
return JS_UNDEFINED;
}
+static JSValue js_function_proto_columnNumber(JSContext *ctx, JSValueConst this_val) {
+ JSFunctionBytecode* b = JS_GetFunctionBytecode(this_val);
+ if (b && b->has_debug) {
+ return JS_NewInt32(ctx, b->debug.column_num);
+ }
+ return JS_UNDEFINED;
+}
+
static int js_arguments_define_own_property(JSContext *ctx,
JSValueConst this_obj,
JSAtom prop, JSValueConst val,
@@ -18664,7 +18774,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
before if the exception happens in a bytecode
operation */
sf->cur_pc = pc;
- build_backtrace(ctx, rt->current_exception, NULL, 0, 0);
+ build_backtrace(ctx, rt->current_exception, NULL, 0, 0, 0);
}
if (!JS_IsUncatchableError(ctx, rt->current_exception)) {
while (sp > stack_buf) {
@@ -19928,6 +20038,11 @@ typedef struct LineNumberSlot {
int line_num;
} LineNumberSlot;
+typedef struct ColumnNumberSlot {
+ uint32_t pc;
+ int column_num;
+} ColumnNumberSlot;
+
typedef enum JSParseFunctionEnum {
JS_PARSE_FUNC_STATEMENT,
JS_PARSE_FUNC_VAR,
@@ -20046,10 +20161,18 @@ typedef struct JSFunctionDef {
int line_number_last;
int line_number_last_pc;
+ ColumnNumberSlot* column_number_slots;
+ int column_number_size;
+ int column_number_count;
+ int column_number_last_pc;
+ int column_number_last;
+
/* pc2line table */
JSAtom filename;
int line_num;
+ int column_num;
DynBuf pc2line;
+ DynBuf pc2column;
char *source; /* raw source, utf-8 encoded */
int source_len;
@@ -20059,7 +20182,8 @@ typedef struct JSFunctionDef {
typedef struct JSToken {
int val;
- int line_num; /* line number of token start */
+ int line_num; /* line number of token start */
+ int column_num; /* colum number of token start */
const uint8_t *ptr;
union {
struct {
@@ -20088,6 +20212,9 @@ typedef struct JSParseState {
JSContext *ctx;
int last_line_num; /* line number of last token */
int line_num; /* line number of current offset */
+ const uint8_t *column_ptr;
+ const uint8_t *column_last_ptr;
+ int column_num_count;
const char *filename;
JSToken token;
BOOL got_lf; /* true if got line feed before the current token */
@@ -20230,11 +20357,19 @@ static void __attribute((unused)) dump_token(JSParseState *s,
}
}
+static int calc_column_position(JSParseState *s) {
+ if(s->column_last_ptr > s->column_ptr) {
+ s->column_num_count += utf8_str_len(s->column_ptr, s->column_last_ptr);
+ s->column_ptr = s->column_last_ptr;
+ }
+ return s->column_num_count;
+}
+
int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const char *fmt, ...)
{
JSContext *ctx = s->ctx;
va_list ap;
- int backtrace_flags;
+ int backtrace_flags, column_num;
va_start(ap, fmt);
JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE);
@@ -20242,7 +20377,9 @@ int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const
backtrace_flags = 0;
if (s->cur_func && s->cur_func->backtrace_barrier)
backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;
+ column_num = calc_column_position(s);
build_backtrace(ctx, ctx->rt->current_exception, s->filename, s->line_num,
+ column_num < 0 ? -1 : column_num,
backtrace_flags);
return -1;
}
@@ -20280,6 +20417,7 @@ static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p)
{
uint32_t c;
StringBuffer b_s, *b = &b_s;
+ s->token.column_num = calc_column_position(s);
/* p points to the first byte of the template part */
if (string_buffer_init(s->ctx, b, 32))
@@ -20312,6 +20450,8 @@ static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p)
}
if (c == '\n') {
s->line_num++;
+ s->column_ptr = s->column_last_ptr = p;
+ s->column_num_count = 0;
} else if (c >= 0x80) {
const uint8_t *p_next;
c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
@@ -20344,6 +20484,7 @@ static __exception int js_parse_string(JSParseState *s, int sep,
int ret;
uint32_t c;
StringBuffer b_s, *b = &b_s;
+ s->token.column_num = calc_column_position(s);
/* string */
if (string_buffer_init(s->ctx, b, 32))
@@ -20399,8 +20540,11 @@ static __exception int js_parse_string(JSParseState *s, int sep,
case '\n':
/* ignore escaped newline sequence */
p++;
- if (sep != '`')
- s->line_num++;
+ if (sep != '`') {
+ s->line_num++;
+ s->column_ptr = s->column_last_ptr = p;
+ s->column_num_count = 0;
+ }
continue;
default:
if (c >= '0' && c <= '9') {
@@ -20673,7 +20817,9 @@ static __exception int next_token(JSParseState *s)
s->got_lf = FALSE;
s->last_line_num = s->token.line_num;
redo:
+ s->column_last_ptr = p;
s->token.line_num = s->line_num;
+ s->token.column_num = 0;
s->token.ptr = p;
c = *p;
switch(c) {
@@ -20704,6 +20850,8 @@ static __exception int next_token(JSParseState *s)
line_terminator:
s->got_lf = TRUE;
s->line_num++;
+ s->column_ptr = p;
+ s->column_num_count = 0;
goto redo;
case '\f':
case '\v':
@@ -20727,6 +20875,8 @@ static __exception int next_token(JSParseState *s)
if (*p == '\n') {
s->line_num++;
s->got_lf = TRUE; /* considered as LF for ASI */
+ s->column_ptr = ++p;
+ s->column_num_count = 0;
p++;
} else if (*p == '\r') {
s->got_lf = TRUE; /* considered as LF for ASI */
@@ -21161,6 +21311,9 @@ static __exception int next_token(JSParseState *s)
break;
}
s->buf_ptr = p;
+ if (!s->token.column_num) {
+ s->token.column_num = calc_column_position(s);
+ }
// dump_token(s, &s->token);
return 0;
@@ -21219,7 +21372,9 @@ static __exception int json_next_token(JSParseState *s)
p = s->last_ptr = s->buf_ptr;
s->last_line_num = s->token.line_num;
redo:
+ s->column_last_ptr = p;
s->token.line_num = s->line_num;
+ s->token.column_num = 0;
s->token.ptr = p;
c = *p;
switch(c) {
@@ -21248,6 +21403,8 @@ static __exception int json_next_token(JSParseState *s)
case '\n':
p++;
s->line_num++;
+ s->column_ptr = p;
+ s->column_num_count = 0;
goto redo;
case '\f':
case '\v':
@@ -21278,7 +21435,9 @@ static __exception int json_next_token(JSParseState *s)
break;
}
if (*p == '\n') {
- s->line_num++;
+ s->line_num++;
+ s->column_ptr = ++p;
+ s->column_num_count = 0;
p++;
} else if (*p == '\r') {
p++;
@@ -21389,6 +21548,9 @@ static __exception int json_next_token(JSParseState *s)
break;
}
s->buf_ptr = p;
+ if (!s->token.column_num) {
+ s->token.column_num = calc_column_position(s);
+ }
// dump_token(s, &s->token);
return 0;
@@ -21569,6 +21731,11 @@ static void emit_atom(JSParseState *s, JSAtom name)
emit_u32(s, JS_DupAtom(s->ctx, name));
}
+static void emit_column(JSParseState *s, int column_num) {
+ emit_u8(s, OP_column_num);
+ emit_u32(s, column_num);
+}
+
static int update_label(JSFunctionDef *s, int label, int delta)
{
LabelSlot *ls;
@@ -22123,7 +22290,7 @@ static __exception int js_parse_function_decl(JSParseState *s,
JSParseFunctionEnum func_type,
JSFunctionKindEnum func_kind,
JSAtom func_name, const uint8_t *ptr,
- int start_line);
+ int start_line, int start_column);
static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s);
static __exception int js_parse_function_decl2(JSParseState *s,
JSParseFunctionEnum func_type,
@@ -22131,6 +22298,7 @@ static __exception int js_parse_function_decl2(JSParseState *s,
JSAtom func_name,
const uint8_t *ptr,
int function_line_num,
+ int function_column_num,
JSParseExportEnum export_flag,
JSFunctionDef **pfd);
static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags);
@@ -22424,12 +22592,18 @@ typedef struct JSParsePos {
int line_num;
BOOL got_lf;
const uint8_t *ptr;
+ const uint8_t *column_ptr;
+ const uint8_t *column_last_ptr;
+ int column_num_count;
} JSParsePos;
static int js_parse_get_pos(JSParseState *s, JSParsePos *sp)
{
sp->last_line_num = s->last_line_num;
sp->line_num = s->token.line_num;
+ sp->column_ptr = s->column_ptr;
+ sp->column_last_ptr = s->column_last_ptr;
+ sp->column_num_count = s->column_num_count;
sp->ptr = s->token.ptr;
sp->got_lf = s->got_lf;
return 0;
@@ -22439,6 +22613,9 @@ static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp
{
s->token.line_num = sp->last_line_num;
s->line_num = sp->line_num;
+ s->column_ptr = sp->column_ptr;
+ s->column_last_ptr = sp->column_last_ptr;
+ s->column_num_count = sp->column_num_count;
s->buf_ptr = sp->ptr;
s->got_lf = sp->got_lf;
return next_token(s);
@@ -22644,7 +22821,7 @@ static __exception int js_parse_object_literal(JSParseState *s)
{
JSAtom name = JS_ATOM_NULL;
const uint8_t *start_ptr;
- int start_line, prop_type;
+ int start_line, start_column, prop_type;
BOOL has_proto;
if (next_token(s))
@@ -22655,7 +22832,8 @@ static __exception int js_parse_object_literal(JSParseState *s)
while (s->token.val != '}') {
/* specific case for getter/setter */
start_ptr = s->token.ptr;
- start_line = s->token.line_num;
+ start_line = s->token.line_num;
+ start_column = s->token.column_num;
if (s->token.val == TOK_ELLIPSIS) {
if (next_token(s))
@@ -22701,7 +22879,7 @@ static __exception int js_parse_object_literal(JSParseState *s)
func_kind = JS_FUNC_ASYNC_GENERATOR;
}
if (js_parse_function_decl(s, func_type, func_kind, JS_ATOM_NULL,
- start_ptr, start_line))
+ start_ptr, start_line, start_column))
goto fail;
if (name == JS_ATOM_NULL) {
emit_op(s, OP_define_method_computed);
@@ -22779,7 +22957,7 @@ static __exception int js_parse_class_default_ctor(JSParseState *s,
{
JSParsePos pos;
const char *str;
- int ret, line_num;
+ int ret, line_num, column_num;
JSParseFunctionEnum func_type;
const uint8_t *saved_buf_end;
@@ -22793,14 +22971,17 @@ static __exception int js_parse_class_default_ctor(JSParseState *s,
func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;
}
line_num = s->token.line_num;
+ column_num = s->token.column_num;
saved_buf_end = s->buf_end;
s->buf_ptr = (uint8_t *)str;
- s->buf_end = (uint8_t *)(str + strlen(str));
+ s->buf_end = (uint8_t *)(str + strlen(str));
+ s->column_last_ptr = s->buf_ptr;
ret = next_token(s);
if (!ret) {
ret = js_parse_function_decl2(s, func_type, JS_FUNC_NORMAL,
JS_ATOM_NULL, (uint8_t *)str,
- line_num, JS_PARSE_EXPORT_NONE, pfd);
+ line_num, column_num,
+ JS_PARSE_EXPORT_NONE, pfd);
}
s->buf_end = saved_buf_end;
ret |= js_parse_seek_token(s, &pos);
@@ -23084,7 +23265,8 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set,
JS_FUNC_NORMAL, JS_ATOM_NULL,
start_ptr, s->token.line_num,
- JS_PARSE_EXPORT_NONE, &method_fd))
+ s->token.column_num, JS_PARSE_EXPORT_NONE,
+ &method_fd))
goto fail;
if (is_private) {
method_fd->need_home_object = TRUE; /* needed for brand check */
@@ -23228,7 +23410,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
if (add_brand(s, &class_fields[is_static]) < 0)
goto fail;
}
- if (js_parse_function_decl2(s, func_type, func_kind, JS_ATOM_NULL, start_ptr, s->token.line_num, JS_PARSE_EXPORT_NONE, &method_fd))
+ if (js_parse_function_decl2(s, func_type, func_kind, JS_ATOM_NULL, start_ptr, s->token.line_num, s->token.column_num, JS_PARSE_EXPORT_NONE, &method_fd))
goto fail;
if (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR ||
func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {
@@ -23893,6 +24075,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
JSAtom prop_name, var_name;
int opcode, scope, tok1, skip_bits;
BOOL has_initializer;
+ emit_column(s, s->token.column_num);
if (has_ellipsis < 0) {
/* pre-parse destructuration target for spread detection */
@@ -24330,10 +24513,12 @@ static void optional_chain_test(JSParseState *s, int *poptional_chaining_label,
static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
{
FuncCallType call_type;
- int optional_chaining_label;
+ int optional_chaining_label, column_num;
BOOL accept_lparen = (parse_flags & PF_POSTFIX_CALL) != 0;
call_type = FUNC_CALL_NORMAL;
+ column_num = s->token.column_num;
+ emit_column(s, column_num);
switch(s->token.val) {
case TOK_NUMBER:
{
@@ -24413,7 +24598,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;
build_backtrace(s->ctx, s->ctx->rt->current_exception,
s->filename, s->token.line_num,
- backtrace_flags);
+ s->token.column_num, backtrace_flags);
return -1;
}
ret = emit_push_const(s, str, 0);
@@ -24433,7 +24618,8 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) {
if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
JS_FUNC_NORMAL, JS_ATOM_NULL,
- s->token.ptr, s->token.line_num))
+ s->token.ptr, s->token.line_num,
+ s->token.column_num))
return -1;
} else {
if (js_parse_expr_paren(s))
@@ -24443,7 +24629,8 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
case TOK_FUNCTION:
if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,
JS_FUNC_NORMAL, JS_ATOM_NULL,
- s->token.ptr, s->token.line_num))
+ s->token.ptr, s->token.line_num,
+ s->token.column_num))
return -1;
break;
case TOK_CLASS:
@@ -24482,21 +24669,25 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
peek_token(s, TRUE) == TOK_ARROW) {
if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
JS_FUNC_NORMAL, JS_ATOM_NULL,
- s->token.ptr, s->token.line_num))
+ s->token.ptr, s->token.line_num,
+ s->token.column_num))
return -1;
} else if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
peek_token(s, TRUE) != '\n') {
const uint8_t *source_ptr;
int source_line_num;
+ int source_column_num;
source_ptr = s->token.ptr;
source_line_num = s->token.line_num;
+ source_column_num = s->token.column_num;
if (next_token(s))
return -1;
if (s->token.val == TOK_FUNCTION) {
if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,
JS_FUNC_ASYNC, JS_ATOM_NULL,
- source_ptr, source_line_num))
+ source_ptr, source_line_num,
+ source_column_num))
return -1;
} else if ((parse_flags & PF_ARROW_FUNC) &&
((s->token.val == '(' &&
@@ -24505,7 +24696,8 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
peek_token(s, TRUE) == TOK_ARROW))) {
if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
JS_FUNC_ASYNC, JS_ATOM_NULL,
- source_ptr, source_line_num))
+ source_ptr, source_line_num,
+ source_column_num))
return -1;
} else {
name = JS_DupAtom(s->ctx, JS_ATOM_async);
@@ -24857,6 +25049,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
break;
}
} else {
+ emit_column(s, column_num);
if (next_token(s))
return -1;
emit_func_call:
@@ -24900,6 +25093,8 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
} else if (s->token.val == '.') {
if (next_token(s))
return -1;
+ column_num = s->token.column_num;
+ emit_column(s, column_num);
parse_property:
if (s->token.val == TOK_PRIVATE_NAME) {
/* private class field */
@@ -24968,6 +25163,7 @@ static __exception int js_parse_delete(JSParseState *s)
JSFunctionDef *fd = s->cur_func;
JSAtom name;
int opcode;
+ emit_column(s, s->token.column_num);
if (next_token(s))
return -1;
@@ -25342,6 +25538,7 @@ static __exception int js_parse_logical_and_or(JSParseState *s, int op,
emit_op(s, OP_drop);
if (op == TOK_LAND) {
+ emit_column(s, s->token.column_num);
if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC))
return -1;
} else {
@@ -25377,7 +25574,8 @@ static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags)
emit_op(s, OP_is_undefined_or_null);
emit_goto(s, OP_if_false, label1);
emit_op(s, OP_drop);
-
+ emit_column(s, s->token.column_num);
+
if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC))
return -1;
if (s->token.val != TOK_DOUBLE_QUESTION_MARK)
@@ -25425,6 +25623,7 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)
int opcode, op, scope;
JSAtom name0 = JS_ATOM_NULL;
JSAtom name;
+ emit_column(s, s->token.column_num);
if (s->token.val == TOK_YIELD) {
BOOL is_star = FALSE, is_async;
@@ -26289,6 +26488,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
JSContext *ctx = s->ctx;
JSAtom label_name;
int tok;
+ emit_column(s, s->token.column_num);
/* specific label handling */
/* XXX: support multiple labels on loop statements */
@@ -26973,7 +27173,8 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
parse_func_var:
if (js_parse_function_decl(s, JS_PARSE_FUNC_VAR,
JS_FUNC_NORMAL, JS_ATOM_NULL,
- s->token.ptr, s->token.line_num))
+ s->token.ptr, s->token.line_num,
+ s->token.column_num))
goto fail;
break;
}
@@ -28387,7 +28588,8 @@ static __exception int js_parse_export(JSParseState *s)
return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,
JS_FUNC_NORMAL, JS_ATOM_NULL,
s->token.ptr, s->token.line_num,
- JS_PARSE_EXPORT_NAMED, NULL);
+ s->token.column_num, JS_PARSE_EXPORT_NAMED,
+ NULL);
}
if (next_token(s))
@@ -28497,7 +28699,8 @@ static __exception int js_parse_export(JSParseState *s)
return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,
JS_FUNC_NORMAL, JS_ATOM_NULL,
s->token.ptr, s->token.line_num,
- JS_PARSE_EXPORT_DEFAULT, NULL);
+ s->token.column_num, JS_PARSE_EXPORT_DEFAULT,
+ NULL);
} else {
if (js_parse_assign_expr(s))
return -1;
@@ -28694,7 +28897,8 @@ static __exception int js_parse_source_element(JSParseState *s)
peek_token(s, TRUE) == TOK_FUNCTION)) {
if (js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT,
JS_FUNC_NORMAL, JS_ATOM_NULL,
- s->token.ptr, s->token.line_num))
+ s->token.ptr, s->token.line_num,
+ s->token.column_num))
return -1;
} else if (s->token.val == TOK_EXPORT && fd->module) {
if (js_parse_export(s))
@@ -28716,7 +28920,9 @@ static JSFunctionDef *js_new_function_def(JSContext *ctx,
JSFunctionDef *parent,
BOOL is_eval,
BOOL is_func_expr,
- const char *filename, int line_num)
+ const char *filename,
+ int line_num,
+ int column_num)
{
JSFunctionDef *fd;
@@ -28764,8 +28970,10 @@ static JSFunctionDef *js_new_function_def(JSContext *ctx,
fd->filename = JS_NewAtom(ctx, filename);
fd->line_num = line_num;
+ fd->column_num = column_num;
js_dbuf_init(ctx, &fd->pc2line);
+ js_dbuf_init(ctx, &fd->pc2column);
//fd->pc2line_last_line_num = line_num;
//fd->pc2line_last_pc = 0;
fd->last_opcode_line_num = line_num;
@@ -28824,6 +29032,7 @@ static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd)
js_free(ctx, fd->jump_slots);
js_free(ctx, fd->label_slots);
js_free(ctx, fd->line_number_slots);
+ js_free(ctx, fd->column_number_slots);
for(i = 0; i < fd->cpool_count; i++) {
JS_FreeValue(ctx, fd->cpool[i]);
@@ -28857,6 +29066,7 @@ static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd)
JS_FreeAtom(ctx, fd->filename);
dbuf_free(&fd->pc2line);
+ dbuf_free(&fd->pc2column);
js_free(ctx, fd->source);
@@ -30842,6 +31052,10 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)
s->line_number_size++;
goto no_change;
+ case OP_column_num:
+ s->column_number_size++;
+ goto no_change;
+
case OP_eval: /* convert scope index to adjusted variable index */
{
int call_argc = get_u16(bc_buf + pos + 1);
@@ -31143,6 +31357,21 @@ static void add_pc2line_info(JSFunctionDef *s, uint32_t pc, int line_num)
}
}
+/* the pc2col table gives a column number for each PC value */
+static void add_pc2col_info(JSFunctionDef *s, uint32_t pc, int column_num)
+{
+ if(s->column_number_slots != NULL
+ && s->column_number_count < s->column_number_size
+ && pc >= s->column_number_last_pc
+ && column_num != s->column_number_last) {
+ s->column_number_slots[s->column_number_count].pc = pc;
+ s->column_number_slots[s->column_number_count].column_num = column_num;
+ s->column_number_count++;
+ s->column_number_last_pc = pc;
+ s->column_number_last = column_num;
+ }
+}
+
static void compute_pc2line_info(JSFunctionDef *s)
{
if (!(s->js_mode & JS_MODE_STRIP) && s->line_number_slots) {
@@ -31181,6 +31410,45 @@ static void compute_pc2line_info(JSFunctionDef *s)
}
}
+static void compute_pc2column_info(JSFunctionDef *s)
+{
+ if(!(s->js_mode & JS_MODE_STRIP) && s->column_number_slots) {
+ int last_column_num = s->column_num;
+ uint32_t last_pc = 0;
+ int i;
+
+ js_dbuf_init(s->ctx, &s->pc2column);
+ for(i = 0; i < s->column_number_count; i++) {
+ uint32_t pc = s->column_number_slots[i].pc;
+ int column_num = s->column_number_slots[i].column_num;
+ int diff_pc, diff_column;
+
+ if (column_num < 0)
+ continue;
+
+ diff_pc = pc - last_pc;
+ diff_column = column_num - last_column_num;
+ if (diff_column == 0 || diff_pc < 0)
+ continue;
+
+ if (diff_column >= PC2COLUMN_BASE &&
+ diff_column < PC2COLUMN_BASE + PC2COLUMN_RANGE &&
+ diff_pc <= PC2COLUMN_DIFF_PC_MAX) {
+ dbuf_putc(&s->pc2column, (diff_column - PC2COLUMN_BASE) +
+ diff_pc * PC2COLUMN_RANGE + PC2COLUMN_OP_FIRST);
+ } else {
+ /* longer encoding */
+ dbuf_putc(&s->pc2column, 0);
+ dbuf_put_leb128(&s->pc2column, diff_pc);
+ dbuf_put_sleb128(&s->pc2column, diff_column);
+ }
+
+ last_pc = pc;
+ last_column_num = column_num;
+ }
+ }
+}
+
static RelocEntry *add_reloc(JSContext *ctx, LabelSlot *ls, uint32_t addr, int size)
{
RelocEntry *re;
@@ -31345,7 +31613,7 @@ static void put_short_code(DynBuf *bc_out, int op, int idx)
/* peephole optimizations and resolve goto/labels */
static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
{
- int pos, pos_next, bc_len, op, op1, len, i, line_num;
+ int pos, pos_next, bc_len, op, op1, len, i, line_num, column_num;
const uint8_t *bc_buf;
DynBuf bc_out;
LabelSlot *label_slots, *ls;
@@ -31359,7 +31627,7 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
label_slots = s->label_slots;
line_num = s->line_num;
-
+ column_num = s->column_num;
cc.bc_buf = bc_buf = s->byte_code.buf;
cc.bc_len = bc_len = s->byte_code.size;
js_dbuf_init(ctx, &bc_out);
@@ -31380,6 +31648,14 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
s->line_number_last_pc = 0;
}
+ if(s->column_number_size && !(s->js_mode & JS_MODE_STRIP)) {
+ s->column_number_slots = js_mallocz(s->ctx, sizeof(*s->column_number_slots) * s->column_number_size);
+ if(s->column_number_slots == NULL)
+ return -1;
+ s->column_number_last = s->column_num;
+ s->column_number_last_pc = 0;
+ }
+
/* initialize the 'home_object' variable if needed */
if (s->home_object_var_idx >= 0) {
dbuf_putc(&bc_out, OP_special_object);
@@ -31453,6 +31729,12 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
line_num = get_u32(bc_buf + pos + 1);
break;
+ case OP_column_num:
+ /* same with OP_line_num */
+ column_num = get_u32(bc_buf + pos + 1);
+ add_pc2col_info(s, bc_out.size, column_num);
+ break;
+
case OP_label:
{
label = get_u32(bc_buf + pos + 1);
@@ -32176,6 +32458,11 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
if (s->line_number_slots[j].pc > pos)
s->line_number_slots[j].pc -= delta;
}
+ for (j = 0; j < s->column_number_count; j++) {
+ if (s->column_number_slots[j].pc > pos) {
+ s->column_number_slots[j].pc -= delta;
+ }
+ }
continue;
}
break;
@@ -32207,8 +32494,11 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
s->label_slots = NULL;
/* XXX: should delay until copying to runtime bytecode function */
compute_pc2line_info(s);
+ compute_pc2column_info(s);
js_free(ctx, s->line_number_slots);
+ js_free(ctx, s->column_number_slots);
s->line_number_slots = NULL;
+ s->column_number_slots = NULL;
/* set the new byte code */
dbuf_free(&s->byte_code);
s->byte_code = bc_out;
@@ -32615,6 +32905,7 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
if (fd->js_mode & JS_MODE_STRIP) {
JS_FreeAtom(ctx, fd->filename);
dbuf_free(&fd->pc2line); // probably useless
+ dbuf_free(&fd->pc2column);
} else {
/* XXX: source and pc2line info should be packed at the end of the
JSFunctionBytecode structure, avoiding allocation overhead
@@ -32622,14 +32913,20 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
b->has_debug = 1;
b->debug.filename = fd->filename;
b->debug.line_num = fd->line_num;
+ b->debug.column_num = fd->column_num;
//DynBuf pc2line;
//compute_pc2line_info(fd, &pc2line);
//js_free(ctx, fd->line_number_slots)
b->debug.pc2line_buf = js_realloc(ctx, fd->pc2line.buf, fd->pc2line.size);
+ b->debug.pc2column_buf = js_realloc(ctx, fd->pc2column.buf, fd->pc2column.size);
if (!b->debug.pc2line_buf)
b->debug.pc2line_buf = fd->pc2line.buf;
+ if(!b->debug.pc2column_buf) {
+ b->debug.pc2column_buf = fd->pc2column.buf;
+ }
b->debug.pc2line_len = fd->pc2line.size;
+ b->debug.pc2column_len = fd->pc2column.size;
b->debug.source = fd->source;
b->debug.source_len = fd->source_len;
}
@@ -32710,6 +33007,7 @@ static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)
if (b->has_debug) {
JS_FreeAtomRT(rt, b->debug.filename);
js_free_rt(rt, b->debug.pc2line_buf);
+ js_free_rt(rt, b->debug.pc2column_buf);
js_free_rt(rt, b->debug.source);
}
@@ -32873,7 +33171,7 @@ static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s)
JSFunctionDef *fd;
fd = js_new_function_def(s->ctx, s->cur_func, FALSE, FALSE,
- s->filename, 0);
+ s->filename, 0, 0);
if (!fd)
return NULL;
fd->func_name = JS_ATOM_NULL;
@@ -32901,6 +33199,7 @@ static __exception int js_parse_function_decl2(JSParseState *s,
JSAtom func_name,
const uint8_t *ptr,
int function_line_num,
+ int function_column_num,
JSParseExportEnum export_flag,
JSFunctionDef **pfd)
{
@@ -33014,7 +33313,8 @@ static __exception int js_parse_function_decl2(JSParseState *s,
}
fd = js_new_function_def(ctx, fd, FALSE, is_expr,
- s->filename, function_line_num);
+ s->filename, function_line_num,
+ function_column_num);
if (!fd) {
JS_FreeAtom(ctx, func_name);
return -1;
@@ -33448,11 +33748,12 @@ static __exception int js_parse_function_decl(JSParseState *s,
JSFunctionKindEnum func_kind,
JSAtom func_name,
const uint8_t *ptr,
- int function_line_num)
+ int function_line_num,
+ int function_column_num)
{
return js_parse_function_decl2(s, func_type, func_kind, func_name, ptr,
- function_line_num, JS_PARSE_EXPORT_NONE,
- NULL);
+ function_line_num, function_column_num,
+ JS_PARSE_EXPORT_NONE, NULL);
}
static __exception int js_parse_program(JSParseState *s)
@@ -33503,10 +33804,14 @@ static void js_parse_init(JSContext *ctx, JSParseState *s,
s->ctx = ctx;
s->filename = filename;
s->line_num = 1;
+ s->column_ptr = (const uint8_t*)input;
+ s->column_last_ptr = s->column_ptr;
+ s->column_num_count = 0;
s->buf_ptr = (const uint8_t *)input;
s->buf_end = s->buf_ptr + input_len;
s->token.val = ' ';
s->token.line_num = 1;
+ s->token.column_num = 0;
}
static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj,
@@ -33620,7 +33925,7 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
js_mode |= JS_MODE_STRICT;
}
}
- fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, 1);
+ fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, 1, 0);
if (!fd)
goto fail1;
s->cur_func = fd;
@@ -34363,6 +34668,9 @@ static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj)
bc_put_leb128(s, b->debug.line_num);
bc_put_leb128(s, b->debug.pc2line_len);
dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len);
+ bc_put_leb128(s, b->debug.column_num);
+ bc_put_leb128(s, b->debug.pc2column_len);
+ dbuf_put(&s->dbuf, b->debug.pc2column_buf, b->debug.pc2column_len);
}
for(i = 0; i < b->cpool_count; i++) {
@@ -35385,6 +35693,17 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s)
if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len))
goto fail;
}
+ if (bc_get_leb128_int(s, &b->debug.column_num))
+ goto fail;
+ if (bc_get_leb128_int(s, &b->debug.pc2column_len))
+ goto fail;
+ if (b->debug.pc2column_len) {
+ b->debug.pc2column_buf = js_mallocz(ctx, b->debug.pc2column_len);
+ if (!b->debug.pc2column_buf)
+ goto fail;
+ if (bc_get_buf(s, b->debug.pc2column_buf, b->debug.pc2column_len))
+ goto fail;
+ }
#ifdef DUMP_READ_OBJECT
bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n");
#endif
@@ -37839,6 +38158,7 @@ static const JSCFunctionListEntry js_function_proto_funcs[] = {
JS_CFUNC_DEF("[Symbol.hasInstance]", 1, js_function_hasInstance ),
JS_CGETSET_DEF("fileName", js_function_proto_fileName, NULL ),
JS_CGETSET_DEF("lineNumber", js_function_proto_lineNumber, NULL ),
+ JS_CGETSET_DEF("columnNumber", js_function_proto_columnNumber, NULL),
};
/* Error class */
@@ -37934,7 +38254,7 @@ static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target,
}
/* skip the Error() function in the backtrace */
- build_backtrace(ctx, obj, NULL, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL);
+ build_backtrace(ctx, obj, NULL, 0, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL);
return obj;
exception:
JS_FreeValue(ctx, obj);
diff --git a/quickjs.h b/quickjs.h
index d4a5cd31..61630280 100644
@@ -407,6 +407,7 @@ typedef struct JSMemoryUsage {
int64_t shape_count, shape_size;
int64_t js_func_count, js_func_size, js_func_code_size;
int64_t js_func_pc2line_count, js_func_pc2line_size;
+ int64_t js_func_pc2column_count, js_func_pc2column_size;
int64_t c_func_count, array_count;
int64_t fast_array_count, fast_array_elements;
int64_t binary_object_count, binary_object_size;
diff --git a/tests/test_line_column.js b/tests/test_line_column.js
new file mode 100644
index 00000000..d1c5989c
@@ -0,0 +1,240 @@
+"use strict";
+
+function assert(actual, expected, message) {
+ if (arguments.length == 1) expected = true;
+
+ if (actual === expected) return;
+
+ if (actual !== null && expected !== null && typeof actual == 'object' &&
+ typeof expected == 'object' && actual.toString() === expected.toString())
+ return;
+
+ throw Error(
+ 'assertion failed: got |' + actual + '|' +
+ ', expected |' + expected + '|' + (message ? ' (' + message + ')' : ''));
+}
+
+/** id not exists -> should be located at id */
+function test_line_column1() {
+ try {
+ eval(`'【';A;`);
+ } catch (e) {
+ assert(e.lineNumber, 1, 'test_line_column1');
+ assert(e.columnNumber, 5, 'test_line_column1');
+ }
+}
+
+/**
+ * memeber call should be located at id:
+ * a.b.c() and c is null -> c will be located
+ */
+function test_line_column2() {
+ try {
+ eval(`
+var a = { b: { c: { d: null }} };
+a.b.c.d();
+ `);
+ } catch (e) {
+ assert(e.lineNumber, 3, 'test_line_column2');
+ assert(e.columnNumber, 7, 'test_line_column2');
+ }
+}
+
+/**
+ * memeber call should be located at id:
+ * a.b.c() and b is null -> c will be located
+ */
+function test_line_column3() {
+ try {
+ eval(`
+var a = { b: { c: { d: null }} };
+a.f.c.d();
+ `);
+ } catch (e) {
+ assert(e.lineNumber, 3, 'test_line_column3');
+ assert(e.columnNumber, 5, 'test_line_column3');
+ }
+}
+
+/** if id not exists -> should be located at id */
+function test_line_column4() {
+ try {
+ eval(`(function(){'use strict';a;}());`);
+ } catch (e) {
+ assert(e.lineNumber, 1, 'test_line_column4');
+ assert(e.columnNumber, 26, 'test_line_column4');
+ }
+}
+
+/** if id not exists -> should be located at id */
+function test_line_column5() {
+ try {
+ eval(`'【';1+1;new A();`);
+ } catch (e) {
+ assert(e.lineNumber, 1, 'test_line_column5');
+ assert(e.columnNumber, 13, 'test_line_column5');
+ }
+}
+
+/** new call should be located at 'new' */
+function test_line_column6() {
+ try {
+ eval(`'【';1+1;throw new Error();`);
+ } catch (e) {
+ assert(e.lineNumber, 1, 'test_line_column6');
+ assert(e.columnNumber, 15, 'test_line_column6');
+ }
+}
+
+/**
+ * normal call should be located at function name:
+ * a() and a is null or occur error -> a will be located
+ */
+function test_line_column7() {
+ try {
+ eval(`1+1;a();`);
+ } catch (e) {
+ assert(e.lineNumber, 1, 'test_line_column7');
+ assert(e.columnNumber, 5, 'test_line_column7');
+ }
+}
+
+/**
+ * if comment is first line,
+ * the line number of one line should be locate at next line
+ */
+function test_line_column8() {
+ try {
+ eval(`
+/**
+ * something
+ * comment
+ * here
+ */
+1+1;a();
+ `);
+ } catch (e) {
+ assert(e.lineNumber, 7, 'test_line_column8');
+ assert(e.columnNumber, 5, 'test_line_column8');
+ }
+}
+
+/** nest function call */
+function test_line_column9() {
+ try {
+ eval(`(function(){'【'(function(){'use strict';a;}())}())`);
+ } catch (e) {
+ assert(e.lineNumber, 1, 'test_line_column9');
+ assert(e.columnNumber, 41, 'test_line_column9');
+ }
+}
+
+/** multi function call */
+function test_line_column10() {
+ try {
+ eval(`
+function a(){
+ throw new Error();
+}
+
+function b(){
+ a();
+}
+
+b();
+ `);
+ } catch (e) {
+ assert(e.lineNumber, 3, 'test_line_column10');
+ assert(e.columnNumber, 11, 'test_line_column10');
+ }
+}
+
+/** syntax error should be located at latest token position */
+function test_line_column11() {
+ try {
+ eval(`
+var a = {
+ b: if(1){}
+}
+ `);
+ } catch (e) {
+ assert(e.lineNumber, 3, 'test_line_column11');
+ assert(e.columnNumber, 7, 'test_line_column11');
+ }
+}
+
+/** string template cases */
+function test_line_column12() {
+// case 1
+ try {
+ eval(`
+var a = \`\$\{b;\}
+1+1
+\`;
+ `);
+ } catch (e) {
+ assert(e.lineNumber, 2, 'test_line_column12');
+ assert(e.columnNumber, 12, 'test_line_column12');
+ }
+
+// case 2
+ try {
+ eval(`
+var a = \`1+1
+\$\{b;\}
+2+2
+\`;
+ `);
+ } catch (e) {
+ assert(e.lineNumber, 3, 'test_line_column12');
+ assert(e.columnNumber, 3, 'test_line_column12');
+ }
+
+// case 3
+ try {
+ eval(`
+var a = \`1+1
+2+2
+\${b\}\`;
+ `);
+ } catch (e) {
+ assert(e.lineNumber, 4, 'test_line_column12');
+ assert(e.columnNumber, 3, 'test_line_column12');
+ }
+
+// case 4
+ try {
+ eval(`
+var a = \`1+1
+2+2
+\${3+3\}\`;b;
+ `);
+ } catch (e) {
+ assert(e.lineNumber, 4, 'test_line_column12');
+ assert(e.columnNumber, 9, 'test_line_column12');
+ }
+}
+
+/** dynamic Function parse error should be located the latest token */
+function test_line_column13() {
+ try {
+ eval(`Function("===>", "a");`);
+ } catch (e) {
+ assert(e.lineNumber, 1, 'test_line_column13');
+ assert(e.columnNumber, 20, 'test_line_column13');
+ }
+}
+
+test_line_column1();
+test_line_column2();
+test_line_column3();
+test_line_column4();
+test_line_column5();
+test_line_column6();
+test_line_column7();
+test_line_column8();
+test_line_column9();
+test_line_column10();
+test_line_column11();
+test_line_column12();
+test_line_column13();
\ No newline at end of file
From c5b00eea281d7bad49277c4878279171370b8dc6 Mon Sep 17 00:00:00 2001
From: ErosZy <zyeros1991@gmail.com>
Date: Sat, 12 Nov 2022 23:25:18 +0800
Subject: [PATCH 2/3] fix: comments parse error because of column number
---
quickjs.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/quickjs.c b/quickjs.c
index 3fb3c09e..f99b3e57 100644
@@ -20877,7 +20877,6 @@ static __exception int next_token(JSParseState *s)
s->got_lf = TRUE; /* considered as LF for ASI */
s->column_ptr = ++p;
s->column_num_count = 0;
- p++;
} else if (*p == '\r') {
s->got_lf = TRUE; /* considered as LF for ASI */
p++;
@@ -21438,7 +21437,6 @@ static __exception int json_next_token(JSParseState *s)
s->line_num++;
s->column_ptr = ++p;
s->column_num_count = 0;
- p++;
} else if (*p == '\r') {
p++;
} else if (*p >= 0x80) {
From 5a1c24872dbaa44cbd6c2df5081e94aedf0f2aec Mon Sep 17 00:00:00 2001
From: ErosZy <zyeros1991@gmail.com>
Date: Sat, 12 Nov 2022 23:59:49 +0800
Subject: [PATCH 3/3] fix: format line
---
quickjs.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/quickjs.c b/quickjs.c
index f99b3e57..49f30bae 100644
@@ -32920,9 +32920,8 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
b->debug.pc2column_buf = js_realloc(ctx, fd->pc2column.buf, fd->pc2column.size);
if (!b->debug.pc2line_buf)
b->debug.pc2line_buf = fd->pc2line.buf;
- if(!b->debug.pc2column_buf) {
+ if(!b->debug.pc2column_buf)
b->debug.pc2column_buf = fd->pc2column.buf;
- }
b->debug.pc2line_len = fd->pc2line.size;
b->debug.pc2column_len = fd->pc2column.size;
b->debug.source = fd->source;