Skip to content
Draft
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
2 changes: 2 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ No changes since 3.2
#1544: Validate `read()` parameters in `MergedStream` and `UTF32Reader`
(implemented by @pjfanning)
#1545: Increase Android baseline from 26 to 34 in Jackson 3.2
#1557: Parsing for non-root number values fails lazily
(fix for blocking parsers by @seonwooj0810)
#1564: Add `SimpleStreamReadContext.rollbackValueRead()` method
#1575: Implement document length tracking for `DataInput`-backed
JSON parsers via subclass
Expand Down
72 changes: 55 additions & 17 deletions src/main/java/tools/jackson/core/json/ReaderBasedJsonParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -1413,10 +1413,8 @@ protected final JsonToken _parseUnsignedNumber(int ch) throws JacksonException
// Got it all: let's add to text buffer for parsing, access
--ptr; // need to push back following separator
_inputPtr = ptr;
// As per #105, need separating space between root values; check here
if (_streamReadContext.inRoot()) {
_verifyRootSpace(ch);
}
// [core#105]/[core#1557]: verify number is properly terminated/separated
_verifyNumberSeparator(ch);
int len = ptr-startPtr;
_textBuffer.resetWithShared(_inputBuffer, startPtr, len);
return resetInt(false, intLen);
Expand Down Expand Up @@ -1480,10 +1478,8 @@ private final JsonToken _parseFloat(int ch, int startPtr, int ptr, boolean neg,
}
--ptr; // need to push back following separator
_inputPtr = ptr;
// As per #105, need separating space between root values; check here
if (_streamReadContext.inRoot()) {
_verifyRootSpace(ch);
}
// [core#105]/[core#1557]: verify number is properly terminated/separated
_verifyNumberSeparator(ch);
int len = ptr-startPtr;
_textBuffer.resetWithShared(_inputBuffer, startPtr, len);
// And there we have it!
Expand Down Expand Up @@ -1538,9 +1534,7 @@ private final JsonToken _parseSignedNumber(final boolean negative) throws Jackso
}
--ptr;
_inputPtr = ptr;
if (_streamReadContext.inRoot()) {
_verifyRootSpace(ch);
}
_verifyNumberSeparator(ch);
int len = ptr-startPtr;
_textBuffer.resetWithShared(_inputBuffer, startPtr, len);
return resetInt(negative, intLen);
Expand Down Expand Up @@ -1704,9 +1698,7 @@ private final JsonToken _parseNumber2(boolean neg, int startPtr) throws JacksonE
// Ok; unless we hit end-of-input, need to push last char read back
if (!eof) {
--_inputPtr;
if (_streamReadContext.inRoot()) {
_verifyRootSpace(c);
}
_verifyNumberSeparator(c);
}
_textBuffer.setCurrentLength(outPtr);

Expand Down Expand Up @@ -1773,9 +1765,7 @@ private final JsonToken _finishHexNumber(boolean neg,

if (!eof) {
--_inputPtr; // push back the terminating non-hex char
if (_streamReadContext.inRoot()) {
_verifyRootSpace(c);
}
_verifyNumberSeparator(c);
}
_textBuffer.setCurrentLength(outPtr);
return resetIntHex(neg, hexLen);
Expand Down Expand Up @@ -1907,6 +1897,54 @@ private final void _verifyRootSpace(int ch) throws JacksonException
_reportMissingRootWS(ch);
}

/**
* Method called to verify that a just-decoded number value is followed by a
* valid separator or terminator character. For root-level values this means
* white space (as per [core#105], see {@link #_verifyRootSpace}); for non-root
* values ([core#1557]) the number must be followed by white space, a value
* separator ({@code ','}), an enclosing-structure end ({@code ']'} or
* {@code '}'}), a comment start (when enabled) or end-of-input. Without this,
* malformed content such as {@code [ 123true ]} would only fail lazily when
* accessing the following token.
*<p>
* On entry the caller has pushed the trailing character back, so {@code _inputPtr}
* points <i>at</i> it; for accepted separators this method leaves {@code _inputPtr}
* untouched so the next {@code nextToken()} call can consume them normally.
*/
private final void _verifyNumberSeparator(int ch) throws JacksonException
{
if (_streamReadContext.inRoot()) {
_verifyRootSpace(ch);
return;
}
switch (ch) {
case ' ':
case '\t':
case '\n':
case '\r':
case ',':
case ']':
case '}':
return;
case '/': // possible Java/C++ style comment
if (isEnabled(JsonReadFeature.ALLOW_JAVA_COMMENTS)) {
return;
}
break;
case '#': // possible YAML/shell style comment
if (isEnabled(JsonReadFeature.ALLOW_YAML_COMMENTS)) {
return;
}
break;
}
// Align `_inputPtr` with what `_reportUnexpectedNumberChar` ->
// `_currentLocationMinusOne()` expects (one past the offending char),
// matching `_verifyRootSpace` which advances up front.
++_inputPtr;
_reportUnexpectedNumberChar(ch,
"expected space, comma, or closing bracket/brace to separate or terminate numeric value");
}

