Skip to content

Commit 28273cf

Browse files
committed
Implement legacy semantics for method calling behavior
1 parent 0f3833c commit 28273cf

8 files changed

+175
-49
lines changed

arshal.go

+13-5
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,13 @@ func marshalEncode(out *jsontext.Encoder, in any, mo *jsonopts.Struct) (err erro
211211
}
212212
// Shallow copy non-pointer values to obtain an addressable value.
213213
// It is beneficial to performance to always pass pointers to avoid this.
214-
if v.Kind() != reflect.Pointer {
214+
forceAddr := v.Kind() != reflect.Pointer
215+
if forceAddr {
215216
v2 := reflect.New(v.Type())
216217
v2.Elem().Set(v)
217218
v = v2
218219
}
219-
va := addressableValue{v.Elem()} // dereferenced pointer is always addressable
220+
va := addressableValue{v.Elem(), forceAddr} // dereferenced pointer is always addressable
220221
t := va.Type()
221222

222223
// Lookup and call the marshal function for this type.
@@ -452,7 +453,7 @@ func unmarshalDecode(in *jsontext.Decoder, out any, uo *jsonopts.Struct) (err er
452453
if v.Kind() != reflect.Pointer || v.IsNil() {
453454
return &SemanticError{action: "unmarshal", GoType: reflect.TypeOf(out), Err: internal.ErrNonNilReference}
454455
}
455-
va := addressableValue{v.Elem()} // dereferenced pointer is always addressable
456+
va := addressableValue{v.Elem(), false} // dereferenced pointer is always addressable
456457
t := va.Type()
457458

458459
// Lookup and call the unmarshal function for this type.
@@ -476,11 +477,18 @@ func unmarshalDecode(in *jsontext.Decoder, out any, uo *jsonopts.Struct) (err er
476477
// There is no compile magic that enforces this property,
477478
// but rather the need to construct this type makes it easier to examine each
478479
// construction site to ensure that this property is upheld.
479-
type addressableValue struct{ reflect.Value }
480+
type addressableValue struct {
481+
reflect.Value
482+
483+
// forcedAddr reports whether this value is addressable
484+
// only through the use of [newAddressableValue].
485+
// This is only used for [jsonflags.CallMethodsWithLegacySemantics].
486+
forcedAddr bool
487+
}
480488

481489
// newAddressableValue constructs a new addressable value of type t.
482490
func newAddressableValue(t reflect.Type) addressableValue {
483-
return addressableValue{reflect.New(t).Elem()}
491+
return addressableValue{reflect.New(t).Elem(), true}
484492
}
485493

486494
// All marshal and unmarshal behavior is implemented using these signatures.

arshal_default.go

+59-19
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package json
66

77
import (
88
"bytes"
9+
"encoding"
910
"encoding/base32"
1011
"encoding/base64"
1112
"encoding/hex"
@@ -724,6 +725,8 @@ func makeMapArshaler(t reflect.Type) *arshaler {
724725
keyFncs = lookupArshaler(t.Key())
725726
valFncs = lookupArshaler(t.Elem())
726727
}
728+
nillableLegacyKey := t.Key().Kind() == reflect.Pointer &&
729+
implementsAny(t.Key(), textMarshalerType, textAppenderType)
727730
fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
728731
// Check for cycles.
729732
xe := export.Encoder(enc)
@@ -795,10 +798,16 @@ func makeMapArshaler(t reflect.Type) *arshaler {
795798
k.SetIterKey(iter)
796799
err := marshalKey(enc, k, mo)
797800
if err != nil {
798-
if serr, ok := err.(*jsontext.SyntacticError); ok && serr.Err == jsontext.ErrNonStringName {
799-
err = newMarshalErrorBefore(enc, k.Type(), err)
801+
if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
802+
errors.Is(err, jsontext.ErrNonStringName) && nillableLegacyKey && k.IsNil() {
803+
err = enc.WriteToken(jsontext.String(""))
804+
}
805+
if err != nil {
806+
if serr, ok := err.(*jsontext.SyntacticError); ok && serr.Err == jsontext.ErrNonStringName {
807+
err = newMarshalErrorBefore(enc, k.Type(), err)
808+
}
809+
return err
800810
}
801-
return err
802811
}
803812
v.SetIterValue(iter)
804813
if err := marshalVal(enc, v, mo); err != nil {
@@ -835,16 +844,22 @@ func makeMapArshaler(t reflect.Type) *arshaler {
835844
vals := reflect.MakeSlice(reflect.SliceOf(t.Elem()), n, n)
836845
for i, iter := 0, va.Value.MapRange(); i < n && iter.Next(); i++ {
837846
// Marshal the member name.
838-
k := addressableValue{keys.Index(i)} // indexed slice element is always addressable
847+
k := addressableValue{keys.Index(i), false} // indexed slice element is always addressable
839848
k.SetIterKey(iter)
840-
v := addressableValue{vals.Index(i)} // indexed slice element is always addressable
849+
v := addressableValue{vals.Index(i), false} // indexed slice element is always addressable
841850
v.SetIterValue(iter)
842851
err := marshalKey(enc, k, mo)
843852
if err != nil {
844-
if serr, ok := err.(*jsontext.SyntacticError); ok && serr.Err == jsontext.ErrNonStringName {
845-
err = newMarshalErrorBefore(enc, k.Type(), err)
853+
if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
854+
errors.Is(err, jsontext.ErrNonStringName) && nillableLegacyKey && k.IsNil() {
855+
err = enc.WriteToken(jsontext.String(""))
856+
}
857+
if err != nil {
858+
if serr, ok := err.(*jsontext.SyntacticError); ok && serr.Err == jsontext.ErrNonStringName {
859+
err = newMarshalErrorBefore(enc, k.Type(), err)
860+
}
861+
return err
846862
}
847-
return err
848863
}
849864
name := xe.UnwriteOnlyObjectMemberName()
850865
members[i] = member{name, k, v}
@@ -1027,7 +1042,7 @@ func makeStructArshaler(t reflect.Type) *arshaler {
10271042
xe.Tokens.Last.DisableNamespace() // we manually ensure unique names below
10281043
for i := range fields.flattened {
10291044
f := &fields.flattened[i]
1030-
v := addressableValue{va.Field(f.index[0])} // addressable if struct value is addressable
1045+
v := addressableValue{va.Field(f.index[0]), va.forcedAddr} // addressable if struct value is addressable
10311046
if len(f.index) > 1 {
10321047
v = v.fieldByIndex(f.index[1:], false)
10331048
if !v.IsValid() {
@@ -1256,7 +1271,7 @@ func makeStructArshaler(t reflect.Type) *arshaler {
12561271
uo.FormatDepth = xd.Tokens.Depth()
12571272
uo.Format = f.format
12581273
}
1259-
v := addressableValue{va.Field(f.index[0])} // addressable if struct value is addressable
1274+
v := addressableValue{va.Field(f.index[0]), va.forcedAddr} // addressable if struct value is addressable
12601275
if len(f.index) > 1 {
12611276
v = v.fieldByIndex(f.index[1:], true)
12621277
if !v.IsValid() {
@@ -1286,7 +1301,7 @@ func (va addressableValue) fieldByIndex(index []int, mayAlloc bool) addressableV
12861301
if !va.IsValid() {
12871302
return va
12881303
}
1289-
va = addressableValue{va.Field(i)} // addressable if struct value is addressable
1304+
va = addressableValue{va.Field(i), va.forcedAddr} // addressable if struct value is addressable
12901305
}
12911306
return va
12921307
}
@@ -1299,7 +1314,7 @@ func (va addressableValue) indirect(mayAlloc bool) addressableValue {
12991314
}
13001315
va.Set(reflect.New(va.Type().Elem()))
13011316
}
1302-
va = addressableValue{va.Elem()} // dereferenced pointer is always addressable
1317+
va = addressableValue{va.Elem(), false} // dereferenced pointer is always addressable
13031318
}
13041319
return va
13051320
}
@@ -1402,7 +1417,7 @@ func makeSliceArshaler(t reflect.Type) *arshaler {
14021417
marshal, _ = mo.Marshalers.(*Marshalers).lookup(marshal, t.Elem())
14031418
}
14041419
for i := range n {
1405-
v := addressableValue{va.Index(i)} // indexed slice element is always addressable
1420+
v := addressableValue{va.Index(i), false} // indexed slice element is always addressable
14061421
if err := marshal(enc, v, mo); err != nil {
14071422
return err
14081423
}
@@ -1452,7 +1467,7 @@ func makeSliceArshaler(t reflect.Type) *arshaler {
14521467
va.SetLen(cap)
14531468
mustZero = false // reflect.Value.Grow ensures new capacity is zero-initialized
14541469
}
1455-
v := addressableValue{va.Index(i)} // indexed slice element is always addressable
1470+
v := addressableValue{va.Index(i), false} // indexed slice element is always addressable
14561471
i++
14571472
if mustZero && !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
14581473
v.SetZero()
@@ -1504,7 +1519,7 @@ func makeArrayArshaler(t reflect.Type) *arshaler {
15041519
marshal, _ = mo.Marshalers.(*Marshalers).lookup(marshal, t.Elem())
15051520
}
15061521
for i := range n {
1507-
v := addressableValue{va.Index(i)} // indexed array element is addressable if array is addressable
1522+
v := addressableValue{va.Index(i), va.forcedAddr} // indexed array element is addressable if array is addressable
15081523
if err := marshal(enc, v, mo); err != nil {
15091524
return err
15101525
}
@@ -1545,7 +1560,7 @@ func makeArrayArshaler(t reflect.Type) *arshaler {
15451560
err = errArrayOverflow
15461561
continue
15471562
}
1548-
v := addressableValue{va.Index(i)} // indexed array element is addressable if array is addressable
1563+
v := addressableValue{va.Index(i), va.forcedAddr} // indexed array element is addressable if array is addressable
15491564
if !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
15501565
v.SetZero()
15511566
}
@@ -1599,7 +1614,7 @@ func makePointerArshaler(t reflect.Type) *arshaler {
15991614
if mo.Marshalers != nil {
16001615
marshal, _ = mo.Marshalers.(*Marshalers).lookup(marshal, t.Elem())
16011616
}
1602-
v := addressableValue{va.Elem()} // dereferenced pointer is always addressable
1617+
v := addressableValue{va.Elem(), false} // dereferenced pointer is always addressable
16031618
return marshal(enc, v, mo)
16041619
}
16051620
fncs.unmarshal = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
@@ -1619,7 +1634,7 @@ func makePointerArshaler(t reflect.Type) *arshaler {
16191634
if va.IsNil() {
16201635
va.Set(reflect.New(t.Elem()))
16211636
}
1622-
v := addressableValue{va.Elem()} // dereferenced pointer is always addressable
1637+
v := addressableValue{va.Elem(), false} // dereferenced pointer is always addressable
16231638
if err := unmarshal(dec, v, uo); err != nil {
16241639
return err
16251640
}
@@ -1648,13 +1663,38 @@ func makeInterfaceArshaler(t reflect.Type) *arshaler {
16481663
// store them back into the interface afterwards.
16491664

16501665
var fncs arshaler
1666+
var whichMarshaler reflect.Type
1667+
for _, iface := range allMarshalerTypes {
1668+
if t.Implements(iface) {
1669+
whichMarshaler = t
1670+
break
1671+
}
1672+
}
16511673
fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
16521674
xe := export.Encoder(enc)
16531675
if mo.Format != "" && mo.FormatDepth == xe.Tokens.Depth() {
16541676
return newInvalidFormatError(enc, t, mo.Format)
16551677
}
16561678
if va.IsNil() {
16571679
return enc.WriteToken(jsontext.Null)
1680+
} else if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) && whichMarshaler != nil {
1681+
// The marshaler for a pointer never calls the method on a nil receiver.
1682+
// Wrap the nil pointer within a struct type so that marshal
1683+
// instead appears on a value receiver and may be called.
1684+
if va.Elem().Kind() == reflect.Pointer && va.Elem().IsNil() {
1685+
v2 := newAddressableValue(whichMarshaler)
1686+
switch whichMarshaler {
1687+
case jsonMarshalerV2Type:
1688+
v2.Set(reflect.ValueOf(struct{ MarshalerV2 }{va.Elem().Interface().(MarshalerV2)}))
1689+
case jsonMarshalerV1Type:
1690+
v2.Set(reflect.ValueOf(struct{ MarshalerV1 }{va.Elem().Interface().(MarshalerV1)}))
1691+
case textAppenderType:
1692+
v2.Set(reflect.ValueOf(struct{ encodingTextAppender }{va.Elem().Interface().(encodingTextAppender)}))
1693+
case textMarshalerType:
1694+
v2.Set(reflect.ValueOf(struct{ encoding.TextMarshaler }{va.Elem().Interface().(encoding.TextMarshaler)}))
1695+
}
1696+
va = v2
1697+
}
16581698
}
16591699
v := newAddressableValue(va.Elem().Type())
16601700
v.Set(va.Elem())
@@ -1731,7 +1771,7 @@ func makeInterfaceArshaler(t reflect.Type) *arshaler {
17311771
v = newAddressableValue(stringType)
17321772
case '0':
17331773
if uo.Flags.Get(jsonflags.UnmarshalAnyWithRawNumber) {
1734-
v = addressableValue{reflect.ValueOf(internal.NewRawNumber()).Elem()}
1774+
v = addressableValue{reflect.ValueOf(internal.NewRawNumber()).Elem(), true}
17351775
} else {
17361776
v = newAddressableValue(float64Type)
17371777
}

arshal_inlined.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ var jsontextValueType = reflect.TypeFor[jsontext.Value]()
3535

3636
// marshalInlinedFallbackAll marshals all the members in an inlined fallback.
3737
func marshalInlinedFallbackAll(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct, f *structField, insertUnquotedName func([]byte) bool) error {
38-
v := addressableValue{va.Field(f.index[0])} // addressable if struct value is addressable
38+
v := addressableValue{va.Field(f.index[0]), va.forcedAddr} // addressable if struct value is addressable
3939
if len(f.index) > 1 {
4040
v = v.fieldByIndex(f.index[1:], false)
4141
if !v.IsValid() {
@@ -166,7 +166,7 @@ func marshalInlinedFallbackAll(enc *jsontext.Encoder, va addressableValue, mo *j
166166

167167
// unmarshalInlinedFallbackNext unmarshals only the next member in an inlined fallback.
168168
func unmarshalInlinedFallbackNext(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct, f *structField, quotedName, unquotedName []byte) error {
169-
v := addressableValue{va.Field(f.index[0])} // addressable if struct value is addressable
169+
v := addressableValue{va.Field(f.index[0]), va.forcedAddr} // addressable if struct value is addressable
170170
if len(f.index) > 1 {
171171
v = v.fieldByIndex(f.index[1:], true)
172172
}

0 commit comments

Comments
 (0)