diff --git a/encoder/compiler.go b/encoder/compiler.go index a8c3c3c..7be2763 100644 --- a/encoder/compiler.go +++ b/encoder/compiler.go @@ -416,7 +416,7 @@ func (self *_Compiler) compileRec(p *_Program, sp int, vt reflect.Type, pv bool) /* check for `json.Marshaler` */ if vt.Implements(jsonMarshalerType) { - p.rtt(_OP_marshal, vt) + self.compileMarshaler(p, _OP_marshal, vt, jsonMarshalerType) return } @@ -428,7 +428,7 @@ func (self *_Compiler) compileRec(p *_Program, sp int, vt reflect.Type, pv bool) /* check for `encoding.TextMarshaler` */ if vt.Implements(encodingTextMarshalerType) { - p.rtt(_OP_marshal_text, vt) + self.compileMarshaler(p, _OP_marshal_text, vt, encodingTextMarshalerType) return } @@ -820,3 +820,23 @@ func (self *_Compiler) compileInterface(p *_Program, vt reflect.Type) { p.add(_OP_null) p.pin(e) } + +func (self *_Compiler) compileMarshaler(p *_Program, op _Op, vt reflect.Type, mt reflect.Type) { + pc := p.pc() + vk := vt.Kind() + + /* direct receiver */ + if vk != reflect.Ptr || !vt.Elem().Implements(mt) { + p.rtt(op, vt) + return + } + + /* value receiver with a pointer type, check for nil before calling the marshaler */ + p.add(_OP_is_nil) + p.rtt(op, vt) + i := p.pc() + p.add(_OP_goto) + p.pin(pc) + p.add(_OP_null) + p.pin(i) +} diff --git a/issue58_test.go b/issue58_test.go new file mode 100644 index 0000000..d0e0007 --- /dev/null +++ b/issue58_test.go @@ -0,0 +1,41 @@ +/* + * Copyright 2021 ByteDance Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sonic + +import ( + `testing` + + `github.com/stretchr/testify/require` +) + +type ( + Issue58ValueReceiver struct {} + Issue58PointerReceiver struct {} +) + +func (_ Issue58ValueReceiver) MarshalJSON() ([]byte, error) { return []byte(`"value"`), nil } +func (_ *Issue58PointerReceiver) MarshalJSON() ([]byte, error) { return []byte(`"pointer"`), nil } + +func TestIssue58_NilPointerOnValueMethod(t *testing.T) { + v := struct { + X *Issue58ValueReceiver + Y *Issue58PointerReceiver + }{} + buf, err := Marshal(v) + require.NoError(t, err) + require.Equal(t, []byte(`{"X":null,"Y":"pointer"}`), buf) +}