/*
/**********************************************************************
/* Internal methods, secondary parsing
Expand Down
69 changes: 54 additions & 15 deletions src/main/java/tools/jackson/core/json/UTF8DataInputJsonParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -1092,11 +1092,9 @@ protected JsonToken _parseUnsignedNumber(int c) throws IOException
return _parseFloat(outBuf, outPtr, c, false, intLen);
}
_textBuffer.setCurrentLength(outPtr);
// As per [core#105], need separating space between root values; check here
// [core#105]/[core#1557]: verify number is properly terminated/separated
_nextByte = c;
if (_streamReadContext.inRoot()) {
_verifyRootSpace();
}
_verifyNumberSeparator();
// And there we have it!
return resetInt(false, intLen);
}
Expand Down Expand Up @@ -1160,11 +1158,9 @@ private final JsonToken _parseSignedNumber(boolean negative) throws IOException
return _parseFloat(outBuf, outPtr, c, negative, intLen);
}
_textBuffer.setCurrentLength(outPtr);
// As per [core#105], need separating space between root values; check here
// [core#105]/[core#1557]: verify number is properly terminated/separated
_nextByte = c;
if (_streamReadContext.inRoot()) {
_verifyRootSpace();
}
_verifyNumberSeparator();
// And there we have it!
return resetInt(negative, intLen);
}
Expand Down Expand Up @@ -1209,9 +1205,7 @@ private final JsonToken _finishHexNumber(boolean neg, char[] outBuf, int outPtr,
}
_textBuffer.setCurrentLength(outPtr);
_nextByte = c;
if (_streamReadContext.inRoot()) {
_verifyRootSpace();
}
_verifyNumberSeparator();
return resetIntHex(neg, hexLen);
}

Expand Down Expand Up @@ -1313,11 +1307,9 @@ private final JsonToken _parseFloat(char[] outBuf, int outPtr, int c,
}

// Ok; unless we hit end-of-input, need to push last char read back
// As per #105, need separating space between root values; check here
// [core#105]/[core#1557]: verify number is properly terminated/separated
_nextByte = c;
if (_streamReadContext.inRoot()) {
_verifyRootSpace();
}
_verifyNumberSeparator();
_textBuffer.setCurrentLength(outPtr);

// And there we have it!
Expand Down Expand Up @@ -1345,6 +1337,53 @@ private final void _verifyRootSpace() throws JacksonException
_reportMissingRootWS(ch);
}

/**
* Method called to verify that a just-decoded number value is followed by a
* valid separator or terminator. For root-level values this means white space
* (as per [core#105], see {@link #_verifyRootSpace}); for non-root values
* ([core#1557]) the number must be followed by white space, a value separator
* ({@code ','}), an enclosing-structure end ({@code ']'} or {@code '}'}), a
* comment start (when enabled) or end-of-input. Without this, malformed content
* such as {@code [ 123true ]} would only fail lazily when accessing the
* following token.
*<p>
* The trailing character is held in {@code _nextByte}; for accepted separators
* it is left there so the next {@code nextToken()} call can consume it normally.
*/
private final void _verifyNumberSeparator() throws JacksonException
{
if (_streamReadContext.inRoot()) {
_verifyRootSpace();
return;
}
final int ch = _nextByte;
if (ch < 0) { // end-of-input: number itself is complete
return;
}
switch (ch) {
case ' ':
case '\t':
case '\n':
case '\r':
case ',':
case ']':
case '}':
return;
case '/': // possible Java/C++ style comment
if (isEnabled(JsonReadFeature.ALLOW_JAVA_COMMENTS)) {
return;
}
break;
case '#': // possible YAML/shell style comment
if (isEnabled(JsonReadFeature.ALLOW_YAML_COMMENTS)) {
return;
}
break;
}
_reportUnexpectedNumberChar(ch,
"expected space, comma, or closing bracket/brace to separate or terminate numeric value");
}

