Browse Source

Merge branch 'json-syntax-error-offset'

Add a byte offset to JSON.parse() syntax error to make it easier to debug
broken JSON.
pull/85/head
Sami Vaarala 10 years ago
parent
commit
3f6a9153d2
  1. 3
      RELEASES.rst
  2. 27
      api-testcases/test-json.c
  3. 34
      ecmascript-testcases/test-bi-json-dec-error-offset.js
  4. 13
      src/duk_bi_json.c
  5. 1
      src/duk_json.h
  6. 3
      src/duk_strings.c
  7. 6
      src/duk_strings.h

3
RELEASES.rst

@ -647,6 +647,9 @@ Planned
the output a valid Javascript string, so that it can be embedded in a
web page or parsed with eval (see GH-68)
* Change JSON.parse() to include a byte offset with a syntax error to help
pinpoint JSON errors
* Use deep C stack for dukweb.js to remove some compiler recursion limit
limitations (see GH-67)

27
api-testcases/test-json.c

@ -16,7 +16,10 @@ JSON decoded meaningOfLife is: 42
top after: 0
==> rc=0, result='undefined'
*** test_decode_error (duk_safe_call)
==> rc=1, result='SyntaxError: invalid json'
ret: 1
SyntaxError: invalid json (at offset N)
top after: 0
==> rc=0, result='undefined'
===*/
static duk_ret_t test_encode(duk_context *ctx) {
@ -65,9 +68,29 @@ static duk_ret_t test_decode_apidoc(duk_context *ctx) {
return 0;
}
static duk_ret_t test_decode_error_raw(duk_context *ctx) {
duk_json_decode(ctx, -1);
return 1;
}
static duk_ret_t test_decode_error(duk_context *ctx) {
/* The JSON.parse() error message includes a byte offset, so we need to
* normalize it.
*/
duk_int_t ret;
duk_push_string(ctx, "{\"meaningOfLife\":,42}");
duk_json_decode(ctx, -1);
ret = duk_safe_call(ctx, test_decode_error_raw, 1 /*nargs*/, 1 /*nrets*/);
printf("ret: %ld\n", (long) ret);
duk_eval_string(ctx, "(function (x) { print(x.replace(/at offset \\d+/, 'at offset N')); })");
duk_dup(ctx, -2);
duk_to_string(ctx, -1);
duk_call(ctx, 1 /*nargs*/);
duk_pop(ctx); /* duk_call result */
duk_pop(ctx); /* duk_safe_call result */
printf("top after: %ld\n", (long) duk_get_top(ctx));
duk_set_top(ctx, 0);
return 0;

34
ecmascript-testcases/test-bi-json-dec-error-offset.js

@ -0,0 +1,34 @@
/*
* Test JSON error byte offset
*/
/*---
{
"custom": true
}
---*/
/*===
SyntaxError: invalid json (at offset 11)
SyntaxError: invalid json (at offset 17)
===*/
function test1() {
JSON.parse('[ "\ufedcfoo"; ]'); // error at char offset 8, byte offset, 11
}
function test2() {
JSON.parse('{ "foo": "bar" } x'); // error at byte offset 17
}
try {
test1();
} catch (e) {
print(e);
}
try {
test2();
} catch (e) {
print(e);
}

13
src/duk_bi_json.c

@ -68,9 +68,11 @@ DUK_LOCAL_DECL duk_bool_t duk__enc_allow_into_proplist(duk_tval *tv);
DUK_LOCAL void duk__dec_syntax_error(duk_json_dec_ctx *js_ctx) {
/* Shared handler to minimize parser size. Cause will be
* hidden, unfortunately.
* hidden, unfortunately, but we'll have an offset which
* is often quite enough.
*/
DUK_ERROR(js_ctx->thr, DUK_ERR_SYNTAX_ERROR, DUK_STR_INVALID_JSON);
DUK_ERROR(js_ctx->thr, DUK_ERR_SYNTAX_ERROR, DUK_STR_FMT_INVALID_JSON,
(long) (js_ctx->p - js_ctx->p_start));
}
DUK_LOCAL void duk__dec_eat_white(duk_json_dec_ctx *js_ctx) {
@ -453,7 +455,7 @@ DUK_LOCAL void duk__dec_number(duk_json_dec_ctx *js_ctx) {
(duk_tval *) duk_get_tval(ctx, -1)));
duk_numconv_parse(ctx, 10 /*radix*/, s2n_flags);
if (duk_is_nan(ctx, -1)) {
DUK_ERROR(js_ctx->thr, DUK_ERR_SYNTAX_ERROR, DUK_STR_INVALID_NUMBER);
duk__dec_syntax_error(js_ctx);
}
DUK_ASSERT(duk_is_number(ctx, -1));
DUK_DDD(DUK_DDDPRINT("parse_number: final number: %!T",
@ -1708,7 +1710,8 @@ void duk_bi_json_parse_helper(duk_context *ctx,
h_text = duk_to_hstring(ctx, idx_value); /* coerce in-place */
DUK_ASSERT(h_text != NULL);
js_ctx->p = (duk_uint8_t *) DUK_HSTRING_GET_DATA(h_text);
js_ctx->p_start = (duk_uint8_t *) DUK_HSTRING_GET_DATA(h_text);
js_ctx->p = js_ctx->p_start;
js_ctx->p_end = ((duk_uint8_t *) DUK_HSTRING_GET_DATA(h_text)) +
DUK_HSTRING_GET_BYTELEN(h_text);
@ -1719,7 +1722,7 @@ void duk_bi_json_parse_helper(duk_context *ctx,
*/
if (js_ctx->p != js_ctx->p_end) {
DUK_ERROR(thr, DUK_ERR_SYNTAX_ERROR, DUK_STR_INVALID_JSON);
duk__dec_syntax_error(js_ctx);
}
if (duk_is_callable(ctx, idx_reviver)) {

1
src/duk_json.h

@ -57,6 +57,7 @@ typedef struct {
typedef struct {
duk_hthread *thr;
duk_uint8_t *p;
duk_uint8_t *p_start;
duk_uint8_t *p_end;
duk_idx_t idx_reviver;
duk_small_uint_t flags;

3
src/duk_strings.c

@ -58,8 +58,7 @@ DUK_INTERNAL const char *duk_str_array_length_over_2g = "array length over 2G";
/* JSON */
DUK_INTERNAL const char *duk_str_fmt_ptr = "%p";
DUK_INTERNAL const char *duk_str_invalid_json = "invalid json";
DUK_INTERNAL const char *duk_str_invalid_number = "invalid number";
DUK_INTERNAL const char *duk_str_fmt_invalid_json = "invalid json (at offset %ld)";
DUK_INTERNAL const char *duk_str_jsondec_reclimit = "json decode recursion limit";
DUK_INTERNAL const char *duk_str_jsonenc_reclimit = "json encode recursion limit";
DUK_INTERNAL const char *duk_str_cyclic_input = "cyclic input";

6
src/duk_strings.h

@ -122,16 +122,14 @@ DUK_INTERNAL_DECL const char *duk_str_array_length_over_2g;
#endif /* !DUK_SINGLE_FILE */
#define DUK_STR_FMT_PTR duk_str_fmt_ptr
#define DUK_STR_INVALID_JSON duk_str_invalid_json
#define DUK_STR_INVALID_NUMBER duk_str_invalid_number
#define DUK_STR_FMT_INVALID_JSON duk_str_fmt_invalid_json
#define DUK_STR_JSONDEC_RECLIMIT duk_str_jsondec_reclimit
#define DUK_STR_JSONENC_RECLIMIT duk_str_jsonenc_reclimit
#define DUK_STR_CYCLIC_INPUT duk_str_cyclic_input
#if !defined(DUK_SINGLE_FILE)
DUK_INTERNAL_DECL const char *duk_str_fmt_ptr;
DUK_INTERNAL_DECL const char *duk_str_invalid_json;
DUK_INTERNAL_DECL const char *duk_str_invalid_number;
DUK_INTERNAL_DECL const char *duk_str_fmt_invalid_json;
DUK_INTERNAL_DECL const char *duk_str_jsondec_reclimit;
DUK_INTERNAL_DECL const char *duk_str_jsonenc_reclimit;
DUK_INTERNAL_DECL const char *duk_str_cyclic_input;

Loading…
Cancel
Save