From 0a9b9d9c6ea04847af83c32930176ec42ba6c842 Mon Sep 17 00:00:00 2001 From: vslashg Date: Mon, 9 Sep 2024 20:30:16 -0400 Subject: [PATCH 01/32] Fix a parser bug where tokens are misidentified as commas. (#1502) * Fix a parser bug where tokens are misidentified as commas. In the old and new readers, when parsing an object, a comment followed by any non-`}` token is treated as a comma. The new unit test required changing the runjsontests.py flag regime so that failure tests could be run with default settings. * Honor allowComments==false mode. Much of the comment handling in the parsers is bespoke, and does not honor this flag. By unfiying it under a common API, the parser is simplified and strict mode is now more correctly strict. Note that allowComments mode does not allow for comments in arbitrary locations; they are allowed only in certain positions. Rectifying this is a bigger effort, since collectComments mode requires storing the comments somewhere, and it's not immediately clear where in the DOM all such comments should live. --------- Co-authored-by: Jordan Bayles --- include/json/reader.h | 2 +- src/jsontestrunner/main.cpp | 7 ++- src/lib_json/json_reader.cpp | 74 +++++++++------------------ test/data/fail_strict_comment_01.json | 4 ++ test/data/fail_strict_comment_02.json | 4 ++ test/data/fail_strict_comment_03.json | 3 ++ test/data/fail_test_object_02.json | 1 + test/runjsontests.py | 9 ++-- 8 files changed, 49 insertions(+), 55 deletions(-) create mode 100644 test/data/fail_strict_comment_01.json create mode 100644 test/data/fail_strict_comment_02.json create mode 100644 test/data/fail_strict_comment_03.json create mode 100644 test/data/fail_test_object_02.json diff --git a/include/json/reader.h b/include/json/reader.h index 10c6a4f..85539d1 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -190,6 +190,7 @@ private: using Errors = std::deque; bool readToken(Token& token); + bool readTokenSkippingComments(Token& token); void skipSpaces(); bool match(const Char* pattern, int patternLength); bool readComment(); @@ -221,7 +222,6 @@ private: int& column) const; String getLocationLineAndColumn(Location location) const; void addComment(Location begin, Location end, CommentPlacement placement); - void skipCommentTokens(Token& token); static bool containsNewLine(Location begin, Location end); static String normalizeEOL(Location begin, Location end); diff --git a/src/jsontestrunner/main.cpp b/src/jsontestrunner/main.cpp index df717ff..ab6a800 100644 --- a/src/jsontestrunner/main.cpp +++ b/src/jsontestrunner/main.cpp @@ -240,11 +240,14 @@ static int parseCommandLine(int argc, const char* argv[], Options* opts) { return printUsage(argv); } int index = 1; - if (Json::String(argv[index]) == "--json-checker") { - opts->features = Json::Features::strictMode(); + if (Json::String(argv[index]) == "--parse-only") { opts->parseOnly = true; ++index; } + if (Json::String(argv[index]) == "--strict") { + opts->features = Json::Features::strictMode(); + ++index; + } if (Json::String(argv[index]) == "--json-config") { printConfig(); return 3; diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index 8dcd2b5..b12c6b8 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -129,7 +129,7 @@ bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root, bool successful = readValue(); Token token; - skipCommentTokens(token); + readTokenSkippingComments(token); if (collectComments_ && !commentsBefore_.empty()) root.setComment(commentsBefore_, commentAfter); if (features_.strictRoot_) { @@ -157,7 +157,7 @@ bool Reader::readValue() { throwRuntimeError("Exceeded stackLimit in readValue()."); Token token; - skipCommentTokens(token); + readTokenSkippingComments(token); bool successful = true; if (collectComments_ && !commentsBefore_.empty()) { @@ -225,14 +225,14 @@ bool Reader::readValue() { return successful; } -void Reader::skipCommentTokens(Token& token) { +bool Reader::readTokenSkippingComments(Token& token) { + bool success = readToken(token); if (features_.allowComments_) { - do { - readToken(token); - } while (token.type_ == tokenComment); - } else { - readToken(token); + while (success && token.type_ == tokenComment) { + success = readToken(token); + } } + return success; } bool Reader::readToken(Token& token) { @@ -446,12 +446,7 @@ bool Reader::readObject(Token& token) { Value init(objectValue); currentValue().swapPayload(init); currentValue().setOffsetStart(token.start_ - begin_); - while (readToken(tokenName)) { - bool initialTokenOk = true; - while (tokenName.type_ == tokenComment && initialTokenOk) - initialTokenOk = readToken(tokenName); - if (!initialTokenOk) - break; + while (readTokenSkippingComments(tokenName)) { if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object return true; name.clear(); @@ -480,15 +475,11 @@ bool Reader::readObject(Token& token) { return recoverFromError(tokenObjectEnd); Token comma; - if (!readToken(comma) || - (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && - comma.type_ != tokenComment)) { + if (!readTokenSkippingComments(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) { return addErrorAndRecover("Missing ',' or '}' in object declaration", comma, tokenObjectEnd); } - bool finalizeTokenOk = true; - while (comma.type_ == tokenComment && finalizeTokenOk) - finalizeTokenOk = readToken(comma); if (comma.type_ == tokenObjectEnd) return true; } @@ -518,10 +509,7 @@ bool Reader::readArray(Token& token) { Token currentToken; // Accept Comment after last item in the array. - ok = readToken(currentToken); - while (currentToken.type_ == tokenComment && ok) { - ok = readToken(currentToken); - } + ok = readTokenSkippingComments(currentToken); bool badTokenType = (currentToken.type_ != tokenArraySeparator && currentToken.type_ != tokenArrayEnd); if (!ok || badTokenType) { @@ -943,6 +931,7 @@ private: using Errors = std::deque; bool readToken(Token& token); + bool readTokenSkippingComments(Token& token); void skipSpaces(); void skipBom(bool skipBom); bool match(const Char* pattern, int patternLength); @@ -976,7 +965,6 @@ private: int& column) const; String getLocationLineAndColumn(Location location) const; void addComment(Location begin, Location end, CommentPlacement placement); - void skipCommentTokens(Token& token); static String normalizeEOL(Location begin, Location end); static bool containsNewLine(Location begin, Location end); @@ -1030,7 +1018,7 @@ bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root, bool successful = readValue(); nodes_.pop(); Token token; - skipCommentTokens(token); + readTokenSkippingComments(token); if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) { addError("Extra non-whitespace after JSON value.", token); return false; @@ -1058,7 +1046,7 @@ bool OurReader::readValue() { if (nodes_.size() > features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue()."); Token token; - skipCommentTokens(token); + readTokenSkippingComments(token); bool successful = true; if (collectComments_ && !commentsBefore_.empty()) { @@ -1145,14 +1133,14 @@ bool OurReader::readValue() { return successful; } -void OurReader::skipCommentTokens(Token& token) { +bool OurReader::readTokenSkippingComments(Token& token) { + bool success = readToken(token); if (features_.allowComments_) { - do { - readToken(token); - } while (token.type_ == tokenComment); - } else { - readToken(token); + while (success && token.type_ == tokenComment) { + success = readToken(token); + } } + return success; } bool OurReader::readToken(Token& token) { @@ -1449,12 +1437,7 @@ bool OurReader::readObject(Token& token) { Value init(objectValue); currentValue().swapPayload(init); currentValue().setOffsetStart(token.start_ - begin_); - while (readToken(tokenName)) { - bool initialTokenOk = true; - while (tokenName.type_ == tokenComment && initialTokenOk) - initialTokenOk = readToken(tokenName); - if (!initialTokenOk) - break; + while (readTokenSkippingComments(tokenName)) { if (tokenName.type_ == tokenObjectEnd && (name.empty() || features_.allowTrailingCommas_)) // empty object or trailing comma @@ -1491,15 +1474,11 @@ bool OurReader::readObject(Token& token) { return recoverFromError(tokenObjectEnd); Token comma; - if (!readToken(comma) || - (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && - comma.type_ != tokenComment)) { + if (!readTokenSkippingComments(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) { return addErrorAndRecover("Missing ',' or '}' in object declaration", comma, tokenObjectEnd); } - bool finalizeTokenOk = true; - while (comma.type_ == tokenComment && finalizeTokenOk) - finalizeTokenOk = readToken(comma); if (comma.type_ == tokenObjectEnd) return true; } @@ -1533,10 +1512,7 @@ bool OurReader::readArray(Token& token) { Token currentToken; // Accept Comment after last item in the array. - ok = readToken(currentToken); - while (currentToken.type_ == tokenComment && ok) { - ok = readToken(currentToken); - } + ok = readTokenSkippingComments(currentToken); bool badTokenType = (currentToken.type_ != tokenArraySeparator && currentToken.type_ != tokenArrayEnd); if (!ok || badTokenType) { diff --git a/test/data/fail_strict_comment_01.json b/test/data/fail_strict_comment_01.json new file mode 100644 index 0000000..b7e0a5e --- /dev/null +++ b/test/data/fail_strict_comment_01.json @@ -0,0 +1,4 @@ +{ + "a": "aaa", + "b": "bbb" // comments not allowed in strict mode +} diff --git a/test/data/fail_strict_comment_02.json b/test/data/fail_strict_comment_02.json new file mode 100644 index 0000000..699a7f7 --- /dev/null +++ b/test/data/fail_strict_comment_02.json @@ -0,0 +1,4 @@ +{ + "a": "aaa", // comments not allowed in strict mode + "b": "bbb" +} diff --git a/test/data/fail_strict_comment_03.json b/test/data/fail_strict_comment_03.json new file mode 100644 index 0000000..5f0fabf --- /dev/null +++ b/test/data/fail_strict_comment_03.json @@ -0,0 +1,3 @@ +{ + "array" : [1, 2, 3 /* comments not allowed in strict mode */] +} diff --git a/test/data/fail_test_object_02.json b/test/data/fail_test_object_02.json new file mode 100644 index 0000000..afe62c5 --- /dev/null +++ b/test/data/fail_test_object_02.json @@ -0,0 +1 @@ +{"one": 1 /* } */ { "two" : 2 } diff --git a/test/runjsontests.py b/test/runjsontests.py index 5496e2c..49cc7a9 100644 --- a/test/runjsontests.py +++ b/test/runjsontests.py @@ -97,14 +97,17 @@ def runAllTests(jsontest_executable_path, input_dir = None, valgrind_path = use_valgrind and VALGRIND_CMD or '' for input_path in tests + test_jsonchecker: expect_failure = os.path.basename(input_path).startswith('fail') - is_json_checker_test = (input_path in test_jsonchecker) or expect_failure + is_json_checker_test = input_path in test_jsonchecker + is_parse_only = is_json_checker_test or expect_failure + is_strict_test = ('_strict_' in os.path.basename(input_path)) or is_json_checker_test print('TESTING:', input_path, end=' ') - options = is_json_checker_test and '--json-checker' or '' + options = is_parse_only and '--parse-only' or '' + options += is_strict_test and ' --strict' or '' options += ' --json-writer %s'%writerClass cmd = '%s%s %s "%s"' % ( valgrind_path, jsontest_executable_path, options, input_path) status, process_output = getStatusOutput(cmd) - if is_json_checker_test: + if is_parse_only: if expect_failure: if not status: print('FAILED') From 3c2205cd97838d3c4143107992967e23f0c958b8 Mon Sep 17 00:00:00 2001 From: vslashg Date: Mon, 9 Sep 2024 20:32:17 -0400 Subject: [PATCH 02/32] Fix out-of-bounds read. (#1503) getLocationLIneAndColumn would read past the end of the provided buffer if generating an error message at the end of the stream, if the final character was `\r`. Co-authored-by: Jordan Bayles --- src/lib_json/json_reader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index b12c6b8..86ed030 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -761,7 +761,7 @@ void Reader::getLocationLineAndColumn(Location location, int& line, while (current < location && current != end_) { Char c = *current++; if (c == '\r') { - if (*current == '\n') + if (current != end_ && *current == '\n') ++current; lastLineStart = current; ++line; @@ -1801,7 +1801,7 @@ void OurReader::getLocationLineAndColumn(Location location, int& line, while (current < location && current != end_) { Char c = *current++; if (c == '\r') { - if (*current == '\n') + if (current != end_ && *current == '\n') ++current; lastLineStart = current; ++line; From e1a3c64fef7351b49ad612c8b355a42666a7ff44 Mon Sep 17 00:00:00 2001 From: vslashg Date: Mon, 9 Sep 2024 20:34:55 -0400 Subject: [PATCH 03/32] Fix asserts in Value::setComment (#1445) The existing asserts seem to not be what was intended; they appear to have been mistranslated in pull/877. The first assert for `comment.empty()` was previously a check that a provided `const char*` parameter was not null. The function this replaced accepted empty strings, and the if() statement at the start of this function handles them. The second assert for `comment[0] == '\0'` was written when `comment` was a `const char*`, and was testing for empty c-string input. This PR replaces it with `comment.empty()` to match the original intent. Co-authored-by: Jordan Bayles --- src/lib_json/json_value.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib_json/json_value.cpp b/src/lib_json/json_value.cpp index aa2b744..26cd843 100644 --- a/src/lib_json/json_value.cpp +++ b/src/lib_json/json_value.cpp @@ -1410,9 +1410,8 @@ void Value::setComment(String comment, CommentPlacement placement) { // Always discard trailing newline, to aid indentation. comment.pop_back(); } - JSON_ASSERT(!comment.empty()); JSON_ASSERT_MESSAGE( - comment[0] == '\0' || comment[0] == '/', + comment.empty() || comment[0] == '/', "in Json::Value::setComment(): Comments must start with /"); comments_.set(placement, std::move(comment)); } From 034976a19dbd7ffe37aa1c3821855d4cde868fcb Mon Sep 17 00:00:00 2001 From: Philip Top Date: Mon, 9 Sep 2024 17:38:22 -0700 Subject: [PATCH 04/32] add a valueToQuotedString overload (#1397) * add a valueToQuotedString overload to take a string length to support things like a string_view more directly. * Apply suggestions from code review Co-authored-by: Billy Donahue --------- Co-authored-by: Billy Donahue Co-authored-by: Jordan Bayles --- include/json/writer.h | 1 + src/lib_json/json_writer.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/json/writer.h b/include/json/writer.h index 655ebff..7c56a21 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -351,6 +351,7 @@ String JSON_API valueToString( PrecisionType precisionType = PrecisionType::significantDigits); String JSON_API valueToString(bool value); String JSON_API valueToQuotedString(const char* value); +String JSON_API valueToQuotedString(const char* value, size_t length); /// \brief Output using the StyledStreamWriter. /// \see Json::operator>>() diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 239c429..5bb5dd1 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -354,6 +354,10 @@ String valueToQuotedString(const char* value) { return valueToQuotedStringN(value, strlen(value)); } +String valueToQuotedString(const char* value, size_t length) { + return valueToQuotedStringN(value, length); +} + // Class Writer // ////////////////////////////////////////////////////////////////// Writer::~Writer() = default; @@ -491,7 +495,7 @@ void StyledWriter::writeValue(const Value& value) { const String& name = *it; const Value& childValue = value[name]; writeCommentBeforeValue(childValue); - writeWithIndent(valueToQuotedString(name.c_str())); + writeWithIndent(valueToQuotedString(name.c_str(), name.size())); document_ += " : "; writeValue(childValue); if (++it == members.end()) { @@ -709,7 +713,7 @@ void StyledStreamWriter::writeValue(const Value& value) { const String& name = *it; const Value& childValue = value[name]; writeCommentBeforeValue(childValue); - writeWithIndent(valueToQuotedString(name.c_str())); + writeWithIndent(valueToQuotedString(name.c_str(), name.size())); *document_ << " : "; writeValue(childValue); if (++it == members.end()) { From 78893d396180bc558dc3b204fae929f0714748a6 Mon Sep 17 00:00:00 2001 From: Jordan Bayles Date: Mon, 9 Sep 2024 18:19:48 -0700 Subject: [PATCH 05/32] Update clang-format.yml --- .github/workflows/clang-format.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 221f8b8..497b9d6 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -1,5 +1,5 @@ name: clang-format check -on: [check_run, pull_request, push] +on: [check_run, push] jobs: formatting-check: From 57de64bf69f3e8fc715bfeb4dbe0bca27b6c4862 Mon Sep 17 00:00:00 2001 From: Jordan Bayles Date: Mon, 9 Sep 2024 18:29:28 -0700 Subject: [PATCH 06/32] Add code coverage (#1561) * Add code coverage * Update meson.yml * Update meson.yml * Update meson.yml * Update meson.yml * Update meson.yml * Update meson.yml * Update meson.yml --- .github/workflows/meson.yml | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/.github/workflows/meson.yml b/.github/workflows/meson.yml index 8314dbc..1899470 100644 --- a/.github/workflows/meson.yml +++ b/.github/workflows/meson.yml @@ -1,6 +1,6 @@ name: meson build and test run-name: update pushed to ${{ github.ref }} -on: [check_run, pull_request, push] +on: [check_run, push] jobs: publish: @@ -31,3 +31,35 @@ jobs: meson-version: 1.5.1 ninja-version: 1.11.1.1 action: test + + coverage: + runs-on: ubuntu-latest + + steps: + - name: checkout repository + uses: actions/checkout@v4 + + - name: setup python + uses: actions/setup-python@v5 + + - name: meson build + uses: BSFishy/meson-build@v1.0.3 + with: + meson-version: 1.5.1 + ninja-version: 1.11.1.1 + setup-options: -Db_coverage=true + action: build + + - name: meson test + uses: BSFishy/meson-build@v1.0.3 + with: + meson-version: 1.5.1 + ninja-version: 1.11.1.1 + setup-options: -Db_coverage=true + action: test + + - name: generate code coverage report + uses: threeal/gcovr-action@v1.0.0 + with: + coveralls-send: true + github-token: ${{ secrets.GITHUB_TOKEN }} From caf5fb0742e99c35abdfb127dde749138adc2715 Mon Sep 17 00:00:00 2001 From: Jordan Bayles Date: Mon, 9 Sep 2024 18:35:36 -0700 Subject: [PATCH 07/32] Update meson.yml (#1562) --- .github/workflows/meson.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/meson.yml b/.github/workflows/meson.yml index 1899470..48d496c 100644 --- a/.github/workflows/meson.yml +++ b/.github/workflows/meson.yml @@ -1,6 +1,6 @@ name: meson build and test run-name: update pushed to ${{ github.ref }} -on: [check_run, push] +on: [check_run, push, pull_request] jobs: publish: From badbbc7185cde8270e6005bcdb6686363fe00557 Mon Sep 17 00:00:00 2001 From: Jordan Bayles Date: Mon, 9 Sep 2024 18:35:49 -0700 Subject: [PATCH 08/32] Update clang-format.yml --- .github/workflows/clang-format.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 497b9d6..221f8b8 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -1,5 +1,5 @@ name: clang-format check -on: [check_run, push] +on: [check_run, pull_request, push] jobs: formatting-check: From fd1abe4cca7f496438f92d797371dabe14e49373 Mon Sep 17 00:00:00 2001 From: Andrea Pappacoda Date: Tue, 10 Sep 2024 03:42:23 +0200 Subject: [PATCH 09/32] build(meson): use find_program('python3') (#1386) If you really want to be sure to always find python3 when running Meson (and not some other implementation like [Muon](https://muon.build)) it is a bit better to use `find_program('python3')`, as described in https://mesonbuild.com/Reference-manual_functions.html#find_program : "if the "python3" program is requested and it is not found in the system, Meson will return its current interpreter Co-authored-by: Jordan Bayles --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index f68db30..fb2b47c 100644 --- a/meson.build +++ b/meson.build @@ -73,7 +73,7 @@ if meson.is_subproject() or not get_option('tests') subdir_done() endif -python = import('python').find_installation() +python = find_program('python3') jsoncpp_test = executable( 'jsoncpp_test', files([ From d39b0dff0c5673ed6b21a7808773cd45b661aae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20R=C3=B6hling?= Date: Tue, 10 Sep 2024 03:42:54 +0200 Subject: [PATCH 10/32] Bump CMake policy version to avoid deprecation warning (#1499) Starting with CMake 3.27, there will be a warning for compat levels below CMake 3.5. Co-authored-by: Jordan Bayles --- jsoncppConfig.cmake.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsoncppConfig.cmake.in b/jsoncppConfig.cmake.in index 76570bc..fdd9fea 100644 --- a/jsoncppConfig.cmake.in +++ b/jsoncppConfig.cmake.in @@ -1,5 +1,5 @@ cmake_policy(PUSH) -cmake_policy(VERSION 3.0) +cmake_policy(VERSION 3.0...3.26) @PACKAGE_INIT@ From c857395951c1d5e80b0ac62f760d698f03b209f2 Mon Sep 17 00:00:00 2001 From: NotWearingPants <26556598+NotWearingPants@users.noreply.github.com> Date: Tue, 10 Sep 2024 04:43:32 +0300 Subject: [PATCH 11/32] Update link in amalgamate.py (#1335) --- amalgamate.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/amalgamate.py b/amalgamate.py index 4a328ab..1d1e488 100755 --- a/amalgamate.py +++ b/amalgamate.py @@ -63,7 +63,7 @@ def amalgamate_source(source_top_dir=None, """ print("Amalgamating header...") header = AmalgamationFile(source_top_dir) - header.add_text("/// Json-cpp amalgamated header (http://jsoncpp.sourceforge.net/).") + header.add_text("/// Json-cpp amalgamated header (https://github.com/open-source-parsers/jsoncpp/).") header.add_text('/// It is intended to be used with #include "%s"' % header_include_path) header.add_file("LICENSE", wrap_in_comment=True) header.add_text("#ifndef JSON_AMALGAMATED_H_INCLUDED") @@ -90,7 +90,7 @@ def amalgamate_source(source_top_dir=None, forward_header_include_path = base + "-forwards" + ext print("Amalgamating forward header...") header = AmalgamationFile(source_top_dir) - header.add_text("/// Json-cpp amalgamated forward header (http://jsoncpp.sourceforge.net/).") + header.add_text("/// Json-cpp amalgamated forward header (https://github.com/open-source-parsers/jsoncpp/).") header.add_text('/// It is intended to be used with #include "%s"' % forward_header_include_path) header.add_text("/// This header provides forward declaration for all JsonCpp types.") header.add_file("LICENSE", wrap_in_comment=True) @@ -112,7 +112,7 @@ def amalgamate_source(source_top_dir=None, print("Amalgamating source...") source = AmalgamationFile(source_top_dir) - source.add_text("/// Json-cpp amalgamated source (http://jsoncpp.sourceforge.net/).") + source.add_text("/// Json-cpp amalgamated source (https://github.com/open-source-parsers/jsoncpp/).") source.add_text('/// It is intended to be used with #include "%s"' % header_include_path) source.add_file("LICENSE", wrap_in_comment=True) source.add_text("") From c04c0c2131e2c0e90eb731b58877eef39bcf8e47 Mon Sep 17 00:00:00 2001 From: martinduffy1 <106178409+martinduffy1@users.noreply.github.com> Date: Mon, 9 Sep 2024 21:48:54 -0400 Subject: [PATCH 12/32] CharReader: Add StructuredError (#1409) * CharReader: Add Structured Error Add getStructuredError to CharReader * run clang format --------- Co-authored-by: Jordan Bayles Co-authored-by: Jordan Bayles --- include/json/reader.h | 27 +++++++++++++++- src/lib_json/json_reader.cpp | 60 ++++++++++++++++++++++++------------ src/test_lib_json/main.cpp | 30 ++++++++++++++++++ 3 files changed, 97 insertions(+), 20 deletions(-) diff --git a/include/json/reader.h b/include/json/reader.h index 85539d1..38b9360 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -244,6 +244,12 @@ private: */ class JSON_API CharReader { public: + struct JSON_API StructuredError { + ptrdiff_t offset_start; + ptrdiff_t offset_limit; + String message; + }; + virtual ~CharReader() = default; /** \brief Read a Value from a JSON * document. The document must be a UTF-8 encoded string containing the @@ -262,7 +268,12 @@ public: * error occurred. */ virtual bool parse(char const* beginDoc, char const* endDoc, Value* root, - String* errs) = 0; + String* errs); + + /** \brief Returns a vector of structured errors encountered while parsing. + * Each parse call resets the stored list of errors. + */ + std::vector getStructuredErrors() const; class JSON_API Factory { public: @@ -272,6 +283,20 @@ public: */ virtual CharReader* newCharReader() const = 0; }; // Factory + +protected: + class Impl { + public: + virtual ~Impl() = default; + virtual bool parse(char const* beginDoc, char const* endDoc, Value* root, + String* errs) = 0; + virtual std::vector getStructuredErrors() const = 0; + }; + + explicit CharReader(std::unique_ptr impl) : _impl(std::move(impl)) {} + +private: + std::unique_ptr _impl; }; // CharReader /** \brief Build a CharReader implementation. diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index 86ed030..4ab4dff 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -878,17 +878,12 @@ class OurReader { public: using Char = char; using Location = const Char*; - struct StructuredError { - ptrdiff_t offset_start; - ptrdiff_t offset_limit; - String message; - }; explicit OurReader(OurFeatures const& features); bool parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments = true); String getFormattedErrorMessages() const; - std::vector getStructuredErrors() const; + std::vector getStructuredErrors() const; private: OurReader(OurReader const&); // no impl @@ -1836,10 +1831,11 @@ String OurReader::getFormattedErrorMessages() const { return formattedMessage; } -std::vector OurReader::getStructuredErrors() const { - std::vector allErrors; +std::vector +OurReader::getStructuredErrors() const { + std::vector allErrors; for (const auto& error : errors_) { - OurReader::StructuredError structured; + CharReader::StructuredError structured; structured.offset_start = error.token_.start_ - begin_; structured.offset_limit = error.token_.end_ - begin_; structured.message = error.message_; @@ -1849,20 +1845,36 @@ std::vector OurReader::getStructuredErrors() const { } class OurCharReader : public CharReader { - bool const collectComments_; - OurReader reader_; public: OurCharReader(bool collectComments, OurFeatures const& features) - : collectComments_(collectComments), reader_(features) {} - bool parse(char const* beginDoc, char const* endDoc, Value* root, - String* errs) override { - bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); - if (errs) { - *errs = reader_.getFormattedErrorMessages(); + : CharReader( + std::unique_ptr(new OurImpl(collectComments, features))) {} + +protected: + class OurImpl : public Impl { + public: + OurImpl(bool collectComments, OurFeatures const& features) + : collectComments_(collectComments), reader_(features) {} + + bool parse(char const* beginDoc, char const* endDoc, Value* root, + String* errs) override { + bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); + if (errs) { + *errs = reader_.getFormattedErrorMessages(); + } + return ok; } - return ok; - } + + std::vector + getStructuredErrors() const override { + return reader_.getStructuredErrors(); + } + + private: + bool const collectComments_; + OurReader reader_; + }; }; CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); } @@ -1952,6 +1964,16 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) { //! [CharReaderBuilderDefaults] } +std::vector +CharReader::getStructuredErrors() const { + return _impl->getStructuredErrors(); +} + +bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root, + String* errs) { + return _impl->parse(beginDoc, endDoc, root, errs); +} + ////////////////////////////////// // global functions diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp index 1ef33bb..fa41d19 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -3917,6 +3917,36 @@ JSONTEST_FIXTURE_LOCAL(FuzzTest, fuzzDoesntCrash) { example.size())); } +struct ParseWithStructuredErrorsTest : JsonTest::TestCase { + void testErrors( + const std::string& doc, bool success, + const std::vector& expectedErrors) { + Json::CharReaderBuilder b; + CharReaderPtr reader(b.newCharReader()); + Json::Value root; + JSONTEST_ASSERT_EQUAL( + reader->parse(doc.data(), doc.data() + doc.length(), &root, nullptr), + success); + auto actualErrors = reader->getStructuredErrors(); + JSONTEST_ASSERT_EQUAL(expectedErrors.size(), actualErrors.size()); + for (std::size_t i = 0; i < actualErrors.size(); i++) { + const auto& a = actualErrors[i]; + const auto& e = expectedErrors[i]; + JSONTEST_ASSERT_EQUAL(a.offset_start, e.offset_start); + JSONTEST_ASSERT_EQUAL(a.offset_limit, e.offset_limit); + JSONTEST_ASSERT_STRING_EQUAL(a.message, e.message); + } + } +}; + +JSONTEST_FIXTURE_LOCAL(ParseWithStructuredErrorsTest, success) { + testErrors("{}", true, {}); +} + +JSONTEST_FIXTURE_LOCAL(ParseWithStructuredErrorsTest, singleError) { + testErrors("{ 1 : 2 }", false, {{2, 3, "Missing '}' or object member name"}}); +} + int main(int argc, const char* argv[]) { JsonTest::Runner runner; From 483f1c310e36db6d53868f9a4f8b9c0f90faba5b Mon Sep 17 00:00:00 2001 From: Pavel Tsynk <36221942+TsynkPavel@users.noreply.github.com> Date: Tue, 10 Sep 2024 08:50:38 +0700 Subject: [PATCH 13/32] Fix compile on windows with clang (#1480) Co-authored-by: Jordan Bayles --- src/lib_json/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib_json/CMakeLists.txt b/src/lib_json/CMakeLists.txt index 3cf66eb..ce8100b 100644 --- a/src/lib_json/CMakeLists.txt +++ b/src/lib_json/CMakeLists.txt @@ -144,7 +144,7 @@ if(BUILD_STATIC_LIBS) # avoid name clashes on windows as the shared import lib is also named jsoncpp.lib if(NOT DEFINED STATIC_SUFFIX AND BUILD_SHARED_LIBS) - if (MSVC) + if (WIN32) set(STATIC_SUFFIX "_static") else() set(STATIC_SUFFIX "") From 31754ce2e25056c0bbf6599c49059d2778e9109c Mon Sep 17 00:00:00 2001 From: Pavel Tsynk <36221942+TsynkPavel@users.noreply.github.com> Date: Tue, 10 Sep 2024 08:51:11 +0700 Subject: [PATCH 14/32] Fixed setting JSONCPP_USE_SECURE_MEMORY definition (#1479) * Fixed setting JSONCPP_USE_SECURE_MEMORY definition * fix indent * Fix passing from command line * simplified definition --------- Co-authored-by: Jordan Bayles --- CMakeLists.txt | 4 +++- include/json/version.h | 5 ++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8920544..ac0a8ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,7 +103,9 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" CACHE PATH "Executable/dll output dir.") endif() -set(JSONCPP_USE_SECURE_MEMORY "0" CACHE STRING "-D...=1 to use memory-wiping allocator for STL") +if(JSONCPP_USE_SECURE_MEMORY) + add_definitions("-DJSONCPP_USE_SECURE_MEMORY=1") +endif() configure_file("${PROJECT_SOURCE_DIR}/version.in" "${PROJECT_BINARY_DIR}/version" diff --git a/include/json/version.h b/include/json/version.h index e931d03..9e95411 100644 --- a/include/json/version.h +++ b/include/json/version.h @@ -18,10 +18,9 @@ ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \ (JSONCPP_VERSION_PATCH << 8)) -#ifdef JSONCPP_USING_SECURE_MEMORY -#undef JSONCPP_USING_SECURE_MEMORY -#endif +#if !defined(JSONCPP_USE_SECURE_MEMORY) #define JSONCPP_USING_SECURE_MEMORY 0 +#endif // If non-zero, the library zeroes any memory that it has allocated before // it frees its memory. From 742c645ab31088f95edd112ffc65eca839cdf8ff Mon Sep 17 00:00:00 2001 From: Kapandaria Date: Tue, 10 Sep 2024 04:51:35 +0300 Subject: [PATCH 15/32] Update readFromString.cpp (#1477) Print the error to screen Co-authored-by: Jordan Bayles --- example/readFromString/readFromString.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/readFromString/readFromString.cpp b/example/readFromString/readFromString.cpp index 0b29a4e..878f9eb 100644 --- a/example/readFromString/readFromString.cpp +++ b/example/readFromString/readFromString.cpp @@ -25,7 +25,7 @@ int main() { const std::unique_ptr reader(builder.newCharReader()); if (!reader->parse(rawJson.c_str(), rawJson.c_str() + rawJsonLength, &root, &err)) { - std::cout << "error" << std::endl; + std::cout << "error: " << err << std::endl; return EXIT_FAILURE; } } From 62f7f3efe650dde5f6075a8dcecbfe358ba530e8 Mon Sep 17 00:00:00 2001 From: Pedro Kaj Kjellerup Nacht Date: Mon, 9 Sep 2024 22:53:23 -0300 Subject: [PATCH 16/32] Add security policy (#1484) Signed-off-by: Pedro Kaj Kjellerup Nacht --- SECURITY.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..67af883 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,17 @@ +# Security Policy + +If you have discovered a security vulnerability in this project, please report it +privately. **Do not disclose it as a public issue.** This gives us time to work with you +to fix the issue before public exposure, reducing the chance that the exploit will be +used before a patch is released. + +Please submit the report by filling out +[this form](https://github.com/open-source-parsers/jsoncpp/security/advisories/new). + +Please provide the following information in your report: + +- A description of the vulnerability and its impact +- How to reproduce the issue + +This project is maintained by volunteers on a reasonable-effort basis. As such, +we ask that you give us 90 days to work on a fix before public exposure. From a4a083c30751a178b7cdfe1bee685d0ec1c2ed28 Mon Sep 17 00:00:00 2001 From: SpaceIm <30052553+SpaceIm@users.noreply.github.com> Date: Tue, 10 Sep 2024 03:57:51 +0200 Subject: [PATCH 17/32] remove ccache micro management (#1448) Co-authored-by: Jordan Bayles --- CMakeLists.txt | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ac0a8ed..a37e726 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,16 +54,6 @@ endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") -# --------------------------------------------------------------------------- -# use ccache if found, has to be done before project() -# --------------------------------------------------------------------------- -find_program(CCACHE_EXECUTABLE "ccache" HINTS /usr/local/bin /opt/local/bin) -if(CCACHE_EXECUTABLE) - message(STATUS "use ccache") - set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_EXECUTABLE}" CACHE PATH "ccache" FORCE) - set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_EXECUTABLE}" CACHE PATH "ccache" FORCE) -endif() - project(jsoncpp # Note: version must be updated in three places when doing a release. This # annoying process ensures that amalgamate, CMake, and meson all report the From d791737ccd74beb4c6bf3f0fa499db74c2791192 Mon Sep 17 00:00:00 2001 From: Jordan Bayles Date: Mon, 9 Sep 2024 19:05:11 -0700 Subject: [PATCH 18/32] Create cmake.yml (#1563) * Create cmake.yml * Update cmake.yml * Update cmake.yml --- .github/workflows/cmake.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/cmake.yml diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml new file mode 100644 index 0000000..675493b --- /dev/null +++ b/.github/workflows/cmake.yml @@ -0,0 +1,18 @@ +name: cmake +on: [push] +jobs: + publish: + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + + steps: + - name: checkout project + uses: actions/checkout@v4 + + - name: build project + uses: threeal/cmake-action@v2.0.0 + From d13801e83239924c281119316aefb8f611d9bfd0 Mon Sep 17 00:00:00 2001 From: Jordan Bayles Date: Mon, 9 Sep 2024 19:06:30 -0700 Subject: [PATCH 19/32] Update meson.yml (#1564) --- .github/workflows/meson.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/meson.yml b/.github/workflows/meson.yml index 48d496c..22fe32f 100644 --- a/.github/workflows/meson.yml +++ b/.github/workflows/meson.yml @@ -3,7 +3,7 @@ run-name: update pushed to ${{ github.ref }} on: [check_run, push, pull_request] jobs: - publish: + meson-publish: runs-on: ${{ matrix.os }} strategy: @@ -32,7 +32,7 @@ jobs: ninja-version: 1.11.1.1 action: test - coverage: + meson-coverage: runs-on: ubuntu-latest steps: From 8d1ea7054f4a0984fc84e406f9253b9cafacd64a Mon Sep 17 00:00:00 2001 From: Jordan Bayles Date: Mon, 9 Sep 2024 19:07:04 -0700 Subject: [PATCH 20/32] Update cmake.yml --- .github/workflows/cmake.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 675493b..91f387a 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -1,7 +1,7 @@ name: cmake -on: [push] +on: [check_run, push, pull_request] jobs: - publish: + cmake-publish: runs-on: ${{ matrix.os }} strategy: From fdb529bd0613308591352ac1d4e765c4d33710c9 Mon Sep 17 00:00:00 2001 From: jedav Date: Mon, 9 Sep 2024 19:53:56 -0700 Subject: [PATCH 21/32] Move removeIndex's result instead of copying (#1516) Currently removeIndex copies the removed value into removed and then destructs the original, which can cause significant performance overhead. Co-authored-by: Jordan Bayles --- src/lib_json/json_value.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib_json/json_value.cpp b/src/lib_json/json_value.cpp index 26cd843..a5e2dd8 100644 --- a/src/lib_json/json_value.cpp +++ b/src/lib_json/json_value.cpp @@ -1205,7 +1205,7 @@ bool Value::removeIndex(ArrayIndex index, Value* removed) { return false; } if (removed) - *removed = it->second; + *removed = std::move(it->second); ArrayIndex oldSize = size(); // shift left all items left, into the place of the "removed" for (ArrayIndex i = index; i < (oldSize - 1); ++i) { From 2072e2b4e394f90ca9cbea32db53231900c01618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Tue, 10 Sep 2024 04:56:37 +0200 Subject: [PATCH 22/32] Use current source / binary dir when assuring out of source builds (#1527) Co-authored-by: Jordan Bayles --- include/PreventInSourceBuilds.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/PreventInSourceBuilds.cmake b/include/PreventInSourceBuilds.cmake index 7ddda54..be5d0dd 100644 --- a/include/PreventInSourceBuilds.cmake +++ b/include/PreventInSourceBuilds.cmake @@ -2,8 +2,8 @@ # This function will prevent in-source builds function(AssureOutOfSourceBuilds) # make sure the user doesn't play dirty with symlinks - get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH) - get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH) + get_filename_component(srcdir "${CMAKE_CURRENT_SOURCE_DIR}" REALPATH) + get_filename_component(bindir "${CMAKE_CURRENT_BINARY_DIR}" REALPATH) # disallow in-source builds if("${srcdir}" STREQUAL "${bindir}") From 48d2e106a71b91a1259127ae0ca4d759e11ea890 Mon Sep 17 00:00:00 2001 From: Bartosz Brachaczek Date: Tue, 10 Sep 2024 05:00:06 +0200 Subject: [PATCH 23/32] Opportunistically take advantage of C++20 move-in/out-of stringstream (#1457) * Opportunistically take advantage of C++20 move-out-of stringstream * Opportunistically take advantage of C++20 move-in/out-of stringstream --------- Co-authored-by: Jordan Bayles --- src/lib_json/json_reader.cpp | 8 +++----- src/lib_json/json_writer.cpp | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index 4ab4dff..8ef29f0 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -587,8 +587,7 @@ bool Reader::decodeDouble(Token& token) { bool Reader::decodeDouble(Token& token, Value& decoded) { double value = 0; - String buffer(token.start_, token.end_); - IStringStream is(buffer); + IStringStream is(String(token.start_, token.end_)); if (!(is >> value)) { if (value == std::numeric_limits::max()) value = std::numeric_limits::infinity(); @@ -1622,8 +1621,7 @@ bool OurReader::decodeDouble(Token& token) { bool OurReader::decodeDouble(Token& token, Value& decoded) { double value = 0; - const String buffer(token.start_, token.end_); - IStringStream is(buffer); + IStringStream is(String(token.start_, token.end_)); if (!(is >> value)) { if (value == std::numeric_limits::max()) value = std::numeric_limits::infinity(); @@ -1981,7 +1979,7 @@ bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root, String* errs) { OStringStream ssin; ssin << sin.rdbuf(); - String doc = ssin.str(); + String doc = std::move(ssin).str(); char const* begin = doc.data(); char const* end = begin + doc.size(); // Note that we do not actually need a null-terminator. diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 5bb5dd1..ee45c43 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -1251,7 +1251,7 @@ String writeString(StreamWriter::Factory const& factory, Value const& root) { OStringStream sout; StreamWriterPtr const writer(factory.newStreamWriter()); writer->write(root, &sout); - return sout.str(); + return std::move(sout).str(); } OStream& operator<<(OStream& sout, Value const& root) { From fa0dff18fdef3dc7cd0ab0376a2aa19f878dbbc2 Mon Sep 17 00:00:00 2001 From: Roelof Oomen Date: Tue, 10 Sep 2024 05:02:24 +0200 Subject: [PATCH 24/32] Protect target JsonCpp::JsonCpp against multi-include (#1435) * Protect target JsonCpp::JsonCpp against multi-include Fixes #1356 * Simplify (@BillyDonahue) --------- Co-authored-by: Jordan Bayles --- jsoncpp-namespaced-targets.cmake | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/jsoncpp-namespaced-targets.cmake b/jsoncpp-namespaced-targets.cmake index ac1504e..70a79ee 100644 --- a/jsoncpp-namespaced-targets.cmake +++ b/jsoncpp-namespaced-targets.cmake @@ -1,7 +1,9 @@ -if (TARGET jsoncpp_static) - add_library(JsonCpp::JsonCpp INTERFACE IMPORTED) - set_target_properties(JsonCpp::JsonCpp PROPERTIES INTERFACE_LINK_LIBRARIES "jsoncpp_static") -elseif (TARGET jsoncpp_lib) - add_library(JsonCpp::JsonCpp INTERFACE IMPORTED) - set_target_properties(JsonCpp::JsonCpp PROPERTIES INTERFACE_LINK_LIBRARIES "jsoncpp_lib") -endif () \ No newline at end of file +if (NOT TARGET JsonCpp::JsonCpp) + if (TARGET jsoncpp_static) + add_library(JsonCpp::JsonCpp INTERFACE IMPORTED) + set_target_properties(JsonCpp::JsonCpp PROPERTIES INTERFACE_LINK_LIBRARIES "jsoncpp_static") + elseif (TARGET jsoncpp_lib) + add_library(JsonCpp::JsonCpp INTERFACE IMPORTED) + set_target_properties(JsonCpp::JsonCpp PROPERTIES INTERFACE_LINK_LIBRARIES "jsoncpp_lib") + endif () +endif () From f4590227862e62264b98b88fd8c82558002f9df7 Mon Sep 17 00:00:00 2001 From: matthieugleg <156894466+matthieugleg@users.noreply.github.com> Date: Tue, 10 Sep 2024 05:06:22 +0200 Subject: [PATCH 25/32] Update CMakeLists.txt (#1528) Remove build directory from include Co-authored-by: Jordan Bayles --- src/lib_json/CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lib_json/CMakeLists.txt b/src/lib_json/CMakeLists.txt index ce8100b..1526353 100644 --- a/src/lib_json/CMakeLists.txt +++ b/src/lib_json/CMakeLists.txt @@ -132,7 +132,6 @@ if(BUILD_SHARED_LIBS) target_include_directories(${SHARED_LIB} PUBLIC $ $ - $ ) list(APPEND CMAKE_TARGETS ${SHARED_LIB}) @@ -166,7 +165,6 @@ if(BUILD_STATIC_LIBS) target_include_directories(${STATIC_LIB} PUBLIC $ $ - $ ) list(APPEND CMAKE_TARGETS ${STATIC_LIB}) @@ -193,7 +191,6 @@ if(BUILD_OBJECT_LIBS) target_include_directories(${OBJECT_LIB} PUBLIC $ $ - $ ) list(APPEND CMAKE_TARGETS ${OBJECT_LIB}) From 4b1bd4405e0d5ebfcc6252f0246e28d56290611e Mon Sep 17 00:00:00 2001 From: Woodrow Douglass Date: Mon, 9 Sep 2024 23:08:12 -0400 Subject: [PATCH 26/32] Create a jsoncppConfig.cmake file, even if building under meson (#1486) * Create a jsoncppConfig.cmake file, even if building under meson * Hardcode many fewer things in the meson-generated cmake files * use join_paths for constructing paths in the output Config.cmake --------- Co-authored-by: Jordan Bayles --- jsoncppConfig.cmake.meson.in | 8 ++++++++ meson.build | 39 +++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 jsoncppConfig.cmake.meson.in diff --git a/jsoncppConfig.cmake.meson.in b/jsoncppConfig.cmake.meson.in new file mode 100644 index 0000000..0f4866d --- /dev/null +++ b/jsoncppConfig.cmake.meson.in @@ -0,0 +1,8 @@ +@PACKAGE_INIT@ + +@MESON_SHARED_TARGET@ +@MESON_STATIC_TARGET@ + +include ( "${CMAKE_CURRENT_LIST_DIR}/jsoncpp-namespaced-targets.cmake" ) + +check_required_components(JsonCpp) diff --git a/meson.build b/meson.build index fb2b47c..8070361 100644 --- a/meson.build +++ b/meson.build @@ -15,7 +15,7 @@ project( 'cpp_std=c++11', 'warning_level=1'], license : 'Public Domain', - meson_version : '>= 0.49.0') + meson_version : '>= 0.54.0') jsoncpp_headers = files([ @@ -62,6 +62,43 @@ import('pkgconfig').generate( filebase : 'jsoncpp', description : 'A C++ library for interacting with JSON') +cmakeconf = configuration_data() +cmakeconf.set('MESON_LIB_DIR', get_option('libdir')) +cmakeconf.set('MESON_INCLUDE_DIR', get_option('includedir')) + +fs = import('fs') +if get_option('default_library') == 'shared' + shared_name = fs.name(jsoncpp_lib.full_path()) +endif +if get_option('default_library') == 'static' + static_name = fs.name(jsoncpp_lib.full_path()) +endif +if get_option('default_library') == 'both' + shared_name = fs.name(jsoncpp_lib.get_shared_lib().full_path()) + static_name = fs.name(jsoncpp_lib.get_static_lib().full_path()) +endif + +if get_option('default_library') == 'shared' or get_option('default_library') == 'both' + cmakeconf.set('MESON_SHARED_TARGET', ''' +add_library(jsoncpp_lib IMPORTED SHARED) +set_target_properties(jsoncpp_lib PROPERTIES + IMPORTED_LOCATION "''' + join_paths('${PACKAGE_PREFIX_DIR}', get_option('libdir'), shared_name) + '''" + INTERFACE_INCLUDE_DIRECTORIES "''' + join_paths('${PACKAGE_PREFIX_DIR}', get_option('includedir')) + '")') +endif +if get_option('default_library') == 'static' or get_option('default_library') == 'both' + cmakeconf.set('MESON_STATIC_TARGET', ''' +add_library(jsoncpp_static IMPORTED STATIC) +set_target_properties(jsoncpp_static PROPERTIES + IMPORTED_LOCATION "''' + join_paths('${PACKAGE_PREFIX_DIR}', get_option('libdir'), static_name) + '''" + INTERFACE_INCLUDE_DIRECTORIES "''' + join_paths('${PACKAGE_PREFIX_DIR}', get_option('includedir')) + '")') +endif + +import('cmake').configure_package_config_file( + name: 'jsoncpp', + input: 'jsoncppConfig.cmake.meson.in', + configuration: cmakeconf) +install_data('jsoncpp-namespaced-targets.cmake', install_dir : join_paths(get_option('libdir'), 'cmake', jsoncpp_lib.name())) + # for libraries bundling jsoncpp jsoncpp_dep = declare_dependency( include_directories : jsoncpp_include_directories, From 162ead383d1fc920a01b2f333e6e69fa089c2541 Mon Sep 17 00:00:00 2001 From: Kerem TAN <56820099+KeremTAN@users.noreply.github.com> Date: Tue, 10 Sep 2024 06:08:55 +0300 Subject: [PATCH 27/32] include/json/value.h is changed (#1462) Co-authored-by: Jordan Bayles --- include/json/value.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/json/value.h b/include/json/value.h index 120dea8..f1232c4 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -3,8 +3,8 @@ // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE -#ifndef JSON_H_INCLUDED -#define JSON_H_INCLUDED +#ifndef JSON_VALUE_H_INCLUDED +#define JSON_VALUE_H_INCLUDED #if !defined(JSON_IS_AMALGAMATION) #include "forwards.h" From 99e8ca69b129ff0b65bded458404820c6a35d898 Mon Sep 17 00:00:00 2001 From: Rudi Heitbaum Date: Tue, 10 Sep 2024 13:09:10 +1000 Subject: [PATCH 28/32] meson.build: fix the version number (#1432) Co-authored-by: Jordan Bayles --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 8070361..e6ba08c 100644 --- a/meson.build +++ b/meson.build @@ -9,7 +9,7 @@ project( # 2. /include/json/version.h # 3. /CMakeLists.txt # IMPORTANT: also update the SOVERSION!! - version : '1.9.4', + version : '1.9.5', default_options : [ 'buildtype=release', 'cpp_std=c++11', From 3aa1192a0071ba640f249566c1d4ba2cf391a21c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 10 Sep 2024 05:11:44 +0200 Subject: [PATCH 29/32] Introduce CharReaderBuilder::ecma404Mode (#1333) * Introduce CharReaderBuilder::ecma404Mode * Bump micro version --------- Co-authored-by: Jordan Bayles Co-authored-by: Billy Donahue Co-authored-by: Jordan Bayles --- CMakeLists.txt | 4 ++-- include/json/reader.h | 6 ++++++ include/json/version.h | 4 ++-- meson.build | 4 ++-- src/lib_json/json_reader.cpp | 16 ++++++++++++++++ 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a37e726..f11425c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,11 +62,11 @@ project(jsoncpp # 2. ./include/json/version.h # 3. ./CMakeLists.txt # IMPORTANT: also update the PROJECT_SOVERSION!! - VERSION 1.9.5 # [.[.[.]]] + VERSION 1.9.6 # [.[.[.]]] LANGUAGES CXX) message(STATUS "JsonCpp Version: ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") -set(PROJECT_SOVERSION 25) +set(PROJECT_SOVERSION 26) include(${CMAKE_CURRENT_SOURCE_DIR}/include/PreventInSourceBuilds.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/include/PreventInBuildInstalls.cmake) diff --git a/include/json/reader.h b/include/json/reader.h index 38b9360..d745378 100644 --- a/include/json/reader.h +++ b/include/json/reader.h @@ -385,6 +385,12 @@ public: * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode */ static void strictMode(Json::Value* settings); + /** ECMA-404 mode. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderECMA404Mode + */ + static void ecma404Mode(Json::Value* settings); }; /** Consume entire stream and use its begin/end. diff --git a/include/json/version.h b/include/json/version.h index 9e95411..38faedf 100644 --- a/include/json/version.h +++ b/include/json/version.h @@ -9,10 +9,10 @@ // 3. /CMakeLists.txt // IMPORTANT: also update the SOVERSION!! -#define JSONCPP_VERSION_STRING "1.9.5" +#define JSONCPP_VERSION_STRING "1.9.6" #define JSONCPP_VERSION_MAJOR 1 #define JSONCPP_VERSION_MINOR 9 -#define JSONCPP_VERSION_PATCH 5 +#define JSONCPP_VERSION_PATCH 6 #define JSONCPP_VERSION_QUALIFIER #define JSONCPP_VERSION_HEXA \ ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \ diff --git a/meson.build b/meson.build index e6ba08c..561b41c 100644 --- a/meson.build +++ b/meson.build @@ -9,7 +9,7 @@ project( # 2. /include/json/version.h # 3. /CMakeLists.txt # IMPORTANT: also update the SOVERSION!! - version : '1.9.5', + version : '1.9.6', default_options : [ 'buildtype=release', 'cpp_std=c++11', @@ -50,7 +50,7 @@ jsoncpp_lib = library( 'src/lib_json/json_value.cpp', 'src/lib_json/json_writer.cpp', ]), - soversion : 25, + soversion : 26, install : true, include_directories : jsoncpp_include_directories, cpp_args: dll_export_flag) diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index 8ef29f0..10c97ae 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -1961,6 +1961,22 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) { (*settings)["skipBom"] = true; //! [CharReaderBuilderDefaults] } +// static +void CharReaderBuilder::ecma404Mode(Json::Value* settings) { + //! [CharReaderBuilderECMA404Mode] + (*settings)["allowComments"] = false; + (*settings)["allowTrailingCommas"] = false; + (*settings)["strictRoot"] = false; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = true; + (*settings)["rejectDupKeys"] = false; + (*settings)["allowSpecialFloats"] = false; + (*settings)["skipBom"] = false; + //! [CharReaderBuilderECMA404Mode] +} std::vector CharReader::getStructuredErrors() const { From 2067f66d662ef0925733ba95595a3cf988a2d32d Mon Sep 17 00:00:00 2001 From: zeroxia Date: Tue, 10 Sep 2024 11:14:17 +0800 Subject: [PATCH 30/32] cmake export configuration: allow repeating find_package(jsoncpp) calls (#1491) In jsoncpp-namspaced-targets.cmake, it creates JsonCpp::JsonCpp imported library without first checking whether it was already created by former call to find_package(JsonCpp). As CMake allows repeated call to find_package(), the error of "another target with the same name already exists" should be fixed. Co-authored-by: xiazuoling.xzl Co-authored-by: Jordan Bayles From 7f36cdb3ead60cdc77ec4e6a104bd2f653730b30 Mon Sep 17 00:00:00 2001 From: Timofey <45315126+petukhovtd@users.noreply.github.com> Date: Tue, 10 Sep 2024 10:14:36 +0700 Subject: [PATCH 31/32] Added Value::find with String key (#1467) * Added Value::find with String key * Fix codestyle --------- Co-authored-by: Jordan Bayles Co-authored-by: Petukhov Timofey --- include/json/value.h | 3 +++ src/lib_json/json_value.cpp | 5 ++++- src/test_lib_json/main.cpp | 9 +++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/include/json/value.h b/include/json/value.h index f1232c4..c8e1aae 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -513,6 +513,9 @@ public: /// and operator[]const /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 Value const* find(char const* begin, char const* end) const; + /// Most general and efficient version of isMember()const, get()const, + /// and operator[]const + Value const* find(const String& key) const; /// Most general and efficient version of object-mutators. /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. diff --git a/src/lib_json/json_value.cpp b/src/lib_json/json_value.cpp index a5e2dd8..5bd8d9a 100644 --- a/src/lib_json/json_value.cpp +++ b/src/lib_json/json_value.cpp @@ -1092,6 +1092,9 @@ Value const* Value::find(char const* begin, char const* end) const { return nullptr; return &(*it).second; } +Value const* Value::find(const String& key) const { + return find(key.data(), key.data() + key.length()); +} Value* Value::demand(char const* begin, char const* end) { JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, "in Json::Value::demand(begin, end): requires " @@ -1105,7 +1108,7 @@ const Value& Value::operator[](const char* key) const { return *found; } Value const& Value::operator[](const String& key) const { - Value const* found = find(key.data(), key.data() + key.length()); + Value const* found = find(key); if (!found) return nullSingleton(); return *found; diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp index fa41d19..55ab224 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -220,11 +220,20 @@ JSONTEST_FIXTURE_LOCAL(ValueTest, objects) { JSONTEST_ASSERT(foundId != nullptr); JSONTEST_ASSERT_EQUAL(Json::Value(1234), *foundId); + const std::string stringIdKey = "id"; + const Json::Value* stringFoundId = object1_.find(stringIdKey); + JSONTEST_ASSERT(stringFoundId != nullptr); + JSONTEST_ASSERT_EQUAL(Json::Value(1234), *stringFoundId); + const char unknownIdKey[] = "unknown id"; const Json::Value* foundUnknownId = object1_.find(unknownIdKey, unknownIdKey + strlen(unknownIdKey)); JSONTEST_ASSERT_EQUAL(nullptr, foundUnknownId); + const std::string stringUnknownIdKey = "unknown id"; + const Json::Value* stringFoundUnknownId = object1_.find(stringUnknownIdKey); + JSONTEST_ASSERT_EQUAL(nullptr, stringFoundUnknownId); + // Access through demand() const char yetAnotherIdKey[] = "yet another id"; const Json::Value* foundYetAnotherId = From 89e2973c754a9c02a49974d839779b151e95afd6 Mon Sep 17 00:00:00 2001 From: Scotty1701 <37419120+Scotty1701@users.noreply.github.com> Date: Mon, 9 Sep 2024 20:18:29 -0700 Subject: [PATCH 32/32] Don't use build dir build interfaces (#1419) Do not export a location in the build directory as a build interface. This location is not created until the build step is run and can interfere with the CMake configuration step if including in another project. Co-authored-by: Jordan Bayles