/*
/**********************************************************************
/* Internal methods, secondary parsing
Expand Down
76 changes: 57 additions & 19 deletions src/main/java/tools/jackson/core/json/UTF8StreamJsonParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -1856,10 +1856,8 @@ protected JsonToken _parseUnsignedNumber(int c) throws JacksonException
}
--_inputPtr; // to push back trailing char (comma etc)
_textBuffer.setCurrentLength(outPtr);
// As per #105, need separating space between root values; check here
if (_streamReadContext.inRoot()) {
_verifyRootSpace(c);
}
// [core#105]/[core#1557]: verify number is properly terminated/separated
_verifyNumberSeparator(c);
// And there we have it!
return resetInt(false, intLen);
}
Expand Down Expand Up @@ -1925,10 +1923,8 @@ private final JsonToken _parseSignedNumber(boolean negative) throws JacksonExcep

--_inputPtr; // to push back trailing char (comma etc)
_textBuffer.setCurrentLength(outPtr);
// As per #105, need separating space between root values; check here
if (_streamReadContext.inRoot()) {
_verifyRootSpace(c);
}
// [core#105]/[core#1557]: verify number is properly terminated/separated
_verifyNumberSeparator(c);

// And there we have it!
return resetInt(negative, intLen);
Expand Down Expand Up @@ -1961,10 +1957,8 @@ private final JsonToken _parseNumber2(char[] outBuf, int outPtr, boolean negativ
}
--_inputPtr; // to push back trailing char (comma etc)
_textBuffer.setCurrentLength(outPtr);
// As per #105, need separating space between root values; check here
if (_streamReadContext.inRoot()) {
_verifyRootSpace(_inputBuffer[_inputPtr] & 0xFF);
}
// [core#105]/[core#1557]: verify number is properly terminated/separated
_verifyNumberSeparator(_inputBuffer[_inputPtr] & 0xFF);

// And there we have it!
return resetInt(negative, intPartLength);
Expand Down Expand Up @@ -2025,9 +2019,7 @@ private final JsonToken _finishHexNumber(boolean neg, char[] outBuf, int outPtr,

if (!eof) {
--_inputPtr; // push back the terminating non-hex char
if (_streamReadContext.inRoot()) {
_verifyRootSpace(c);
}
_verifyNumberSeparator(c);
}
_textBuffer.setCurrentLength(outPtr);
return resetIntHex(neg, hexLen);
Expand Down Expand Up @@ -2157,10 +2149,8 @@ private final JsonToken _parseFloat(char[] outBuf, int outPtr, int c,
// Ok; unless we hit end-of-input, need to push last char read back
if (!eof) {
--_inputPtr;
// As per [core#105], need separating space between root values; check here
if (_streamReadContext.inRoot()) {
_verifyRootSpace(c);
}
// [core#105]/[core#1557]: verify number is properly terminated/separated
_verifyNumberSeparator(c);
}
_textBuffer.setCurrentLength(outPtr);

Expand Down Expand Up @@ -2203,6 +2193,54 @@ private final void _verifyRootSpace(int ch) throws JacksonException
_reportMissingRootWS(ch);
}

/**
* Method called to verify that a just-decoded number value is followed by a
* valid separator or terminator character. For root-level values this means
* white space (as per [core#105], see {@link #_verifyRootSpace}); for non-root
* values ([core#1557]) the number must be followed by white space, a value
* separator ({@code ','}), an enclosing-structure end ({@code ']'} or
* {@code '}'}), a comment start (when enabled) or end-of-input. Without this,
* malformed content such as {@code [ 123true ]} would only fail lazily when
* accessing the following token.
*<p>
* On entry the caller has pushed the trailing character back, so {@code _inputPtr}
* points <i>at</i> it; for accepted separators this method leaves {@code _inputPtr}
* untouched so the next {@code nextToken()} call can consume them normally.
*/
private final void _verifyNumberSeparator(int ch) throws JacksonException
{
if (_streamReadContext.inRoot()) {
_verifyRootSpace(ch);
return;
}
switch (ch) {
case ' ':
case '\t':
case '\n':
case '\r':
case ',':
case ']':
case '}':
return;
case '/': // possible Java/C++ style comment
if (isEnabled(JsonReadFeature.ALLOW_JAVA_COMMENTS)) {
return;
}
break;
case '#': // possible YAML/shell style comment
if (isEnabled(JsonReadFeature.ALLOW_YAML_COMMENTS)) {
return;
}
break;
}
// Align `_inputPtr` with what `_reportUnexpectedNumberChar` ->
// `_currentLocationMinusOne()` expects (one past the offending char),
// matching `_verifyRootSpace` which advances up front.
++_inputPtr;
_reportUnexpectedNumberChar(ch,
"expected space, comma, or closing bracket/brace to separate or terminate numeric value");
}

/*
/**********************************************************************
/* Internal methods, secondary parsing
Expand Down
1 change: 0 additions & 1 deletion src/test/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
opens tools.jackson.core.unittest.jsonptr;
opens tools.jackson.core.unittest.read;
opens tools.jackson.core.unittest.read.loc;
opens tools.jackson.core.unittest.tofix;
opens tools.jackson.core.unittest.tofix.async;
opens tools.jackson.core.unittest.sym;
opens tools.jackson.core.unittest.type;
Expand Down
Loading