Skip to content

Commit

Permalink
add more strict checking
Browse files Browse the repository at this point in the history
  • Loading branch information
lunatikub committed Sep 18, 2017
1 parent 3508659 commit d3abf9c
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 16 deletions.
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ clean:
rm -f *.a *.so
rm -f simple_example
rm -f jsondump
rm -f test/test_default
rm -f test/test_links
rm -f test/test_strict
rm -f test/test_strict_links

.PHONY: all clean test

134 changes: 133 additions & 1 deletion jsmn.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,87 @@
#include "jsmn.h"

#ifdef JSMN_STRICT

typedef enum {
JSMN_TOK_UNDEFINED = 0,
JSMN_TOK_STRING = 1,
JSMN_TOK_VALUE,
JSMN_TOK_OPENING_BRACE,
JSMN_TOK_CLOSING_BRACE,
JSMN_TOK_OPENING_BRACKET,
JSMN_TOK_CLOSING_BRACKET,
JSMN_TOK_COLON,
JSMN_TOK_COMA,
JSMN_TOK_END,
} jsmn_tok_type_t;

/**
* Next token type depends on context.
*/
static inline jsmn_tok_type_t jsmn_string_next_tok(jsmntok_t *token_parent,
jsmn_tok_type_t toktype)
{
if (token_parent->type == JSMN_ARRAY && toktype == JSMN_TOK_COMA) {
return JSMN_TOK_VALUE;
} else if (toktype == JSMN_TOK_OPENING_BRACE || toktype == JSMN_TOK_COMA) {
return JSMN_TOK_STRING;
}
return JSMN_TOK_VALUE;
}

static const jsmn_tok_type_t coma_expected[] = {
JSMN_TOK_UNDEFINED, /* tokens == NULL */
JSMN_TOK_OPENING_BRACE,
JSMN_TOK_CLOSING_BRACE,
JSMN_TOK_OPENING_BRACKET,
JSMN_TOK_CLOSING_BRACKET,
JSMN_TOK_VALUE,
JSMN_TOK_END
};

static const jsmn_tok_type_t double_quote_expected[] = {
JSMN_TOK_OPENING_BRACE,
JSMN_TOK_OPENING_BRACKET,
JSMN_TOK_COLON,
JSMN_TOK_COMA,
JSMN_TOK_END
};

static const jsmn_tok_type_t closing_brace_expected[] = {
JSMN_TOK_UNDEFINED, /* tokens == NULL */
JSMN_TOK_VALUE,
JSMN_TOK_OPENING_BRACE,
JSMN_TOK_CLOSING_BRACKET,
JSMN_TOK_CLOSING_BRACE,
JSMN_TOK_END
};

static const jsmn_tok_type_t closing_bracket_expected[] = {
JSMN_TOK_UNDEFINED, /* tokens == NULL */
JSMN_TOK_VALUE,
JSMN_TOK_OPENING_BRACKET,
JSMN_TOK_CLOSING_BRACKET,
JSMN_TOK_CLOSING_BRACE,
JSMN_TOK_END
};

/**
* Return 0 if 'toktype' is found in the 'expected' token list.
* Otherwise return -1;
*/
static inline int jsmn_tok_expected(jsmn_tok_type_t curr_toktype,
const jsmn_tok_type_t *expected)
{
for (int i = 0; expected[i] != JSMN_TOK_END; ++i) {
if (curr_toktype == expected[i]) {
return 0;
}
}
return -1;
}

#endif

/**
* Allocates a fresh unused token from the token pull.
*/
Expand All @@ -18,6 +100,7 @@ static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
return tok;
}


