Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ jobs:
for k in totals: totals[k]+=int(r.get(k,'0'))
except Exception:
pass
exp_tests=4426
exp_skipped=1715
exp_tests=4436
exp_skipped=1692
if totals['tests']!=exp_tests or totals['skipped']!=exp_skipped:
print(f"Unexpected test totals: {totals} != expected tests={exp_tests}, skipped={exp_skipped}")
sys.exit(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@ public T get() {
}
return result;
}

@Override
public String toString() {
return get().toString();
}

@Override
public int hashCode() {
return get().hashCode();
}

@Override
public boolean equals(Object obj) {
return get().equals(obj);
Comment on lines +67 to +79

Choose a reason for hiding this comment

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

[P1] Avoid breaking Supplier equality contract

The new equals and hashCode implementations delegate straight to the cached value without checking the argument type. As a result a supplier now reports true when compared with its underlying value (supplier.equals("foo")) while the reverse comparison ("foo".equals(supplier)) remains false. This violates the symmetry requirement of Object.equals and can corrupt hash-based collections when suppliers are used as keys. Consider keeping identity semantics or only comparing other suppliers by their cached value.

Useful? React with 👍 / 👎.

}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright (c) 2025, Simon Massey. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/

/*
* @test
* @enablePreview
* @summary Test escaped characters in JSON object keys
* @run junit EscapedKeyBugTest
*/

package jdk.sandbox.java.util.json;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import java.util.logging.Logger;

public class EscapedKeyBugTest {
private static final Logger LOGGER = Logger.getLogger(EscapedKeyBugTest.class.getName());

@Test
public void testEscapedCharactersInKeys() {
LOGGER.info("Executing testEscapedCharactersInKeys");
// Should parse successfully with escaped characters in keys
String json = """
{"foo\\nbar":1,"foo\\tbar":2}
""";

JsonValue result = Json.parse(json);
JsonObject obj = (JsonObject) result;

// Verify both keys are parsed correctly
assertEquals(1L, ((JsonNumber) obj.members().get("foo\nbar")).toNumber());
assertEquals(2L, ((JsonNumber) obj.members().get("foo\tbar")).toNumber());
}

@Test
public void testEscapedQuoteInKey() {
LOGGER.info("Executing testEscapedQuoteInKey");
// Test escaped quotes in keys
String json = """
{"foo\\"bar":1}
""";

JsonValue result = Json.parse(json);
JsonObject obj = (JsonObject) result;

// Verify key with escaped quote is parsed correctly
assertEquals(1L, ((JsonNumber) obj.members().get("foo\"bar")).toNumber());
}

@Test
public void testEscapedBackslashInKey() {
LOGGER.info("Executing testEscapedBackslashInKey");
// Test escaped backslashes in keys
String json = """
{"foo\\\\bar":1}
""";

JsonValue result = Json.parse(json);
JsonObject obj = (JsonObject) result;

// Verify key with escaped backslash is parsed correctly
assertEquals(1L, ((JsonNumber) obj.members().get("foo\\bar")).toNumber());
}

@Test
public void testMultipleEscapedCharactersInKey() {
LOGGER.info("Executing testMultipleEscapedCharactersInKey");
// Test multiple escaped characters in one key
String json = """
{"foo\\n\\t\\"bar":1}
""";

JsonValue result = Json.parse(json);
JsonObject obj = (JsonObject) result;

// Verify key with multiple escaped characters is parsed correctly
assertEquals(1L, ((JsonNumber) obj.members().get("foo\n\t\"bar")).toNumber());
}
}