From e0dd517d5027fba176dbd386f2c325e960c1b900 Mon Sep 17 00:00:00 2001 From: Xiaolong Peng Date: Fri, 22 Nov 2024 17:41:35 +0000 Subject: [PATCH] 8319640: ClassicFormat::parseObject (from DateTimeFormatter) does not conform to the javadoc and may leak DateTimeException Backport-of: fe0ccdf5f8a5559a608d2e2cd2b6aecbe245c5ec --- .../java/time/format/DateTimeFormatter.java | 22 +++++------- .../java/time/format/TestDateTimeParsing.java | 36 +++++++++++++++++-- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java b/src/java.base/share/classes/java/time/format/DateTimeFormatter.java index c94b123b26c..c8e865db2b4 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatter.java @@ -2240,29 +2240,23 @@ public Object parseObject(String text, ParsePosition pos) { DateTimeParseContext context; try { context = formatter.parseUnresolved0(text, pos); - } catch (IndexOutOfBoundsException ex) { - if (pos.getErrorIndex() < 0) { - pos.setErrorIndex(0); - } - return null; - } - if (context == null) { - if (pos.getErrorIndex() < 0) { - pos.setErrorIndex(0); + if (context == null) { + if (pos.getErrorIndex() < 0) { + pos.setErrorIndex(0); + } + return null; } - return null; - } - try { TemporalAccessor resolved = context.toResolved(formatter.resolverStyle, formatter.resolverFields); if (parseType == null) { return resolved; } return resolved.query(parseType); } catch (RuntimeException ex) { - pos.setErrorIndex(0); + if (pos.getErrorIndex() < 0) { + pos.setErrorIndex(0); + } return null; } } } - } diff --git a/test/jdk/java/time/test/java/time/format/TestDateTimeParsing.java b/test/jdk/java/time/test/java/time/format/TestDateTimeParsing.java index d5110d5f5d2..67c91e6ad7e 100644 --- a/test/jdk/java/time/test/java/time/format/TestDateTimeParsing.java +++ b/test/jdk/java/time/test/java/time/format/TestDateTimeParsing.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, 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 @@ -62,15 +62,19 @@ import static java.time.temporal.ChronoField.AMPM_OF_DAY; import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.HOUR_OF_AMPM; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; import static java.time.temporal.ChronoField.INSTANT_SECONDS; import static java.time.temporal.ChronoField.MICRO_OF_SECOND; import static java.time.temporal.ChronoField.MILLI_OF_SECOND; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; import static java.time.temporal.ChronoField.NANO_OF_SECOND; import static java.time.temporal.ChronoField.OFFSET_SECONDS; import static java.time.temporal.ChronoField.SECOND_OF_DAY; import static java.util.Locale.US; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import java.text.ParsePosition; import java.time.DateTimeException; import java.time.Instant; import java.time.LocalDateTime; @@ -80,7 +84,9 @@ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; +import java.time.format.SignStyle; import java.time.temporal.TemporalAccessor; +import java.util.Locale; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -88,7 +94,7 @@ /** * @test * @summary Test parsing of edge cases. - * @bug 8223773 8272473 + * @bug 8223773 8272473 8319640 */ public class TestDateTimeParsing { @@ -237,4 +243,30 @@ public void test_validateHourOfAmPm() { } } } + + // Checks ::toFormat().parseObject(text, pos) do not throw DateTimeException + @Test + public void test_toFormat_2arg_null_return_on_DateTimeException() { + var f = new DateTimeFormatterBuilder() + .appendValue(HOUR_OF_DAY, 2, 2, SignStyle.NOT_NEGATIVE) + .optionalStart() + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 2, 2, SignStyle.NOT_NEGATIVE) + .optionalEnd() + .optionalStart() + .appendOffset("+HHmm", "Z") + .optionalEnd() + .toFormatter(Locale.ROOT) + .toFormat(); + assertNull(f.parseObject("17-30", new ParsePosition(0))); + } + + // Checks ::toFormat().parseObject(text, pos) do not throw IOOBE + @Test + public void test_toFormat_2arg_null_return_on_IOOBE() { + var date = "2023-11-13"; + assertNull(DateTimeFormatter.ISO_LOCAL_DATE + .toFormat() + .parseObject(date, new ParsePosition(date.length() + 1))); + } }