/**
* Fills token type and boundaries.
*/
Expand Down Expand Up @@ -162,6 +245,10 @@ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
c = js[parser->pos];
switch (c) {
case '{': case '[':
#ifdef JSMN_STRICT
parser->toktype = (c == '{') ?
JSMN_TOK_OPENING_BRACE : JSMN_TOK_OPENING_BRACKET;
#endif
count++;
if (tokens == NULL) {
break;
Expand All @@ -180,6 +267,21 @@ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
parser->toksuper = parser->toknext - 1;
break;
case '}': case ']':
#ifdef JSMN_STRICT
if (tokens != NULL) {
if (c == '}') {
if (jsmn_tok_expected
(parser->toktype, closing_brace_expected)) {
return JSMN_ERROR_INVAL;
}
} else if (jsmn_tok_expected
(parser->toktype, closing_bracket_expected )) {
return JSMN_ERROR_INVAL;
}
}
parser->toktype = (c == '}') ?
JSMN_TOK_CLOSING_BRACE : JSMN_TOK_CLOSING_BRACKET;
#endif
if (tokens == NULL)
break;
type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
Expand Down Expand Up @@ -229,18 +331,41 @@ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
#endif
break;
case '\"':
#ifdef JSMN_STRICT
if (jsmn_tok_expected(parser->toktype, double_quote_expected)) {
return JSMN_ERROR_INVAL;
}
#endif
r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
if (r < 0) return r;
count++;
if (parser->toksuper != -1 && tokens != NULL)
tokens[parser->toksuper].size++;
#ifdef JSMN_STRICT
if (tokens != NULL) {
parser->toktype = jsmn_string_next_tok
(&tokens[parser->toksuper], parser->toktype);
}
#endif
break;
case '\t' : case '\r' : case '\n' : case ' ':
break;
case ':':
parser->toksuper = parser->toknext - 1;
#ifdef JSMN_STRICT
if (parser->toktype != JSMN_TOK_STRING) {
return JSMN_ERROR_INVAL;
}
parser->toktype = JSMN_TOK_COLON;
#endif
break;
case ',':
#ifdef JSMN_STRICT
if (tokens != NULL &&
jsmn_tok_expected(parser->toktype, coma_expected)) {
return JSMN_ERROR_INVAL;
}
#endif
if (tokens != NULL && parser->toksuper != -1 &&
tokens[parser->toksuper].type != JSMN_ARRAY &&
tokens[parser->toksuper].type != JSMN_OBJECT) {
Expand All @@ -256,7 +381,11 @@ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
}
}
#endif

}
#ifdef JSMN_STRICT
parser->toktype = JSMN_TOK_COMA;
#endif
break;
#ifdef JSMN_STRICT
/* In strict mode primitives are: numbers and booleans */
Expand All @@ -271,6 +400,7 @@ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
return JSMN_ERROR_INVAL;
}
}
parser->toktype = JSMN_TOK_VALUE;
#else
/* In non-strict mode every unquoted value is a primitive */
default:
Expand Down Expand Up @@ -310,5 +440,7 @@ void jsmn_init(jsmn_parser *parser) {
parser->pos = 0;
parser->toknext = 0;
parser->toksuper = -1;
#ifdef JSMN_STRICT
parser->toktype = 0;
#endif
}

3 changes: 3 additions & 0 deletions jsmn.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ typedef struct {
unsigned int pos; /* offset in the JSON string */
unsigned int toknext; /* next token to allocate */
int toksuper; /* superior token node, e.g parent object or array */
#ifdef JSMN_STRICT
int toktype;
#endif
} jsmn_parser;

/**
Expand Down
27 changes: 13 additions & 14 deletions test/tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,27 +54,26 @@ int test_object(void) {
check(parse("{\"a\": {2}}", JSMN_ERROR_INVAL, 3));
check(parse("{\"a\": {2: 3}}", JSMN_ERROR_INVAL, 3));
check(parse("{\"a\": {\"a\": 2 3}}", JSMN_ERROR_INVAL, 5));
/* FIXME */
/*check(parse("{\"a\"}", JSMN_ERROR_INVAL, 2));*/
/*check(parse("{\"a\": 1, \"b\"}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{\"a\",\"b\":1}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{\"a\":1,}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{\"a\":\"b\":\"c\"}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{,}", JSMN_ERROR_INVAL, 4));*/
check(parse("{\"a\"}", JSMN_ERROR_INVAL, 2));
check(parse("{\"a\": 1, \"b\"}", JSMN_ERROR_INVAL, 4));
check(parse("{\"a\",\"b\":1}", JSMN_ERROR_INVAL, 4));
check(parse("{\"a\":1,}", JSMN_ERROR_INVAL, 4));
check(parse("{\"a\":\"b\":\"c\"}", JSMN_ERROR_INVAL, 4));
check(parse("{,}", JSMN_ERROR_INVAL, 4));
#endif
return 0;
}

int test_array(void) {
/* FIXME */
/*check(parse("[10}", JSMN_ERROR_INVAL, 3));*/
/*check(parse("[1,,3]", JSMN_ERROR_INVAL, 3)*/
#ifdef JSMN_STRICT
check(parse("[10}", JSMN_ERROR_INVAL, 3));
check(parse("[1,,3]", JSMN_ERROR_INVAL, 3));
check(parse("[\"a\": 1]", JSMN_ERROR_INVAL, 3));
#endif
check(parse("[10]", 2, 2,
JSMN_ARRAY, -1, -1, 1,
JSMN_PRIMITIVE, "10"));
check(parse("{\"a\": 1]", JSMN_ERROR_INVAL, 3));
/* FIXME */
/*check(parse("[\"a\": 1]", JSMN_ERROR_INVAL, 3));*/
return 0;
}

Expand Down Expand Up @@ -357,8 +356,8 @@ int test_nonstrict(void) {
//nested {s don't cause a parse error.
js = "\"key {1\": 1234";
check(parse(js, 2, 2,
JSMN_STRING, "key {1", 1,
JSMN_PRIMITIVE, "1234"));
JSMN_STRING, "key {1", 1,
JSMN_PRIMITIVE, "1234"));


#endif
Expand Down

0 comments on commit d3abf9c

Please sign in to comment.