From 0a1e3f5174191d78513fbcc5fb5bf7fa93032469 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Fri, 27 Jan 2023 01:59:27 -0700 Subject: decode: Separate the scanner's panic-flow from the decoder's panic-flow It may be useful to hit "ignore space change" when viewing this patch. --- decode.go | 105 ++++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 61 insertions(+), 44 deletions(-) diff --git a/decode.go b/decode.go index 8c836d5..b206a88 100644 --- a/decode.go +++ b/decode.go @@ -53,8 +53,8 @@ import ( // or another is encountered; if it does not, then the parent Decode // call will return a *DecodeTypeError. // -// Implementor's note: "limitingScanner" is the thing to search for in -// decode.go if you want to read up on that io.RuneScanner. +// Implementor's note: "withLimitingScanner" is the thing to search +// for in decode.go if you want to read up on that io.RuneScanner. type Decodable interface { DecodeJSON(io.RuneScanner) error } @@ -314,6 +314,8 @@ type decRuneScanner struct { eof bool } +type scannerPanic *DecodeError + func (sc *decRuneScanner) ReadRune() (rune, int, error) { if sc.eof { return 0, 0, io.EOF @@ -325,12 +327,12 @@ func (sc *decRuneScanner) ReadRune() (rune, int, error) { return 0, 0, io.EOF } if e != nil { - panic(decodeError{ + panic(scannerPanic(&DecodeError{ Field: sc.dec.structStackStr(), FieldParent: sc.dec.structStackParent(), FieldName: sc.dec.structStackName(), Err: e, - }) + })) } return c, s, nil } @@ -339,11 +341,21 @@ func (sc *decRuneScanner) UnreadRune() error { return sc.dec.io.UnreadRune() } -func (dec *Decoder) limitingScanner() io.RuneScanner { +func (dec *Decoder) withLimitingScanner(fn func(io.RuneScanner) *DecodeError) (err *DecodeError) { dec.io.PushReadBarrier() - return &decRuneScanner{ - dec: dec, + defer func() { + if r := recover(); r != nil { + if sp, ok := r.(scannerPanic); ok { + err = (*DecodeError)(sp) + } else { + panic(r) + } + } + }() + if err := fn(&decRuneScanner{dec: dec}); err != nil { + return err } + return nil } // decoder main //////////////////////////////////////////////////////////////////////////////////// @@ -392,13 +404,15 @@ func (dec *Decoder) decode(val reflect.Value, nullOK bool) (_err *DecodeError) { case val.CanAddr() && reflect.PointerTo(typ).Implements(decodableType): t := dec.peekRuneType() obj := val.Addr().Interface().(Decodable) - l := dec.limitingScanner() - if err := obj.DecodeJSON(l); err != nil { - dec.panicType(t.JSONType(), reflect.PointerTo(typ), err) - } - if _, _, err := l.ReadRune(); err != io.EOF { - dec.panicType(t.JSONType(), reflect.PointerTo(typ), fmt.Errorf("did not consume entire %s", t.JSONType())) - } + return dec.withLimitingScanner(func(l io.RuneScanner) *DecodeError { + if err := obj.DecodeJSON(l); err != nil { + dec.panicType(t.JSONType(), reflect.PointerTo(typ), err) + } + if _, _, err := l.ReadRune(); err != io.EOF { + dec.panicType(t.JSONType(), reflect.PointerTo(typ), fmt.Errorf("did not consume entire %s", t.JSONType())) + } + return nil + }) case val.CanAddr() && reflect.PointerTo(typ).Implements(jsonUnmarshalerType): t := dec.peekRuneType() var buf bytes.Buffer @@ -906,31 +920,33 @@ func DecodeObject(r io.RuneScanner, decodeKey, decodeVal func(io.RuneScanner) er func() *DecodeError { dec.posStackPush() defer dec.posStackPop() - l := dec.limitingScanner() - if err := decodeKey(l); err != nil { - // TODO: Find a better Go type to use than `nil`. - dec.panicType("string", nil, err) - } - if _, _, err := l.ReadRune(); err != io.EOF { - // TODO: Find a better Go type to use than `nil`. - dec.panicType("string", nil, fmt.Errorf("did not consume entire string")) - } - return nil + return dec.withLimitingScanner(func(l io.RuneScanner) *DecodeError { + if err := decodeKey(l); err != nil { + // TODO: Find a better Go type to use than `nil`. + dec.panicType("string", nil, err) + } + if _, _, err := l.ReadRune(); err != io.EOF { + // TODO: Find a better Go type to use than `nil`. + dec.panicType("string", nil, fmt.Errorf("did not consume entire string")) + } + return nil + }) }, func() *DecodeError { dec.posStackPush() defer dec.posStackPop() t := dec.peekRuneType() - l := dec.limitingScanner() - if err := decodeVal(l); err != nil { - // TODO: Find a better Go type to use than `nil`. - dec.panicType(t.JSONType(), nil, err) - } - if _, _, err := l.ReadRune(); err != io.EOF { - // TODO: Find a better Go type to use than `nil`. - dec.panicType(t.JSONType(), nil, fmt.Errorf("did not consume entire %s", t.JSONType())) - } - return nil + return dec.withLimitingScanner(func(l io.RuneScanner) *DecodeError { + if err := decodeVal(l); err != nil { + // TODO: Find a better Go type to use than `nil`. + dec.panicType(t.JSONType(), nil, err) + } + if _, _, err := l.ReadRune(); err != io.EOF { + // TODO: Find a better Go type to use than `nil`. + dec.panicType(t.JSONType(), nil, fmt.Errorf("did not consume entire %s", t.JSONType())) + } + return nil + }) }); err != nil { return err } @@ -999,16 +1015,17 @@ func DecodeArray(r io.RuneScanner, decodeMember func(r io.RuneScanner) error) (e dec.posStackPush() defer dec.posStackPop() t := dec.peekRuneType() - l := dec.limitingScanner() - if err := decodeMember(l); err != nil { - // TODO: Find a better Go type to use than `nil`. - dec.panicType(t.JSONType(), nil, err) - } - if _, _, err := l.ReadRune(); err != io.EOF { - // TODO: Find a better Go type to use than `nil`. - dec.panicType(t.JSONType(), nil, fmt.Errorf("did not consume entire %s", t.JSONType())) - } - return nil + return dec.withLimitingScanner(func(l io.RuneScanner) *DecodeError { + if err := decodeMember(l); err != nil { + // TODO: Find a better Go type to use than `nil`. + dec.panicType(t.JSONType(), nil, err) + } + if _, _, err := l.ReadRune(); err != io.EOF { + // TODO: Find a better Go type to use than `nil`. + dec.panicType(t.JSONType(), nil, fmt.Errorf("did not consume entire %s", t.JSONType())) + } + return nil + }) }); err != nil { return err } -- cgit v1.2.3-2-g168b