Skip to content

feat: sync API with latest upstream java.util.json#115

Closed
simbo1905 wants to merge 2 commits intomainfrom
sync-upstream-api
Closed

feat: sync API with latest upstream java.util.json#115
simbo1905 wants to merge 2 commits intomainfrom
sync-upstream-api

Conversation

@simbo1905
Copy link
Owner

Summary

Synchronized the backport API with the latest changes from the OpenJDK sandbox json branch. This sync addresses the drift reported by the daily API tracker.

Key Changes

  • Public API Refactor:
    • Renamed values() to elements() in JsonArray.
    • Renamed value() to bool() in JsonBoolean and string() in JsonString.
    • Replaced toNumber() and toBigDecimal() with toLong() and toDouble() in JsonNumber.
    • Removed Json.fromUntyped() and Json.toUntyped() as they are no longer in the upstream API.
    • Updated JsonParseException to use getErrorLine() and getErrorPosition().
  • Implementation Updates:
    • Updated JsonParser, Utils, and all Json*Impl classes to match upstream logic.
    • Backported Math.powExact for Java 21 compatibility.
    • Replaced Java 22+ unnamed variables (_) with named alternatives.
    • Preserved StableValue backport logic.
  • Tests & Documentation:
    • Updated README.md and index.html to reflect the new API.
    • Fixed all compilation errors and test failures across json-java21, json-java21-jtd, and json-java21-api-tracker.
    • Removed JsonTypedUntypedTests.java which relied on the removed untyped conversion methods.

Verified with mvn clean compile test passing across all modules.

@gemini-code-assist
Copy link

Summary of Changes

Hello @simbo1905, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request brings the backported JSON API up-to-date with the latest changes from the OpenJDK sandbox json branch. The primary goal is to ensure API consistency and address any functional drifts. This involved significant refactoring of public methods, internal implementation adjustments for parsing and number handling, and a comprehensive update of documentation and test suites to reflect these changes.

Highlights

  • Public API Refactor: Renamed values() to elements() in JsonArray, value() to bool() in JsonBoolean, and value() to string() in JsonString. The JsonNumber API now uses toLong() and toDouble() instead of toNumber() and toBigDecimal(). Removed Json.fromUntyped() and Json.toUntyped() methods. JsonParseException now provides getErrorLine() and getErrorPosition().
  • Implementation Updates: Updated JsonParser, Utils, and all Json*Impl classes to align with the upstream OpenJDK sandbox json branch logic. This includes backporting Math.powExact for Java 21 compatibility and replacing Java 22+ unnamed variables (_) with named alternatives.
  • Documentation and Testing: Updated README.md and index.html to reflect the new API. All compilation errors and test failures were fixed across json-java21, json-java21-jtd, and json-java21-api-tracker. The JsonTypedUntypedTests.java file was removed due to the removal of untyped conversion methods.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1ffcc80a29

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +79 to +80
var str = Double.toString(num);
return new JsonNumberImpl(str.toCharArray(), 0, str.length(), 0, 0);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Derive decimal/exponent offsets for JsonNumber.of(double)

Here JsonNumber.of(double) always passes (dec=0, exp=0) to JsonNumberImpl, which signals “exponent present” to initNumLong(). That method then tries to parse an exponent substring starting at index 1, so values like JsonNumber.of(1.0) or JsonNumber.of(1e3) hit NumberFormatException and toLong() throws JsonAssertionException even though the numeric value is an integer. This breaks the contract that whole-number doubles can be retrieved as long. You need to compute actual ./e offsets from str (or use -1 when absent) before constructing JsonNumberImpl.

Useful? React with 👍 / 👎.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request does an excellent job of synchronizing the API with the latest upstream changes from the OpenJDK sandbox. The updates are comprehensive, covering method renames, API removals, and implementation refactoring across the core library, documentation, and tests. The changes are consistent and correctly applied. I've found one critical issue in the JsonParser's duplicate key detection logic that could lead to incorrect parsing behavior, which I've detailed in a specific comment. Besides that, the quality of this large-scale refactoring is very high.

Comment on lines +126 to +128
if (members.putIfAbsent(name, parseValue()) != null) {
throw failure(nameOffset, "The duplicate member name: \"%s\" was already parsed".formatted(name));
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The current logic for detecting duplicate keys has a subtle but critical bug. parseValue() is called within members.putIfAbsent(), which means the value is parsed even if the key is a duplicate. This incorrectly advances the parser's offset, and if there are more members after the duplicate, it will likely lead to a subsequent syntax error because the parser is in the wrong position.

The check for a duplicate key should happen before parsing the value to avoid this side effect. The previous implementation was safer in this regard.

Suggested change
if (members.putIfAbsent(name, parseValue()) != null) {
throw failure(nameOffset, "The duplicate member name: \"%s\" was already parsed".formatted(name));
}
if (members.containsKey(name)) {
throw failure(nameOffset, "The duplicate member name: \"%s\" was already parsed".formatted(name));
}
members.put(name, parseValue());

- Update upstream reference to 4de0eb4f0c867df2d420501cf6741e50dee142d9 (2024-10-10)
- Fix API examples: JsonNumber uses toNumber().intValue() not intValue()
- Update conformance to 99.3%
- Update CI section to reflect auto-issue creation for API drift
- Clarify security section about recursive parser
- Fix typos throughout
- Add Eclipse project files to .gitignore
@simbo1905
Copy link
Owner Author

Response to AI Code Review Comments

Re: Gemini's concern about putIfAbsent duplicate key detection

False positive - tested and verified.

This is a faithful backport of the upstream OpenJDK jdk-sandbox json branch. The putIfAbsent pattern matches upstream exactly:
https://github.com/openjdk/jdk-sandbox/blob/json/src/java.base/share/classes/jdk/internal/util/json/JsonParser.java#L127

I created a demo class PrReviewIssuesDemo.java to test this specific concern. The duplicate key detection works correctly even with members after the duplicate:

Test: Duplicate key WITH members after (Gemini's specific case)
  Input: {"first": 1, "dup": "original", "dup": "duplicate", "last": 3}
  Threw: The duplicate member name: "dup" was already parsed. Location: line 0, position 37.
  [CORRECT - duplicate detected properly]

The parser throws on the duplicate key with the correct error message. The concern about "subsequent syntax error because the parser is in the wrong position" does not occur in practice because the exception is thrown immediately after putIfAbsent returns non-null, before any further parsing.


Re: Codex's concern about JsonNumber.of(double) with (dec=0, exp=0)

Confirmed bug - but it exists in upstream too.

This is a valid bug report. The issue is that JsonNumber.of(double) passes hardcoded (dec=0, exp=0) to JsonNumberImpl, which causes toLong() to fail even for whole numbers like 1.0.

Test: JsonNumber.of(1.0)
  toString() = '1.0'
  toLong() threw: 1.0 cannot be represented as a long.
  [CONFIRMED BUG]

Control: Json.parse("1.0") - parsed from string
  toString() = '1.0'
  toLong() = 1 [Parser sets correct offsets]

However, this bug exists in upstream OpenJDK - our backport faithfully reproduces their code:
https://github.com/openjdk/jdk-sandbox/blob/json/src/java.base/share/classes/java/util/json/JsonNumber.java#L112

Since this is a backport, we cannot "fix" bugs that exist upstream without diverging from the upstream API. I will file a separate issue to track this for potential upstream reporting or local fix consideration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant