From 4158fffe386c9fb6df6d2c2c10da382b26752c6d Mon Sep 17 00:00:00 2001 From: "duanyi.aster" Date: Thu, 1 Aug 2024 20:23:11 +0800 Subject: [PATCH] encoder --- go1.23 | 1 - internal/encoder/compiler.go | 25 ++++++++--- issue_test/race_test_go | 50 --------------------- issue_test/xx/xx_test.go | 86 ++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 56 deletions(-) delete mode 160000 go1.23 delete mode 100644 issue_test/race_test_go create mode 100644 issue_test/xx/xx_test.go diff --git a/go1.23 b/go1.23 deleted file mode 160000 index c9940fe2a..000000000 --- a/go1.23 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c9940fe2a9f2eb77327efca860abfbae8d94bf28 diff --git a/internal/encoder/compiler.go b/internal/encoder/compiler.go index 034e1d17d..902fbc98b 100644 --- a/internal/encoder/compiler.go +++ b/internal/encoder/compiler.go @@ -127,31 +127,40 @@ func (self *Compiler) compileOne(p *ir.Program, sp int, vt reflect.Type, pv bool } } -func (self *Compiler) compileRec(p *ir.Program, sp int, vt reflect.Type, pv bool) { - pr := self.pv +func (self *Compiler) tryCompileMarshaler(p *ir.Program, vt reflect.Type, pv bool) bool { pt := reflect.PtrTo(vt) /* check for addressable `json.Marshaler` with pointer receiver */ if pv && pt.Implements(vars.JsonMarshalerType) { addMarshalerOp(p, ir.OP_marshal_p, pt, vars.JsonMarshalerType) - return + return true } /* check for `json.Marshaler` */ if vt.Implements(vars.JsonMarshalerType) { self.compileMarshaler(p, ir.OP_marshal, vt, vars.JsonMarshalerType) - return + return true } /* check for addressable `encoding.TextMarshaler` with pointer receiver */ if pv && pt.Implements(vars.EncodingTextMarshalerType) { addMarshalerOp(p, ir.OP_marshal_text_p, pt, vars.EncodingTextMarshalerType) - return + return true } /* check for `encoding.TextMarshaler` */ if vt.Implements(vars.EncodingTextMarshalerType) { self.compileMarshaler(p, ir.OP_marshal_text, vt, vars.EncodingTextMarshalerType) + return true + } + + return false +} + +func (self *Compiler) compileRec(p *ir.Program, sp int, vt reflect.Type, pv bool) { + pr := self.pv + + if self.tryCompileMarshaler(p, vt, pv) { return } @@ -485,6 +494,12 @@ func (self *Compiler) compileStructBody(p *ir.Program, sp int, vt reflect.Type) } func (self *Compiler) compileStructFieldStr(p *ir.Program, sp int, vt reflect.Type) { + // NOTICE: according to encoding/json, Marshaler type has higher priority than string option + // see issue: + if self.tryCompileMarshaler(p, vt, self.pv) { + return + } + pc := -1 ft := vt sv := false diff --git a/issue_test/race_test_go b/issue_test/race_test_go deleted file mode 100644 index 2830cdef4..000000000 --- a/issue_test/race_test_go +++ /dev/null @@ -1,50 +0,0 @@ - -//go:build race -// +build race - -/* - * Copyright 2024 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 issue_test - -import ( - `github.com/bytedance/sonic` - `testing` -) - -type MyFoo struct { - List []*int64 -} - -func TestRaceEncode(t *testing.T) { - f := &MyFoo{ - List: []*int64{new(int64), new(int64)}, - } - - go func() { - f.List = []*int64{new(int64), new(int64)} - }() - - go func() { - sonic.Marshal(f) - }() - - // encoding/json always detect data race when enabling `-race` here - // go func() { - // json.Marshal(f) - // }() -} diff --git a/issue_test/xx/xx_test.go b/issue_test/xx/xx_test.go new file mode 100644 index 000000000..84e1cfde9 --- /dev/null +++ b/issue_test/xx/xx_test.go @@ -0,0 +1,86 @@ +// Copyright 2024 CloudWeGo Authors +// +// 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 +// +// https://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 issue_test + +import ( + "encoding/json" + "fmt" + "testing" + "time" + + "github.com/bytedance/sonic" + "github.com/stretchr/testify/assert" +) + +func TestX(t *testing.T) { + // var obj = Issue670Case{ D: Date(time.Now().Unix()) } + // so, _ := sonic.MarshalString(obj) + // eo, _ := json.Marshal(obj) + // assert.Equal(t, string(eo), so) + // println(string(eo)) + + // match + eo := []byte(`{"D":"2021-08-26","E":1}`) + testUnmarshal(t, eo) + + // // mismatch + // eo = []byte(`{"D":11,"E":1}`) + // testUnmarshal(t, eo) + + // // null + // eo = []byte(`{"D":null,"E":1}`) + // testUnmarshal(t, eo) +} + +func testUnmarshal(t *testing.T, eo []byte) { + obj := Issue670Case{} + println(string(eo)) + println("sonic") + es := sonic.Unmarshal(eo, &obj) + obj2 := Issue670Case{} + println("std") + ee := json.Unmarshal(eo, &obj2) + assert.Equal(t, ee ==nil, es == nil, es) + assert.Equal(t, obj2, obj) + fmt.Printf("error: %v, obj: %#v", ee, obj2) +} + +type Issue670Case struct { + D Date `form:"D" json:"D,string" query:"D"` + E int +} + +type Date int64 + +func (d Date) MarshalJSON() ([]byte, error) { + if d == 0 { + return []byte("null"), nil + } + return []byte(fmt.Sprintf("\"%s\"", time.Unix(int64(d), 0).Format("2006-01-02"))), nil +} + +func (d *Date) UnmarshalJSON(in []byte) error { + if string(in) == "null" { + *d = 0 + return nil + } + println("hook ", string(in)) + t, err := time.Parse("2006-01-02", string(in)) + if err != nil { + return err + } + *d = Date(t.Unix()) + return nil +}