diff --git a/encoder/assembler_amd64_go116.go b/encoder/assembler_amd64_go116.go index 9455ed8e1..a03e09ca6 100644 --- a/encoder/assembler_amd64_go116.go +++ b/encoder/assembler_amd64_go116.go @@ -106,6 +106,7 @@ const ( _LB_error_too_deep = "_error_too_deep" _LB_error_invalid_number = "_error_invalid_number" _LB_error_nan_or_infinite = "_error_nan_or_infinite" + _LB_panic = "_panic" ) var ( @@ -273,6 +274,7 @@ func (self *_Assembler) builtins() { self.error_too_deep() self.error_invalid_number() self.error_nan_or_infinite() + self.go_panic() } func (self *_Assembler) epilogue() { @@ -625,12 +627,24 @@ func (self *_Assembler) error_nan_or_infinite() { var ( _F_quote = jit.Imm(int64(native.S_quote)) + _F_panic = jit.Func(goPanic) ) +func (self *_Assembler) go_panic() { + self.Link(_LB_panic) + self.Emit("MOVQ", _SP_p, jit.Ptr(_SP, 8)) + self.call_go(_F_panic) +} + func (self *_Assembler) encode_string(doubleQuote bool) { self.Emit("MOVQ" , jit.Ptr(_SP_p, 8), _AX) // MOVQ 8(SP.p), AX self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX self.Sjmp("JZ" , "_str_empty_{n}") // JZ _str_empty_{n} + self.Emit("CMPQ", jit.Ptr(_SP_p, 0), jit.Imm(0)) + self.Sjmp("JNE" , "_str_next_{n}") + self.Emit("MOVQ", jit.Imm(int64(panicNilPointerOfNonEmptyString)), jit.Ptr(_SP, 0)) + self.Sjmp("JMP", _LB_panic) + self.Link("_str_next_{n}") /* openning quote, check for double quote */ if !doubleQuote { @@ -865,10 +879,15 @@ func (self *_Assembler) _asm_OP_quote(_ *_Instr) { } func (self *_Assembler) _asm_OP_number(_ *_Instr) { - self.Emit("MOVQ" , jit.Ptr(_SP_p, 0), _AX) // MOVQ (SP.p), AX self.Emit("MOVQ" , jit.Ptr(_SP_p, 8), _CX) // MOVQ (SP.p), CX self.Emit("TESTQ", _CX, _CX) // TESTQ CX, CX self.Sjmp("JZ" , "_empty_{n}") // JZ _empty_{n} + self.Emit("MOVQ" , jit.Ptr(_SP_p, 0), _AX) // MOVQ (SP.p), AX + self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX + self.Sjmp("JNZ" , "_number_next_{n}") + self.Emit("MOVQ", jit.Imm(int64(panicNilPointerOfNonEmptyString)), jit.Ptr(_SP, 0)) + self.Sjmp("JMP", _LB_panic) + self.Link("_number_next_{n}") self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP) self.Emit("MOVQ" , _CX, jit.Ptr(_SP, 8)) // MOVQ CX, 8(SP) self.call_go(_F_isValidNumber) // CALL_GO isValidNumber diff --git a/encoder/assembler_amd64_go117.go b/encoder/assembler_amd64_go117.go index 7d59be575..331b742f3 100644 --- a/encoder/assembler_amd64_go117.go +++ b/encoder/assembler_amd64_go117.go @@ -107,6 +107,7 @@ const ( _LB_error_too_deep = "_error_too_deep" _LB_error_invalid_number = "_error_invalid_number" _LB_error_nan_or_infinite = "_error_nan_or_infinite" + _LB_panic = "_panic" ) var ( @@ -277,6 +278,7 @@ func (self *_Assembler) builtins() { self.error_too_deep() self.error_invalid_number() self.error_nan_or_infinite() + self.go_panic() } func (self *_Assembler) epilogue() { @@ -636,12 +638,24 @@ func (self *_Assembler) error_nan_or_infinite() { var ( _F_quote = jit.Imm(int64(native.S_quote)) + _F_panic = jit.Func(goPanic) ) -func (self *_Assembler) encode_string(doubleQuote bool) { +func (self *_Assembler) go_panic() { + self.Link(_LB_panic) + self.Emit("MOVQ", _SP_p, _BX) + self.call_go(_F_panic) +} + +func (self *_Assembler) encode_string(doubleQuote bool) { self.Emit("MOVQ" , jit.Ptr(_SP_p, 8), _AX) // MOVQ 8(SP.p), AX self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX self.Sjmp("JZ" , "_str_empty_{n}") // JZ _str_empty_{n} + self.Emit("CMPQ", jit.Ptr(_SP_p, 0), jit.Imm(0)) + self.Sjmp("JNE" , "_str_next_{n}") + self.Emit("MOVQ", jit.Imm(int64(panicNilPointerOfNonEmptyString)), _AX) + self.Sjmp("JMP", _LB_panic) + self.Link("_str_next_{n}") /* openning quote, check for double quote */ if !doubleQuote { @@ -877,10 +891,15 @@ func (self *_Assembler) _asm_OP_quote(_ *_Instr) { } func (self *_Assembler) _asm_OP_number(_ *_Instr) { - self.Emit("MOVQ" , jit.Ptr(_SP_p, 0), _AX) // MOVQ (SP.p), AX self.Emit("MOVQ" , jit.Ptr(_SP_p, 8), _BX) // MOVQ (SP.p), BX self.Emit("TESTQ", _BX, _BX) // TESTQ BX, BX self.Sjmp("JZ" , "_empty_{n}") + self.Emit("MOVQ" , jit.Ptr(_SP_p, 0), _AX) // MOVQ (SP.p), AX + self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX + self.Sjmp("JNZ" , "_number_next_{n}") + self.Emit("MOVQ", jit.Imm(int64(panicNilPointerOfNonEmptyString)), _AX) + self.Sjmp("JMP", _LB_panic) + self.Link("_number_next_{n}") self.call_go(_F_isValidNumber) // CALL_GO isValidNumber self.Emit("CMPB" , _AX, jit.Imm(0)) // CMPB AX, $0 self.Sjmp("JE" , _LB_error_invalid_number) // JE _error_invalid_number diff --git a/encoder/assembler_test.go b/encoder/assembler_test.go index 79a592bc1..eb9952b03 100644 --- a/encoder/assembler_test.go +++ b/encoder/assembler_test.go @@ -17,17 +17,18 @@ package encoder import ( - `encoding/hex` - `encoding/json` - `math` - `reflect` - `runtime` - `testing` - `unsafe` + "encoding/hex" + "encoding/json" + "math" + "reflect" + "runtime" + "strings" + "testing" + "unsafe" - `github.com/bytedance/sonic/internal/rt` - `github.com/davecgh/go-spew/spew` - `github.com/stretchr/testify/assert` + "github.com/bytedance/sonic/internal/rt" + "github.com/davecgh/go-spew/spew" + "github.com/stretchr/testify/assert" ) func TestAssembler_CompileAndLoad(t *testing.T) { @@ -375,3 +376,37 @@ func TestAssembler_TwitterJSON_Structure(t *testing.T) { println(string(m)) runtime.KeepAlive(s) } + +func TestScratchedString(t *testing.T) { + fatal := *(*string)(unsafe.Pointer(&rt.GoString{nil, 1})) + defer func(){ + if v := recover(); v == nil { + t.Fatal() + } else if s, ok := v.(string); !ok { + t.Fatal(v) + }else{ + if !strings.Contains(s, "has nil pointer while its length is not zero") { + t.Fatal(s) + } + } + }() + _, _ = Encode(fatal, 0) + t.Fatal() +} + +func TestScratchedNumber(t *testing.T) { + fatal := *(*json.Number)(unsafe.Pointer(&rt.GoString{nil, 1})) + defer func(){ + if v := recover(); v == nil { + t.Fatal() + } else if s, ok := v.(string); !ok { + t.Fatal(v) + }else{ + if !strings.Contains(s, "has nil pointer while its length is not zero") { + t.Fatal(s) + } + } + }() + _, _ = Encode(fatal, 0) + t.Fatal() +} \ No newline at end of file diff --git a/encoder/errors.go b/encoder/errors.go index 9c278883c..ac6848a5b 100644 --- a/encoder/errors.go +++ b/encoder/errors.go @@ -21,6 +21,9 @@ import ( `fmt` `reflect` `strconv` + `unsafe` + + `github.com/bytedance/sonic/internal/rt` ) var _ERR_too_deep = &json.UnsupportedValueError { @@ -47,3 +50,16 @@ func error_number(number json.Number) error { func error_marshaler(ret []byte, pos int) error { return fmt.Errorf("invalid Marshaler output json syntax at %d: %q", pos, ret) } + +const ( + panicNilPointerOfNonEmptyString int = 1 + iota +) + +func goPanic(code int, val unsafe.Pointer) { + switch(code){ + case panicNilPointerOfNonEmptyString: + panic(fmt.Sprintf("val: %#v has nil pointer while its length is not zero!", (*rt.GoString)(val))) + default: + panic("encoder error!") + } +} \ No newline at end of file