From e8e3bad5b2852c53ff6771dc42f584fcd5ed7215 Mon Sep 17 00:00:00 2001 From: "duanyi.aster" Date: Sat, 29 Oct 2022 16:56:17 +0800 Subject: [PATCH] feat:(decoder) support skip mismatche-typed value --- decoder/assembler_amd64_go117.go | 65 +++++++++++++++++++++++++++++++- decoder/assembler_test.go | 32 ++++++++++++++++ decoder/compiler.go | 42 +++++++++++++++++++++ decoder/errors.go | 13 ++++++- decoder/types.go | 1 + 5 files changed, 149 insertions(+), 4 deletions(-) diff --git a/decoder/assembler_amd64_go117.go b/decoder/assembler_amd64_go117.go index a8f79f1e4..a09473edc 100644 --- a/decoder/assembler_amd64_go117.go +++ b/decoder/assembler_amd64_go117.go @@ -70,7 +70,7 @@ const ( _FP_args = 72 // 72 bytes to pass and spill register arguements _FP_fargs = 80 // 80 bytes for passing arguments to other Go functions _FP_saves = 48 // 48 bytes for saving the registers before CALL instructions - _FP_locals = 120 // 112 bytes for local variables + _FP_locals = 136 // 136 bytes for local variables ) const ( @@ -191,6 +191,11 @@ var ( var _VAR_fl = jit.Ptr(_SP, _FP_fargs + _FP_saves + 112) +var ( + _VAR_et = jit.Ptr(_SP, _FP_fargs + _FP_saves + 120) // save dismatched type + _VAR_ic = jit.Ptr(_SP, _FP_fargs + _FP_saves + 128) // save dismatched position +) + type _Assembler struct { jit.BaseAssembler p _Program @@ -290,6 +295,11 @@ var _OpFuncTab = [256]func(*_Assembler, *_Instr) { _OP_recurse : (*_Assembler)._asm_OP_recurse, _OP_goto : (*_Assembler)._asm_OP_goto, _OP_switch : (*_Assembler)._asm_OP_switch, + _OP_check_bool : (*_Assembler)._asm_OP_check_bool, + _OP_check_bytes : (*_Assembler)._asm_OP_check_bytes, + _OP_check_num : (*_Assembler)._asm_OP_check_num, + _OP_check_char_0 : (*_Assembler)._asm_OP_check_char_0, + _OP_dismatch_err : (*_Assembler)._asm_OP_dismatch_err, } func (self *_Assembler) instr(v *_Instr) { @@ -310,8 +320,10 @@ func (self *_Assembler) instrs() { func (self *_Assembler) epilogue() { self.Mark(len(self.p)) - self.Emit("XORL", _ET, _ET) // XORL ET, ET self.Emit("XORL", _EP, _EP) // XORL EP, EP + self.Emit("MOVQ", _VAR_et, _ET) // MOVQ VAR_et, ET + self.Emit("TESTQ", _ET, _ET) // TESTQ ET, ET + self.Sjmp("JNZ", _LB_type_error) // JZ _LB_error self.Link(_LB_error) // _error: self.Emit("MOVQ", _EP, _CX) // MOVQ BX, CX self.Emit("MOVQ", _ET, _BX) // MOVQ AX, BX @@ -343,6 +355,7 @@ func (self *_Assembler) prologue() { self.Emit("MOVQ", jit.Imm(0), _VAR_sv_p) // MOVQ $0, sv.p<>+48(FP) self.Emit("MOVQ", jit.Imm(0), _VAR_sv_n) // MOVQ $0, sv.n<>+56(FP) self.Emit("MOVQ", jit.Imm(0), _VAR_vk) // MOVQ $0, vk<>+64(FP) + self.Emit("MOVQ", jit.Imm(0), _VAR_et) // MOVQ $0, et<>+120(FP) // initialize digital buffer first self.Emit("MOVQ", jit.Imm(_MaxDigitNums), _VAR_st_Dc) // MOVQ $_MaxDigitNums, ss.Dcap self.Emit("LEAQ", jit.Ptr(_ST, _DbufOffset), _AX) // LEAQ _DbufOffset(ST), AX @@ -450,6 +463,7 @@ var ( func (self *_Assembler) type_error() { self.Link(_LB_type_error) // _type_error: + self.Emit("MOVBLZX", jit.Sib(_IP, _IC, 1, 0), _BX) // MOVBLZX 0(IP)(IC*1), BX self.call_go(_F_error_type) // CALL_GO error_type self.Sjmp("JMP" , _LB_error) // JMP _error } @@ -1616,6 +1630,53 @@ func (self *_Assembler) _asm_OP_check_char(p *_Instr) { self.Xjmp("JE" , p.vi()) // JE {p.vi()} } +func (self *_Assembler) _asm_OP_check_char_0(p *_Instr) { + self.check_eof(1) + self.Emit("CMPB", jit.Sib(_IP, _IC, 1, 0), jit.Imm(int64(p.vb()))) // CMPB (IP)(IC), ${p.vb()} + self.Xjmp("JE" , p.vi()) // JE {p.vi()} +} + +func (self *_Assembler) _asm_OP_check_bool(p *_Instr) { + self.check_eof(1) + self.Emit("MOVBLZX", jit.Sib(_IP, _IC, 1, 0), _AX) + self.Emit("CMPB", _AX, jit.Imm(int64('f'))) + self.Xjmp("JE" , p.vi()) + self.Emit("CMPB", _AX, jit.Imm(int64('t'))) + self.Xjmp("JE" , p.vi()) +} + +func (self *_Assembler) _asm_OP_check_bytes(p *_Instr) { + self.check_eof(1) + self.Emit("MOVBLZX", jit.Sib(_IP, _IC, 1, 0), _AX) + self.Emit("CMPB", _AX, jit.Imm(int64('"'))) + self.Xjmp("JE" , p.vi()) + self.Emit("CMPB", _AX, jit.Imm(int64('['))) + self.Xjmp("JE" , p.vi()) +} + +func (self *_Assembler) _asm_OP_check_num(p *_Instr) { + self.check_eof(1) + self.Emit("MOVBLZX", jit.Sib(_IP, _IC, 1, 0), _AX) + b := p.vb() + if b == 2 { // json.Number + self.Emit("CMPB", _AX, jit.Imm(int64('"'))) + self.Xjmp("JE" , p.vi()) + } + if b == 1 { // negative number + self.Emit("CMPB", _AX, jit.Imm(int64('-'))) + self.Xjmp("JE" , p.vi()) + } + self.Emit("LEAL", jit.Ptr(_AX, -int64('0')), _CX) + self.Emit("CMPB", _CX, jit.Imm(int64('9'-'0'))) + self.Xjmp("JLS" , p.vi()) +} + +func (self *_Assembler) _asm_OP_dismatch_err(p *_Instr) { + self.Emit("MOVQ", _IC, _VAR_ic) + self.Emit("MOVQ", jit.Type(p.vt()), _AX) + self.Emit("MOVQ", _AX, _VAR_et) +} + func (self *_Assembler) _asm_OP_load(_ *_Instr) { self.Emit("MOVQ", jit.Ptr(_ST, 0), _AX) // MOVQ (ST), AX self.Emit("MOVQ", jit.Sib(_ST, _AX, 1, 0), _VP) // MOVQ (ST)(AX), VP diff --git a/decoder/assembler_test.go b/decoder/assembler_test.go index 69c757760..788726825 100644 --- a/decoder/assembler_test.go +++ b/decoder/assembler_test.go @@ -31,6 +31,38 @@ import ( `github.com/stretchr/testify/require` ) +func TestSkipError(t *testing.T) { + type skiptype struct { + A int `json:"a"` + B string `json:"b"` + + Pass int `json:"pass"` + + C struct{ + D struct{ + E float32 `json:"e"` + } `json:"d"` + + Pass int `json:"pass"` + + } `json:"c"` + E bool `json:"e"` + F []int `json:"f"` + G map[string]int `json:"g"` + + Pass2 int `json:"pass2"` + } + var obj, obj2 = &skiptype{}, &skiptype{} + var data = `{"a":"","b":1,"c":{"d":true,"pass":1},"e":{},"f":"","g":[],"pass":1,"pass2":1}` + d := NewDecoder(data) + err := d.Decode(obj) + // println("decoder out: ", err.Error()) + err2 := json.Unmarshal([]byte(data), obj2) + assert.Equal(t, err2 == nil, err == nil) + // assert.Equal(t, len(data), d.i) + assert.Equal(t, obj2, obj) +} + func TestAssembler_PrologueAndEpilogue(t *testing.T) { a := newAssembler(nil) _, e := a.Load()("", 0, nil, nil, 0, "", nil) diff --git a/decoder/compiler.go b/decoder/compiler.go index b18e9382a..8f4977c21 100644 --- a/decoder/compiler.go +++ b/decoder/compiler.go @@ -94,6 +94,11 @@ const ( _OP_recurse _OP_goto _OP_switch + _OP_check_bool + _OP_check_bytes + _OP_check_num + _OP_check_char_0 + _OP_dismatch_err ) const ( @@ -559,6 +564,8 @@ func (self *_Compiler) compileOne(p *_Program, sp int, vt reflect.Type) { } func (self *_Compiler) compileOps(p *_Program, sp int, vt reflect.Type) { + // check first char mathes the type + self.checkType(p, vt) switch vt.Kind() { case reflect.Bool : self.compilePrimitive (p, _OP_bool) case reflect.Int : self.compilePrimitive (p, _OP_int()) @@ -893,6 +900,41 @@ end_of_object: p.pin(n) } +func (self *_Compiler) checkType(p *_Program, vt reflect.Type) { + if k := vt.Kind(); k == reflect.Ptr { + self.checkType(p, vt.Elem()) + return + } else if k == reflect.Interface { + return + } else { + x := p.pc() + switch vt.Kind() { + case reflect.Bool : p.add(_OP_check_bool) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float32, reflect.Float64: p.chr(_OP_check_num, 1) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: p.chr(_OP_check_num, 0) + case reflect.String : + if vt == jsonNumberType { + p.chr(_OP_check_num, 2) + }else { + p.chr(_OP_check_char_0, '"') + } + case reflect.Array : p.chr(_OP_check_char_0, '[') + case reflect.Map : p.chr(_OP_check_char_0, '{') + case reflect.Slice : + if vt == bytesType { + p.chr(_OP_check_bytes, '"') + } else { + p.chr(_OP_check_char_0, '[') + } + case reflect.Struct : p.chr(_OP_check_char_0, '{') + default : panic(&json.UnmarshalTypeError{Type: vt}) + } + p.rtt(_OP_dismatch_err, vt) + p.add(_OP_object_next) + p.pin(x) + } +} + func (self *_Compiler) compileStructFieldStr(p *_Program, sp int, vt reflect.Type) { n1 := -1 ft := vt diff --git a/decoder/errors.go b/decoder/errors.go index 24d4caed2..1c54897ef 100644 --- a/decoder/errors.go +++ b/decoder/errors.go @@ -113,8 +113,17 @@ func error_wrap(src string, pos int, code types.ParsingError) error { } //go:nosplit -func error_type(vt *rt.GoType) error { - return &json.UnmarshalTypeError{Type: vt.Pack()} +func error_type(vt *rt.GoType, c byte) error { + var val string + switch c { + case 'f': fallthrough + case 't': val = "bool" + case '"': val = "string" + case '{': val = "object" + case '[': val = "array" + case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val = "number" + } + return &json.UnmarshalTypeError{Type: vt.Pack(), Value: val} } //go:nosplit diff --git a/decoder/types.go b/decoder/types.go index cab01a51f..400d9d09f 100644 --- a/decoder/types.go +++ b/decoder/types.go @@ -28,6 +28,7 @@ import ( var ( byteType = reflect.TypeOf(byte(0)) + bytesType = reflect.TypeOf([]byte(nil)) jsonNumberType = reflect.TypeOf(json.Number("")) base64CorruptInputError = reflect.TypeOf(base64.CorruptInputError(0)) )