summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2023-01-27 01:59:27 -0700
committerLuke Shumaker <lukeshu@lukeshu.com>2023-02-10 21:49:02 -0700
commit0a1e3f5174191d78513fbcc5fb5bf7fa93032469 (patch)
treee3a936aeb5cbf66a6c75c68c5e36830b32421f0d
parent9d2ae945e330516fa1b3586262f7fe218ff60d33 (diff)
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.
-rw-r--r--decode.go105
1 files 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
}