/* * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. * */ /* * This is not really json in the state it is now. * Some differences: * - Double quotes around the key in an object is not enforced. * i.e you can write: { foo : "bar" } instead of { "foo" : "bar" }. * - Comments are allowed. * - The last element in an object or array can have an ending comma. */ #include "precompiled.hpp" #include "utilities/json.hpp" #include const char* strchrnul_(const char *s, int c) { const char* tmp = strchr(s, c); return tmp == NULL ? s + strlen(s) : tmp; } JSON::JSON(const char* text, bool silent) : start(text), pos(text), mark(text), level(0), line(1), column(0), silent(silent), _valid(true) { } void JSON::parse() { assert(start != NULL, "Need something to parse"); if (start == NULL) { _valid = false; error(INTERNAL_ERROR, "JSON parser was called with a string that was NULL."); } else { _valid = parse_json_value(); } } bool JSON::valid() { return _valid; } bool JSON::parse_json_value() { int c; c = skip_to_token(); if (c == -1) { return false; } // Must start with object or array if (level == 0) { switch (c) { case '{': if (parse_json_object() == false) { return false; } c = skip_to_token(); if (c > 0) { mark_pos(); error(SYNTAX_ERROR, "Only one top level object/array is allowed."); return false; } else if (c < 0) { return false; } return true; case '[': if (parse_json_array() == false) { return false; } c = skip_to_token(); if (c > 0) { mark_pos(); error(SYNTAX_ERROR, "Only one top level object/array is allowed."); return false; } else if (c < 0) { return false; } return true; case 0: error(SYNTAX_ERROR, "EOS was encountered before any json declarations"); return false; default: error(SYNTAX_ERROR, "Json must start with an object or an array."); return false; } } else { // level > 0 switch (c) { case '{': return parse_json_object(); case '[': return parse_json_array(); case '"': return parse_json_string(); case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return parse_json_number(); case 't': return parse_json_symbol("true", JSON_TRUE); case 'f': return parse_json_symbol("false", JSON_FALSE); case 'n': return parse_json_symbol("null", JSON_NULL); case 0: error(SYNTAX_ERROR, "EOS was encountered when expecting a json value."); return false; default: error(SYNTAX_ERROR, "Could not parse as a json value (did you forget to quote your strings?)."); return false; } } } // Should only be called when we actually have the start of an object // Otherwise it is an internal error bool JSON::parse_json_object() { NOT_PRODUCT(const char* prev_pos); int c; mark_pos(); // Check that we are not called in error if (expect_any("{", "object start", INTERNAL_ERROR) <= 0) { return false; } if (!callback(JSON_OBJECT_BEGIN, NULL, level++)) { return false; } for (;;) { mark_pos(); c = skip_to_token(); if (c == 0) { error(SYNTAX_ERROR, "EOS when expecting an object key or object end"); return false; } else if (c < 0) { return false; } else if (c == '}') { // We got here from either empty object "{}" or ending comma "{a:1,}" next(); break; } NOT_PRODUCT(prev_pos = pos); if (parse_json_key() == false) { return false; } assert(pos > prev_pos, "parsing stalled"); skip_to_token(); mark_pos(); if (expect_any(":", "object key-value separator") <= 0) { return false; } skip_to_token(); mark_pos(); NOT_PRODUCT(prev_pos = pos); if (parse_json_value() == false) { return false; } assert(pos > prev_pos, "parsing stalled"); c = skip_to_token(); mark_pos(); if (expect_any(",}", "value separator or object end") <= 0) { return false; } if (c == '}') { break; } } assert(c == '}', "array parsing ended without object end token ('}')"); return callback(JSON_OBJECT_END, NULL, --level); } // Should only be called when we actually have the start of an array // Otherwise it is an internal error bool JSON::parse_json_array() { NOT_PRODUCT(const char* prev_pos); int c; mark_pos(); // Check that we are not called in error if (expect_any("[", "array start character", INTERNAL_ERROR) <= 0) { return false; } if (!callback(JSON_ARRAY_BEGIN, NULL, level++)) { return false; } for (;;) { mark_pos(); c = skip_to_token(); if (c == 0) { error(SYNTAX_ERROR, "EOS when expecting a json value or array end"); return false; } else if (c < 0) { return false; } else if (c == ']') { // We got here from either empty array "[]" or ending comma "[1,]" next(); break; } mark_pos(); NOT_PRODUCT(prev_pos = pos); if (parse_json_value() == false) { return false; } assert(pos > prev_pos, "parsing stalled"); c = skip_to_token(); mark_pos(); if (expect_any(",]", "value separator or array end") <= 0) { return false; } if (c == ']') { break; } } assert(c == ']', "array parsing ended without array end token (']')"); return callback(JSON_ARRAY_END, NULL, --level); } bool JSON::parse_json_string(bool key) { const char* end; JSON_VAL v; mark_pos(); if (expect_any("\"", "string start character", INTERNAL_ERROR) <= 0) { return false; } end = strchr(pos, '"'); // TODO: escapes if (end == NULL) { error(SYNTAX_ERROR, "String started here never ended. Expected \'\"\' before EOS."); return false; } v.str.start = pos; v.str.length = end - pos; skip(end - pos); if (expect_any("\"", "string end character", INTERNAL_ERROR) <= 0) { return false; } if (key == true) { return callback(JSON_KEY, &v, level); } else { return callback(JSON_STRING, &v, level); } } // TODO: hotspot equivalents? static bool is_alpha(u_char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); } static bool is_numeric(u_char c) { return (c >= '0' && c <= '9'); } static bool is_alnum(u_char c) { return is_alpha(c) || is_numeric(c); } static bool is_word(u_char c) { return c == '_' || is_alnum(c); } // Allow object keys to be without quotation, // but then restrict to ([a-zA-Z0-9_])+ bool JSON::parse_json_key() { const char* begin; JSON_VAL v; u_char c; mark_pos(); c = peek(); if (c == '"') { return parse_json_string(true); } begin = pos; c = peek(); if (c == 0) { error(SYNTAX_ERROR, "Got EOS when expecting an object key."); return false; } else if (is_word(c) == false) { error(SYNTAX_ERROR, "Expected an object key, which can be a double-quoted (\") string or a simple string (only alphanumeric characters and underscore, separated by whitespace) that doesn't need to be quoted."); return false; } for (;;) { c = peek(); // Allow the key to be delimited by control characters and the object key-value separator ':' if (c <= ' ' || c == ':') { break; } else if (is_word(c) == false) { error(SYNTAX_ERROR, "Object key need to be quoted, or consist entirely of alphanumeric characters and underscores."); return false; } next(); } v.str.start = begin; v.str.length = pos - begin; return callback(JSON_KEY, &v, level); } bool JSON::parse_json_number() { double double_value; int tokens, read; JSON_VAL v; mark_pos(); // Parsing number - for simplicity ints are limited to 2**53 // sscanf as a double and check if part is 0. tokens = sscanf(pos, "%lf%n", &double_value, &read); assert(tokens <= 1, "scanf implementation that counts %n as a token, parsing json numbers will always fail"); if (tokens == 1) { assert(read > 0, "sanity"); if (floor(double_value) == double_value) { // No exponent - treat as an int v.int_value = (int)double_value; if (!callback(JSON_NUMBER_INT, &v, level)) { return false; } } else { v.double_value = double_value; if (!callback(JSON_NUMBER_FLOAT, &v, level)) { return false; } } skip(read); return true; } error(SYNTAX_ERROR, "Couldn't parse json number (note that exponents are not supported)."); return false; } bool JSON::parse_json_symbol(const char* name, JSON_TYPE symbol) { if (expect_string(name, "maybe you forgot to quote your strings?") == false) { mark_pos(); return false; } return callback(symbol, NULL, level); } void JSON::mark_pos() { assert((mark == start || *(mark - 1)) != 0, "buffer overrun"); assert(mark <= pos, "mark runahead"); u_char c; while (mark < pos) { c = *mark; assert(c != 0, "pos buffer overrun?"); if (c != 0) { mark++; column++; } if (c == '\n') { line++; column = 0; } } assert(mark <= pos, "mark runahead"); } u_char JSON::next() { assert((pos == start || *(pos - 1)) != 0, "buffer overrun"); u_char c = *pos; if (c != 0) { pos++; } return c; } u_char JSON::peek() { return *pos; } // Peek ahead i chars (0 is same as peek()) u_char JSON::peek(size_t i) { u_char c; const char* p; p = pos; c = *p; while (i > 0 && c != 0) { i--; p++; c = *p; } return c; } /* * Check that one of the expected characters is next in the stream. * If not, it is an error. * Returns 0 if EOS is encountered. * Returns -1 if the next character was not one of the expected. * Otherwise consumes and returns the expected character that was encountered. */ int JSON::expect_any(const char* valid_chars, const char* error_msg, JSON_ERROR e) { size_t len; u_char c; len = strlen(valid_chars); assert(len > 0, "need non-empty string"); c = peek(); if (c == 0) { error(e, "Got EOS when expecting %s (%s\'%s\').", error_msg, len > 1 ? "one of " : "", valid_chars); return 0; } for (size_t i = 0; i < len; i++) { if (c == valid_chars[i]) { return next(); } } error(e, "Expected %s (%s\'%s\').", error_msg, len > 1 ? "one of " : "", valid_chars); return -1; } /* * Check that the expected string is next in the stream. * If not, it is an error. * Consumes the expected characters if they are present. * Returns true if the expected characters were present, otherwise false. */ bool JSON::expect_string(const char* expected_string, const char* error_msg, JSON_ERROR e) { u_char c, expected_char; size_t len; assert(expected_string != NULL, "need non-null string"); len = strlen(expected_string); assert(len > 0, "need non-empty string"); for (size_t i = 0; i < len; i++) { expected_char = expected_string[i]; assert(expected_char > ' ', "not sane for control characters"); if (expected_char <= ' ') { error(INTERNAL_ERROR, "expect got a control char"); } c = pos[i]; if (c == 0) { error(e, "EOS encountered when expecting %s (\"%s\")", error_msg, expected_string); return false; } else if (c != expected_char) { error(e, "Expected \"%s\" (%s)", expected_string, error_msg); return false; } } skip(len); return true; } /* * Skip i characters. * Returns number of characters skipped. */ size_t JSON::skip(size_t i) { u_char c; size_t j; c = peek(); for (j = i; c != 0 && j > 0; j--) { c = next(); } return i - j; } /* * Skip whitespace and comments. * Returns the first token after whitespace/comments without consuming it * Returns 0 if EOS is encountered. * Returns -1 if there is an error */ int JSON::skip_to_token() { for (;;) { int c = peek(0); if (c == '/') { u_char c2 = peek(1); if (c2 == '/') { c = skip_line_comment(); } else if (c2 == '*') { c = skip_block_comment(); if (c < 0) { return -1; } } // Fall through to keep checking if there // are more whitespace / comments to skip } if (c == 0 || c > ' ') { return c; } next(); } return 0; } /* * Skip to, and return the wanted char without consuming it * Returns 0 if EOS is encountered. */ u_char JSON::skip_to(u_char want) { // We want the bookkeeping done in next(). // Otherwise strchr could have been used. u_char c; for(;;) { c = peek(); if (c == 0 || c == want) { return c; } next(); } } /* * Should only be called when we actually have a line comment to skip. * Otherwise it is an internal error. * * Will return the first token after the line comment without consuming it. * Returns 0 if EOS is encoutered. */ u_char JSON::skip_line_comment() { u_char c; // Check that we are not called in error expect_any("/", "line comment start", INTERNAL_ERROR); expect_any("/", "line comment start", INTERNAL_ERROR); c = skip_to('\n'); if (c == 0) { return 0; } next(); return next(); } /* * Should only be called when we actually have a block comment to skip. * Otherwise it is an internal error. * * Returns the first token after the block comment without consuming it. * Returns -1 if EOS is encountered in the middle of a comment. */ int JSON::skip_block_comment() { const char* current; // Check that we are not called in error. if (peek() != '/' || peek(1) != '*') { // Let expect handle EOS. expect_string("/*", "block comment start", INTERNAL_ERROR); return 0; } current = pos; for (;;) { current = strchrnul_(current, '*'); if (current[0] == 0 || current[1] == 0) { // Advance error marker to start of block comment mark_pos(); error(SYNTAX_ERROR, "Block comment started here never ended. Expected \"*/\" before EOS."); return -1; } if (current[1] == '/') { pos = current; if (expect_string("*/", "block comment end", INTERNAL_ERROR) == false) { return -1; } // Found block comment end return peek(); } current++; } } const char* JSON::strerror(JSON_ERROR e) { switch (e) { case SYNTAX_ERROR: return "Syntax error"; case INTERNAL_ERROR: return "Internal error"; case KEY_ERROR: return "Key error"; case VALUE_ERROR: return "Value error"; default: ShouldNotReachHere(); return "Unknown error"; } } void JSON::error(JSON_ERROR e, const char* format, ...) { _valid = false; if (silent == false) { const char* line_start; const char* tmp; size_t line_length; va_list args; u_char c; printf("%s on line %u byte %u: ", JSON::strerror(e), line, column + 1); va_start(args, format); vprintf(format, args); printf("\n"); va_end(args); line_start = mark - column; assert(line_start >= start, "out of bounds"); assert(line_start <= mark, "out of bounds"); assert(line_start == start || line_start[-1] == '\n', "line counting error"); c = *pos; if (c == 0) { printf(" Got "); printf("EOS.\n"); } tmp = mark; c = *tmp; if (c > ' ') { printf(" At "); printf("'"); while (c > ' ') { printf("%c", c); tmp++; c = *tmp; } printf("'.\n"); } // Skip to newline or EOS tmp = strchrnul_(mark, '\n'); line_length = tmp - line_start; printf("%s\n", line_start); } } #ifndef PRODUCT void JSONTest::test(const char* text, bool should_pass) { JSONTest json(text); if (should_pass) { assert(json.valid() == true, "failed on a valid json string"); if (VerboseInternalVMTests) { printf("-- json test passed as expected --\n"); } } else { assert(json.valid() == false, "succeeded on an invalid json string"); if (VerboseInternalVMTests) { printf("-- json test failed as expected --\n"); } } } JSONTest::JSONTest(const char* text) : JSON(text, VerboseInternalVMTests == false) { prev = JSON_NONE; parse(); } bool JSONTest::test() { JSONTest::test("{}", true); JSONTest::test("[]", true); JSONTest::test(" { } ", true); JSONTest::test(" [ ] ", true); JSONTest::test("\"error\"", false); JSONTest::test("error", false); JSONTest::test("1", false); JSONTest::test("1.2", false); JSONTest::test("true", false); JSONTest::test("false", false); JSONTest::test("null", false); JSONTest::test("[ 1 ]", true); JSONTest::test("[ 1, ]", true); JSONTest::test("[ true ]", true); JSONTest::test("[ true, ]", true); JSONTest::test("[ false ]", true); JSONTest::test("[ false, ]", true); JSONTest::test("[ null ]", true); JSONTest::test("[ null, ]", true); JSONTest::test("[ \"\" ]", true); JSONTest::test("[ \"\", ]", true); JSONTest::test("[ \"elem1\" ]", true); JSONTest::test("[ \"elem1\", ]", true); JSONTest::test("[ \"elem1\", ]", true); JSONTest::test("[ \"elem1\" ]", true); JSONTest::test("[ \"elem1\", \"elem2\" ]", true); JSONTest::test("[ \"elem1\", \"elem2\", ]", true); JSONTest::test("[ \"elem1\" ] { }", false); JSONTest::test("[ elem1, \"elem2\" ]", false); JSONTest::test("[ \"elem1\"", false); JSONTest::test("[ \"elem1 ]", false); JSONTest::test("[ \"elem1\", \"elem2\"", false); JSONTest::test("[ truefoo ]", false); JSONTest::test("[ falsefoo ]", false); JSONTest::test("[ nullfoo ]", false); JSONTest::test("{ key : 1 }", true); JSONTest::test("{ key : 1, }", true); JSONTest::test("{ key : 1,2 }", true); JSONTest::test("{ key : true }", true); JSONTest::test("{ key : true, }", true); JSONTest::test("{ key : false }", true); JSONTest::test("{ key : false, }", true); JSONTest::test("{ key : null }", true); JSONTest::test("{ key : null, }", true); JSONTest::test("{ \"\" : \"\" }", true); JSONTest::test("{ \"\" : \"\", }", true); JSONTest::test("{ \"key1\" : \"val1\" }", true); JSONTest::test("{ \"key1\" : \"val1\", }", true); JSONTest::test("{ \"key1\" : \"val1\", \"key2\" : \"val2\" }", true); JSONTest::test("{ \"key1\" : \"val1\", \"key2\" : \"val2\", }", true); JSONTest::test("{ \"key\" : \"val\" } [ \"error\" ]", false); JSONTest::test("{ \"key\" : \"val\" ", false); JSONTest::test("/**/ { }", true); JSONTest::test("/* */ { }", true); JSONTest::test("/*foo*/ { }", true); JSONTest::test("/* *foo */ { }", true); JSONTest::test("/* *foo* */ { }", true); JSONTest::test("/* /*foo */ { }", true); JSONTest::test("{ } /* foo */", true); JSONTest::test("{ } /* foo */ ", true); JSONTest::test("{ } //", true); JSONTest::test("{ } // ", true); JSONTest::test("{ } // foo", true); JSONTest::test("/* * / { }", false); JSONTest::test("/ * */ { }", false); JSONTest::test("// { }", false); JSONTest::test("/* { } */", false); JSONTest::test("/* { } */ ", false); JSONTest::test("/* { } ", false); JSONTest::test("{ } /* ", false); JSONTest::test("/* { } *", false); JSONTest::test("{ /* } */", false); JSONTest::test("[ /* ] */", false); JSONTest::test("{ key : \"val\", /* } */", false); JSONTest::test("[ \"val\", /* ] */", false); JSONTest::test("/* comment */{ key1 : { \"key2\" : { \"key3\" : [ \"elem1\", \"elem2\", { \"key4\" : null }, 3 , 2 , 1 , 0 , -1 , -2 , -3 , true, false, null, ] }, \"key5\" : true }, \"key6\" : [ \"☃\" ], key7 : \"val\",}", true); JSONTest::test("/* comment */ { \"key1\" : { \"key2\" : { \"key3\" : [ \"elem1\", \"elem2\", { \"key4\" : null }, 3 , 2 , 1 , 0 , -1 , -2 , -3 , true, false, null, ] }, \"key5\" : true }, \"key6\" : [ \"☃\" ], key7 : \"val\",}", true); JSONTest::test("/*comment*/{\"ff1 fsd\":{\"☃\":{\"☃\":[\"☃\",\"☃\"]},\"☃\":true},\"☃\":[\"☃\"],\"foo\":\"☃\",}", true); JSONTest::test("/* comment */ { key1 error : { \"☃\" : { \"☃\" : [ \"☃\", \"☃\" ] }, \"☃\" : true }, \"baz\" : [ \"☃\" ], foo : \"☃\",}", false); // first key needs to be quoted since it contains a space JSONTest::test("[\n]", true); JSONTest::test( "[" "\n" " {" " // pattern to match against class+method+signature" "\n" " // leading and trailing wildcard (*) allowed" "\n" " match: \"foo.bar.*\"," "\n" " " "\n" " // override defaults for specified compiler" "\n" " // we may differentiate between levels too. TBD." "\n" " c1: {" "\n" " //override c1 presets " "\n" " array_bounds_check_removal: false" "\n" " }," "\n" "" "\n" " c2: {" "\n" " // control inlining of method" "\n" " // + force inline, - dont inline" "\n" " inline : [ \"+java.util.*\", \"-com.sun.*\"]," "\n" " }," "\n" "" "\n" " // directives outside a specific preset applies to all compilers" "\n" " inline : [ \"+java.util.*\", \"-com.sun.*\"]," "\n" " print_assembly: true," "\n" " verify_oopmaps: true," "\n" " max_loop_unrolling: 5" "\n" " }," "\n" " {" "\n" " // matching several patterns require an array" "\n" " match: [\"baz.*\",\"frob*\"]," "\n" "" "\n" " // only enable c1 for this directive" "\n" " // all enabled by default. Command disables all not listed" "\n" " enable: \"c1\"," "\n" "" "\n" " // applies to all compilers" "\n" " // + force inline, - dont inline" "\n" " inline : [ \"+java.util.*\", \"-com.sun.*\"]," "\n" " print_inlining: true," "\n" "" "\n" " // force matching compiles to be blocking/syncronous" "\n" " blocking_compile: true" "\n" " }," "\n" "]" "\n", true); return true; } void JSONTest::log(uint indent, const char* format, ...) { if (VerboseInternalVMTests) { if (prev != JSON_KEY) { for (uint i = 0; i < indent; i++) { printf(" "); } } va_list args; va_start(args, format); vprintf(format, args); va_end(args); } } bool JSONTest::callback(JSON_TYPE t, JSON_VAL* v, uint rlevel) { switch (t) { case JSON_OBJECT_BEGIN: log(rlevel, "{\n"); prev = JSON_NONE; // Only care about JSON_KEY, to indent correctly return true; case JSON_OBJECT_END: log(rlevel, "},\n"); prev = JSON_NONE; return true; case JSON_ARRAY_BEGIN: log(rlevel, "[\n"); prev = JSON_NONE; return true; case JSON_ARRAY_END: log(rlevel, "],\n"); prev = JSON_NONE; return true; case JSON_KEY: if (VerboseInternalVMTests) { for (uint i = 0; i < rlevel; i++) { printf(" "); } printf(""); for (size_t i = 0; i < v->str.length; i++) { u_char c = v->str.start[i]; assert(c != 0, "string overrun"); if (c == 0) { return false; } printf("%c", c); } printf(" : "); } prev = JSON_KEY; return true; case JSON_STRING: if (VerboseInternalVMTests) { if (prev != JSON_KEY) { for (uint i = 0; i < rlevel; i++) { printf(" "); } } printf(""); for (size_t i = 0; i < v->str.length; i++) { u_char c = v->str.start[i]; assert(c != 0, "string overrun"); if (c == 0) { return false; } printf("%c", c); } printf(",\n"); } prev = JSON_NONE; return true; case JSON_NUMBER_INT: log(rlevel, "%" PRId64 ",\n", v->int_value); prev = JSON_NONE; return true; case JSON_NUMBER_FLOAT: log(rlevel, "%lf,\n", v->double_value); prev = JSON_NONE; return true; case JSON_TRUE: log(rlevel, ",\n"); prev = JSON_NONE; return true; case JSON_FALSE: log(rlevel, ",\n"); prev = JSON_NONE; return true; case JSON_NULL: log(rlevel, ",\n"); prev = JSON_NONE; return true; default: error(INTERNAL_ERROR, "unknown JSON type"); return false; } } #endif