From e6605a1bcc56523c0168caf765399dede7d3d1e4 Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Sat, 21 Jun 2025 21:01:32 -0700 Subject: [PATCH] encoding/json: use reflect.TypeAssert Updates #62121 Change-Id: Ic3c4fe84a5dacfd8270aba0d5dd59f83f0a9030f Reviewed-on: https://go-review.googlesource.com/c/go/+/701955 Reviewed-by: Mark Freeman Reviewed-by: Damien Neil LUCI-TryBot-Result: Go LUCI --- src/encoding/json/decode.go | 4 ++-- src/encoding/json/encode.go | 10 +++++----- src/encoding/json/v2/arshal_funcs.go | 12 ++++++++---- src/encoding/json/v2/arshal_inlined.go | 5 ++--- src/encoding/json/v2/arshal_methods.go | 16 +++++++++------- src/encoding/json/v2/arshal_time.go | 10 ++++------ 6 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go index fc29296c0f4..46f9ed2da9b 100644 --- a/src/encoding/json/decode.go +++ b/src/encoding/json/decode.go @@ -482,11 +482,11 @@ func indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnm v.Set(reflect.New(v.Type().Elem())) } if v.Type().NumMethod() > 0 && v.CanInterface() { - if u, ok := v.Interface().(Unmarshaler); ok { + if u, ok := reflect.TypeAssert[Unmarshaler](v); ok { return u, nil, reflect.Value{} } if !decodingNull { - if u, ok := v.Interface().(encoding.TextUnmarshaler); ok { + if u, ok := reflect.TypeAssert[encoding.TextUnmarshaler](v); ok { return nil, u, reflect.Value{} } } diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go index 29fcc91fd7b..1b7942dd3ad 100644 --- a/src/encoding/json/encode.go +++ b/src/encoding/json/encode.go @@ -475,7 +475,7 @@ func marshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) { e.WriteString("null") return } - m, ok := v.Interface().(Marshaler) + m, ok := reflect.TypeAssert[Marshaler](v) if !ok { e.WriteString("null") return @@ -498,7 +498,7 @@ func addrMarshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) { e.WriteString("null") return } - m := va.Interface().(Marshaler) + m, _ := reflect.TypeAssert[Marshaler](va) b, err := m.MarshalJSON() if err == nil { e.Grow(len(b)) @@ -516,7 +516,7 @@ func textMarshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) { e.WriteString("null") return } - m, ok := v.Interface().(encoding.TextMarshaler) + m, ok := reflect.TypeAssert[encoding.TextMarshaler](v) if !ok { e.WriteString("null") return @@ -534,7 +534,7 @@ func addrTextMarshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) { e.WriteString("null") return } - m := va.Interface().(encoding.TextMarshaler) + m, _ := reflect.TypeAssert[encoding.TextMarshaler](va) b, err := m.MarshalText() if err != nil { e.error(&MarshalerError{v.Type(), err, "MarshalText"}) @@ -991,7 +991,7 @@ func resolveKeyName(k reflect.Value) (string, error) { if k.Kind() == reflect.String { return k.String(), nil } - if tm, ok := k.Interface().(encoding.TextMarshaler); ok { + if tm, ok := reflect.TypeAssert[encoding.TextMarshaler](k); ok { if k.Kind() == reflect.Pointer && k.IsNil() { return "", nil } diff --git a/src/encoding/json/v2/arshal_funcs.go b/src/encoding/json/v2/arshal_funcs.go index 5986c547326..673caf3c376 100644 --- a/src/encoding/json/v2/arshal_funcs.go +++ b/src/encoding/json/v2/arshal_funcs.go @@ -177,7 +177,8 @@ func MarshalFunc[T any](fn func(T) ([]byte, error)) *Marshalers { typFnc := typedMarshaler{ typ: t, fnc: func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error { - val, err := fn(va.castTo(t).Interface().(T)) + v, _ := reflect.TypeAssert[T](va.castTo(t)) + val, err := fn(v) if err != nil { err = wrapSkipFunc(err, "marshal function of type func(T) ([]byte, error)") if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) { @@ -222,7 +223,8 @@ func MarshalToFunc[T any](fn func(*jsontext.Encoder, T) error) *Marshalers { xe := export.Encoder(enc) prevDepth, prevLength := xe.Tokens.DepthLength() xe.Flags.Set(jsonflags.WithinArshalCall | 1) - err := fn(enc, va.castTo(t).Interface().(T)) + v, _ := reflect.TypeAssert[T](va.castTo(t)) + err := fn(enc, v) xe.Flags.Set(jsonflags.WithinArshalCall | 0) currDepth, currLength := xe.Tokens.DepthLength() if err == nil && (prevDepth != currDepth || prevLength+1 != currLength) { @@ -269,7 +271,8 @@ func UnmarshalFunc[T any](fn func([]byte, T) error) *Unmarshalers { if err != nil { return err // must be a syntactic or I/O error } - err = fn(val, va.castTo(t).Interface().(T)) + v, _ := reflect.TypeAssert[T](va.castTo(t)) + err = fn(val, v) if err != nil { err = wrapSkipFunc(err, "unmarshal function of type func([]byte, T) error") if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) { @@ -304,7 +307,8 @@ func UnmarshalFromFunc[T any](fn func(*jsontext.Decoder, T) error) *Unmarshalers xd := export.Decoder(dec) prevDepth, prevLength := xd.Tokens.DepthLength() xd.Flags.Set(jsonflags.WithinArshalCall | 1) - err := fn(dec, va.castTo(t).Interface().(T)) + v, _ := reflect.TypeAssert[T](va.castTo(t)) + err := fn(dec, v) xd.Flags.Set(jsonflags.WithinArshalCall | 0) currDepth, currLength := xd.Tokens.DepthLength() if err == nil && (prevDepth != currDepth || prevLength+1 != currLength) { diff --git a/src/encoding/json/v2/arshal_inlined.go b/src/encoding/json/v2/arshal_inlined.go index a2531445025..d911bfa1c04 100644 --- a/src/encoding/json/v2/arshal_inlined.go +++ b/src/encoding/json/v2/arshal_inlined.go @@ -50,8 +50,7 @@ func marshalInlinedFallbackAll(enc *jsontext.Encoder, va addressableValue, mo *j } if v.Type() == jsontextValueType { - // TODO(https://go.dev/issue/62121): Use reflect.Value.AssertTo. - b := *v.Addr().Interface().(*jsontext.Value) + b, _ := reflect.TypeAssert[jsontext.Value](v.Value) if len(b) == 0 { // TODO: Should this be nil? What if it were all whitespace? return nil } @@ -174,7 +173,7 @@ func unmarshalInlinedFallbackNext(dec *jsontext.Decoder, va addressableValue, uo v = v.indirect(true) if v.Type() == jsontextValueType { - b := v.Addr().Interface().(*jsontext.Value) + b, _ := reflect.TypeAssert[*jsontext.Value](v.Addr()) if len(*b) == 0 { // TODO: Should this be nil? What if it were all whitespace? *b = append(*b, '{') } else { diff --git a/src/encoding/json/v2/arshal_methods.go b/src/encoding/json/v2/arshal_methods.go index 099be298c26..664c3927cc3 100644 --- a/src/encoding/json/v2/arshal_methods.go +++ b/src/encoding/json/v2/arshal_methods.go @@ -111,7 +111,7 @@ func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler { (needAddr && va.forcedAddr) { return prevMarshal(enc, va, mo) } - marshaler := va.Addr().Interface().(encoding.TextMarshaler) + marshaler, _ := reflect.TypeAssert[encoding.TextMarshaler](va.Addr()) if err := export.Encoder(enc).AppendRaw('"', false, func(b []byte) ([]byte, error) { b2, err := marshaler.MarshalText() return append(b, b2...), err @@ -137,7 +137,7 @@ func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler { (needAddr && va.forcedAddr) { return prevMarshal(enc, va, mo) } - appender := va.Addr().Interface().(encoding.TextAppender) + appender, _ := reflect.TypeAssert[encoding.TextAppender](va.Addr()) if err := export.Encoder(enc).AppendRaw('"', false, appender.AppendText); err != nil { err = wrapSkipFunc(err, "append method") if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) { @@ -160,7 +160,7 @@ func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler { ((needAddr && va.forcedAddr) || export.Encoder(enc).Tokens.Last.NeedObjectName()) { return prevMarshal(enc, va, mo) } - marshaler := va.Addr().Interface().(Marshaler) + marshaler, _ := reflect.TypeAssert[Marshaler](va.Addr()) val, err := marshaler.MarshalJSON() if err != nil { err = wrapSkipFunc(err, "marshal method") @@ -194,7 +194,8 @@ func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler { xe := export.Encoder(enc) prevDepth, prevLength := xe.Tokens.DepthLength() xe.Flags.Set(jsonflags.WithinArshalCall | 1) - err := va.Addr().Interface().(MarshalerTo).MarshalJSONTo(enc) + marshaler, _ := reflect.TypeAssert[MarshalerTo](va.Addr()) + err := marshaler.MarshalJSONTo(enc) xe.Flags.Set(jsonflags.WithinArshalCall | 0) currDepth, currLength := xe.Tokens.DepthLength() if (prevDepth != currDepth || prevLength+1 != currLength) && err == nil { @@ -233,7 +234,7 @@ func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler { return newUnmarshalErrorAfter(dec, t, errNonStringValue) } s := jsonwire.UnquoteMayCopy(val, flags.IsVerbatim()) - unmarshaler := va.Addr().Interface().(encoding.TextUnmarshaler) + unmarshaler, _ := reflect.TypeAssert[encoding.TextUnmarshaler](va.Addr()) if err := unmarshaler.UnmarshalText(s); err != nil { err = wrapSkipFunc(err, "unmarshal method") if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) { @@ -260,7 +261,7 @@ func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler { if err != nil { return err // must be a syntactic or I/O error } - unmarshaler := va.Addr().Interface().(Unmarshaler) + unmarshaler, _ := reflect.TypeAssert[Unmarshaler](va.Addr()) if err := unmarshaler.UnmarshalJSON(val); err != nil { err = wrapSkipFunc(err, "unmarshal method") if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) { @@ -284,7 +285,8 @@ func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler { xd := export.Decoder(dec) prevDepth, prevLength := xd.Tokens.DepthLength() xd.Flags.Set(jsonflags.WithinArshalCall | 1) - err := va.Addr().Interface().(UnmarshalerFrom).UnmarshalJSONFrom(dec) + unmarshaler, _ := reflect.TypeAssert[UnmarshalerFrom](va.Addr()) + err := unmarshaler.UnmarshalJSONFrom(dec) xd.Flags.Set(jsonflags.WithinArshalCall | 0) currDepth, currLength := xd.Tokens.DepthLength() if (prevDepth != currDepth || prevLength+1 != currLength) && err == nil { diff --git a/src/encoding/json/v2/arshal_time.go b/src/encoding/json/v2/arshal_time.go index ac29abe5021..70dca8a2943 100644 --- a/src/encoding/json/v2/arshal_time.go +++ b/src/encoding/json/v2/arshal_time.go @@ -57,8 +57,7 @@ func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler { return newMarshalErrorBefore(enc, t, errors.New("no default representation (see https://go.dev/issue/71631); specify an explicit format")) } - // TODO(https://go.dev/issue/62121): Use reflect.Value.AssertTo. - m.td = *va.Addr().Interface().(*time.Duration) + m.td, _ = reflect.TypeAssert[time.Duration](va.Value) k := stringOrNumberKind(!m.isNumeric() || xe.Tokens.Last.NeedObjectName() || mo.Flags.Get(jsonflags.StringifyNumbers)) if err := xe.AppendRaw(k, true, m.appendMarshal); err != nil { if !isSyntacticError(err) && !export.IsIOError(err) { @@ -85,7 +84,7 @@ func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler { stringify := !u.isNumeric() || xd.Tokens.Last.NeedObjectName() || uo.Flags.Get(jsonflags.StringifyNumbers) var flags jsonwire.ValueFlags - td := va.Addr().Interface().(*time.Duration) + td, _ := reflect.TypeAssert[*time.Duration](va.Addr()) val, err := xd.ReadValue(&flags) if err != nil { return err @@ -129,8 +128,7 @@ func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler { } } - // TODO(https://go.dev/issue/62121): Use reflect.Value.AssertTo. - m.tt = *va.Addr().Interface().(*time.Time) + m.tt, _ = reflect.TypeAssert[time.Time](va.Value) k := stringOrNumberKind(!m.isNumeric() || xe.Tokens.Last.NeedObjectName() || mo.Flags.Get(jsonflags.StringifyNumbers)) if err := xe.AppendRaw(k, !m.hasCustomFormat(), m.appendMarshal); err != nil { if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) { @@ -156,7 +154,7 @@ func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler { stringify := !u.isNumeric() || xd.Tokens.Last.NeedObjectName() || uo.Flags.Get(jsonflags.StringifyNumbers) var flags jsonwire.ValueFlags - tt := va.Addr().Interface().(*time.Time) + tt, _ := reflect.TypeAssert[*time.Time](va.Addr()) val, err := xd.ReadValue(&flags) if err != nil { return err