Skip to content

Commit cf4af0b

Browse files
dsnetgopherbot
authored andcommitted
encoding/json/v2: fix UnmarshalDecode regression with EOF
When EOF is encountered within jsontext.Decoder stream without starting to parse any token, UnmarshalDecode should report EOF, rather than converting it into ErrUnexpectedEOF. This fixes a regression introduced by https://go.dev/cl/689919. This change only affects code compiled under goexperiment.jsonv2. Fixes golang#74835 Change-Id: I7e8e57ab11b462c422c538503ed8c6b91ead53bd Reviewed-on: https://go-review.googlesource.com/c/go/+/692175 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Sean Liao <[email protected]> Reviewed-by: David Chase <[email protected]> Reviewed-by: Jake Bailey <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> Auto-Submit: Joseph Tsai <[email protected]>
1 parent b096ddb commit cf4af0b

File tree

2 files changed

+47
-2
lines changed

2 files changed

+47
-2
lines changed

src/encoding/json/v2/arshal.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ func unmarshalDecode(in *jsontext.Decoder, out any, uo *jsonopts.Struct, last bo
470470
// was validated before attempting to unmarshal it.
471471
if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
472472
if err := export.Decoder(in).CheckNextValue(last); err != nil {
473-
if err == io.EOF {
473+
if err == io.EOF && last {
474474
offset := in.InputOffset() + int64(len(in.UnreadBuffer()))
475475
return &jsontext.SyntacticError{ByteOffset: offset, Err: io.ErrUnexpectedEOF}
476476
}
@@ -487,7 +487,7 @@ func unmarshalDecode(in *jsontext.Decoder, out any, uo *jsonopts.Struct, last bo
487487
if !uo.Flags.Get(jsonflags.AllowDuplicateNames) {
488488
export.Decoder(in).Tokens.InvalidateDisabledNamespaces()
489489
}
490-
if err == io.EOF {
490+
if err == io.EOF && last {
491491
offset := in.InputOffset() + int64(len(in.UnreadBuffer()))
492492
return &jsontext.SyntacticError{ByteOffset: offset, Err: io.ErrUnexpectedEOF}
493493
}

src/encoding/json/v2/arshal_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9413,6 +9413,51 @@ func TestUnmarshalDecodeOptions(t *testing.T) {
94139413
}
94149414
}
94159415

9416+
func TestUnmarshalDecodeStream(t *testing.T) {
9417+
tests := []struct {
9418+
in string
9419+
want []any
9420+
err error
9421+
}{
9422+
{in: ``, err: io.EOF},
9423+
{in: `{`, err: &jsontext.SyntacticError{ByteOffset: len64(`{`), Err: io.ErrUnexpectedEOF}},
9424+
{in: `{"`, err: &jsontext.SyntacticError{ByteOffset: len64(`{"`), Err: io.ErrUnexpectedEOF}},
9425+
{in: `{"k"`, err: &jsontext.SyntacticError{ByteOffset: len64(`{"k"`), JSONPointer: "/k", Err: io.ErrUnexpectedEOF}},
9426+
{in: `{"k":`, err: &jsontext.SyntacticError{ByteOffset: len64(`{"k":`), JSONPointer: "/k", Err: io.ErrUnexpectedEOF}},
9427+
{in: `{"k",`, err: &jsontext.SyntacticError{ByteOffset: len64(`{"k"`), JSONPointer: "/k", Err: jsonwire.NewInvalidCharacterError(",", "after object name (expecting ':')")}},
9428+
{in: `{"k"}`, err: &jsontext.SyntacticError{ByteOffset: len64(`{"k"`), JSONPointer: "/k", Err: jsonwire.NewInvalidCharacterError("}", "after object name (expecting ':')")}},
9429+
{in: `[`, err: &jsontext.SyntacticError{ByteOffset: len64(`[`), Err: io.ErrUnexpectedEOF}},
9430+
{in: `[0`, err: &jsontext.SyntacticError{ByteOffset: len64(`[0`), Err: io.ErrUnexpectedEOF}},
9431+
{in: ` [0`, err: &jsontext.SyntacticError{ByteOffset: len64(` [0`), Err: io.ErrUnexpectedEOF}},
9432+
{in: `[0.`, err: &jsontext.SyntacticError{ByteOffset: len64(`[`), JSONPointer: "/0", Err: io.ErrUnexpectedEOF}},
9433+
{in: `[0. `, err: &jsontext.SyntacticError{ByteOffset: len64(`[0.`), JSONPointer: "/0", Err: jsonwire.NewInvalidCharacterError(" ", "in number (expecting digit)")}},
9434+
{in: `[0,`, err: &jsontext.SyntacticError{ByteOffset: len64(`[0,`), Err: io.ErrUnexpectedEOF}},
9435+
{in: `[0:`, err: &jsontext.SyntacticError{ByteOffset: len64(`[0`), Err: jsonwire.NewInvalidCharacterError(":", "after array element (expecting ',' or ']')")}},
9436+
{in: `n`, err: &jsontext.SyntacticError{ByteOffset: len64(`n`), Err: io.ErrUnexpectedEOF}},
9437+
{in: `nul`, err: &jsontext.SyntacticError{ByteOffset: len64(`nul`), Err: io.ErrUnexpectedEOF}},
9438+
{in: `fal `, err: &jsontext.SyntacticError{ByteOffset: len64(`fal`), Err: jsonwire.NewInvalidCharacterError(" ", "in literal false (expecting 's')")}},
9439+
{in: `false`, want: []any{false}, err: io.EOF},
9440+
{in: `false0.0[]null`, want: []any{false, 0.0, []any{}, nil}, err: io.EOF},
9441+
}
9442+
for _, tt := range tests {
9443+
d := jsontext.NewDecoder(strings.NewReader(tt.in))
9444+
var got []any
9445+
for {
9446+
var v any
9447+
if err := UnmarshalDecode(d, &v); err != nil {
9448+
if !reflect.DeepEqual(err, tt.err) {
9449+
t.Errorf("`%s`: UnmarshalDecode error = %v, want %v", tt.in, err, tt.err)
9450+
}
9451+
break
9452+
}
9453+
got = append(got, v)
9454+
}
9455+
if !reflect.DeepEqual(got, tt.want) {
9456+
t.Errorf("`%s`: UnmarshalDecode = %v, want %v", tt.in, got, tt.want)
9457+
}
9458+
}
9459+
}
9460+
94169461
// BenchmarkUnmarshalDecodeOptions is a minimal decode operation to measure
94179462
// the overhead options setup before the unmarshal operation.
94189463
func BenchmarkUnmarshalDecodeOptions(b *testing.B) {

0 commit comments

Comments
 (0)