From f6d91525b2492c0d6278700728338b09fa9290c4 Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Thu, 25 Nov 2021 13:10:01 +0900 Subject: [PATCH] work cover map and slice --- internal/encoder/code.go | 153 ++++++++++++++-------------- internal/encoder/compiler.go | 11 +- internal/encoder/compiler_norace.go | 4 + internal/encoder/compiler_race.go | 4 + internal/encoder/context.go | 4 + test/cover/cover_map_test.go | 29 +++--- test/cover/cover_slice_test.go | 11 +- 7 files changed, 120 insertions(+), 96 deletions(-) diff --git a/internal/encoder/code.go b/internal/encoder/code.go index ce91714..67be30e 100644 --- a/internal/encoder/code.go +++ b/internal/encoder/code.go @@ -228,8 +228,8 @@ func (c *SliceCode) ToOpcode(ctx *compileContext) Opcodes { size := c.typ.Elem().Size() header := newSliceHeaderCode(ctx) ctx.incIndex() - codes := c.value.ToOpcode(ctx) - elemCode := newSliceElemCode(ctx.withType(c.typ.Elem()).incIndent(), header, size) + codes := c.value.ToOpcode(ctx.incIndent()) + elemCode := newSliceElemCode(ctx.withType(c.typ.Elem()), header, size) ctx.incIndex() end := newOpCode(ctx, OpSliceEnd) ctx.incIndex() @@ -292,7 +292,6 @@ func (c *MapCode) ToOpcode(ctx *compileContext) Opcodes { // header => code => value => code => key => code => value => code => end // ^ | // |_______________________| - ctx = ctx.incIndent() header := newMapHeaderCode(ctx) ctx.incIndex() @@ -300,13 +299,11 @@ func (c *MapCode) ToOpcode(ctx *compileContext) Opcodes { value := newMapValueCode(ctx, header) ctx.incIndex() - valueCodes := c.value.ToOpcode(ctx) + valueCodes := c.value.ToOpcode(ctx.incIndent()) key := newMapKeyCode(ctx, header) ctx.incIndex() - ctx = ctx.decIndent() - end := newMapEndCode(ctx, header) ctx.incIndex() @@ -340,14 +337,11 @@ func (c *StructCode) ToOpcode(ctx *compileContext) Opcodes { // header => code => structField => code => end // ^ | // |__________| - var recursive *Opcode - compiled := &CompiledCode{} if c.isRecursive { - recursive = newRecursiveCode(ctx, compiled) + recursive := newRecursiveCode(ctx, &CompiledCode{}) + recursive.Type = c.typ ctx.incIndex() - } - if len(c.recursiveCodes) > 0 { - c.linkRecursiveCode(compiled, c.recursiveCodes) + *ctx.recursiveCodes = append(*ctx.recursiveCodes, recursive) return Opcodes{recursive} } codes := Opcodes{} @@ -389,12 +383,27 @@ func (c *StructCode) ToOpcode(ctx *compileContext) Opcodes { } codes = append(codes, fieldCodes...) } - ctx = ctx.decIndent() - if c.isRecursive { - c.recursiveCodes = codes - c.linkRecursiveCode(compiled, c.recursiveCodes) - return Opcodes{recursive} + if len(codes) == 0 { + end := &Opcode{ + Op: OpStructEnd, + Idx: opcodeOffset(ctx.ptrIndex), + DisplayIdx: ctx.opcodeIndex, + Indent: ctx.indent, + } + head := &Opcode{ + Op: OpStructHead, + Idx: opcodeOffset(ctx.ptrIndex), + NextField: end, + Type: c.typ, + DisplayIdx: ctx.opcodeIndex, + Indent: ctx.indent, + } + codes = append(codes, head, end) + end.PrevField = head + ctx.incIndex() } + ctx = ctx.decIndent() + ctx.structTypeToCodes[uintptr(unsafe.Pointer(c.typ))] = codes return codes } @@ -402,14 +411,11 @@ func (c *StructCode) ToAnonymousOpcode(ctx *compileContext) Opcodes { // header => code => structField => code => end // ^ | // |__________| - var recursive *Opcode - compiled := &CompiledCode{} if c.isRecursive { - recursive = newRecursiveCode(ctx, compiled) + recursive := newRecursiveCode(ctx, &CompiledCode{}) + recursive.Type = c.typ ctx.incIndex() - } - if len(c.recursiveCodes) > 0 { - c.linkRecursiveCode(compiled, c.recursiveCodes) + *ctx.recursiveCodes = append(*ctx.recursiveCodes, recursive) return Opcodes{recursive} } codes := Opcodes{} @@ -440,36 +446,35 @@ func (c *StructCode) ToAnonymousOpcode(ctx *compileContext) Opcodes { prevField = fieldCodes.First() codes = append(codes, fieldCodes...) } - if c.isRecursive { - c.recursiveCodes = codes - c.linkRecursiveCode(compiled, c.recursiveCodes) - return Opcodes{recursive} - } return codes } -func (c *StructCode) linkRecursiveCode(compiled *CompiledCode, codes Opcodes) { - compiled.Code = copyOpcode(codes.First()) - code := compiled.Code - code.End.Next = newEndOp(&compileContext{}) - code.Op = code.Op.PtrHeadToHead() +func linkRecursiveCode2(ctx *compileContext) { + for _, recursive := range *ctx.recursiveCodes { + typeptr := uintptr(unsafe.Pointer(recursive.Type)) + codes := ctx.structTypeToCodes[typeptr] + compiled := recursive.Jmp + compiled.Code = copyOpcode(codes.First()) + code := compiled.Code + code.End.Next = newEndOp(&compileContext{}) + code.Op = code.Op.PtrHeadToHead() - beforeLastCode := code.End - lastCode := beforeLastCode.Next + beforeLastCode := code.End + lastCode := beforeLastCode.Next - lastCode.Idx = beforeLastCode.Idx + uintptrSize - lastCode.ElemIdx = lastCode.Idx + uintptrSize - lastCode.Length = lastCode.Idx + 2*uintptrSize + lastCode.Idx = beforeLastCode.Idx + uintptrSize + lastCode.ElemIdx = lastCode.Idx + uintptrSize + lastCode.Length = lastCode.Idx + 2*uintptrSize + code.End.Next.Op = OpRecursiveEnd - // extend length to alloc slot for elemIdx + length - totalLength := uintptr((codes.Last().MaxIdx() / uintptrSize) + 3) - nextTotalLength := uintptr(code.TotalLength() + 3) + // extend length to alloc slot for elemIdx + length + totalLength := uintptr(recursive.TotalLength()) + 3 + nextTotalLength := uintptr(codes.First().TotalLength()) + 3 - code.End.Next.Op = OpRecursiveEnd - - compiled.CurLen = totalLength - compiled.NextLen = nextTotalLength - compiled.Linked = true + compiled.CurLen = totalLength + compiled.NextLen = nextTotalLength + compiled.Linked = true + } } func (c *StructCode) removeFieldsByTags(tags runtime.StructTags) { @@ -567,9 +572,9 @@ func (c *StructFieldCode) ToOpcode(ctx *compileContext, isFirstField, isEndField ctx.incIndex() var codes Opcodes if c.isAnonymous { - codes = c.value.(AnonymousCode).ToAnonymousOpcode(ctx) + codes = c.value.(AnonymousCode).ToAnonymousOpcode(ctx.withType(c.typ)) } else { - codes = c.value.ToOpcode(ctx) + codes = c.value.ToOpcode(ctx.withType(c.typ)) } if isFirstField { op := optimizeStructHeader(codes.First(), c.tag) @@ -673,9 +678,9 @@ func (c *StructFieldCode) ToAnonymousOpcode(ctx *compileContext, isFirstField, i ctx.incIndex() var codes Opcodes if c.isAnonymous { - codes = c.value.(AnonymousCode).ToAnonymousOpcode(ctx) + codes = c.value.(AnonymousCode).ToAnonymousOpcode(ctx.withType(c.typ)) } else { - codes = c.value.ToOpcode(ctx) + codes = c.value.ToOpcode(ctx.withType(c.typ)) } if isFirstField { op := optimizeStructHeader(codes.First(), c.tag) @@ -746,7 +751,7 @@ func (c *MarshalJSONCode) Type() CodeType2 { } func (c *MarshalJSONCode) ToOpcode(ctx *compileContext) Opcodes { - code := newOpCode(ctx, OpMarshalJSON) + code := newOpCode(ctx.withType(c.typ), OpMarshalJSON) typ := c.typ if isPtrMarshalJSONType(typ) { code.Flags |= AddrForMarshalerFlags @@ -772,7 +777,7 @@ func (c *MarshalTextCode) Type() CodeType2 { } func (c *MarshalTextCode) ToOpcode(ctx *compileContext) Opcodes { - code := newOpCode(ctx, OpMarshalText) + code := newOpCode(ctx.withType(c.typ), OpMarshalText) typ := c.typ if !typ.Implements(marshalTextType) && runtime.PtrTo(typ).Implements(marshalTextType) { code.Flags |= AddrForMarshalerFlags @@ -797,14 +802,14 @@ func (c *PtrCode) Type() CodeType2 { } func (c *PtrCode) ToOpcode(ctx *compileContext) Opcodes { - codes := c.value.ToOpcode(ctx) + codes := c.value.ToOpcode(ctx.withType(c.typ.Elem())) codes.First().Op = convertPtrOp(codes.First()) codes.First().PtrNum = c.ptrNum return codes } func (c *PtrCode) ToAnonymousOpcode(ctx *compileContext) Opcodes { - codes := c.value.(AnonymousCode).ToAnonymousOpcode(ctx) + codes := c.value.(AnonymousCode).ToAnonymousOpcode(ctx.withType(c.typ.Elem())) codes.First().Op = convertPtrOp(codes.First()) codes.First().PtrNum = c.ptrNum return codes @@ -954,17 +959,17 @@ func compileInt2(ctx *compileContext, isPtr bool) (*IntCode, error) { } func compileInt82(ctx *compileContext, isPtr bool) (*IntCode, error) { - return &IntCode{typ: ctx.typ, bitSize: intSize, isPtr: isPtr}, nil -} - -func compileInt162(ctx *compileContext, isPtr bool) (*IntCode, error) { return &IntCode{typ: ctx.typ, bitSize: 8, isPtr: isPtr}, nil } -func compileInt322(ctx *compileContext, isPtr bool) (*IntCode, error) { +func compileInt162(ctx *compileContext, isPtr bool) (*IntCode, error) { return &IntCode{typ: ctx.typ, bitSize: 16, isPtr: isPtr}, nil } +func compileInt322(ctx *compileContext, isPtr bool) (*IntCode, error) { + return &IntCode{typ: ctx.typ, bitSize: 32, isPtr: isPtr}, nil +} + func compileInt642(ctx *compileContext, isPtr bool) (*IntCode, error) { return &IntCode{typ: ctx.typ, bitSize: 64, isPtr: isPtr}, nil } @@ -974,17 +979,17 @@ func compileUint2(ctx *compileContext, isPtr bool) (*UintCode, error) { } func compileUint82(ctx *compileContext, isPtr bool) (*UintCode, error) { - return &UintCode{typ: ctx.typ, bitSize: intSize, isPtr: isPtr}, nil -} - -func compileUint162(ctx *compileContext, isPtr bool) (*UintCode, error) { return &UintCode{typ: ctx.typ, bitSize: 8, isPtr: isPtr}, nil } -func compileUint322(ctx *compileContext, isPtr bool) (*UintCode, error) { +func compileUint162(ctx *compileContext, isPtr bool) (*UintCode, error) { return &UintCode{typ: ctx.typ, bitSize: 16, isPtr: isPtr}, nil } +func compileUint322(ctx *compileContext, isPtr bool) (*UintCode, error) { + return &UintCode{typ: ctx.typ, bitSize: 32, isPtr: isPtr}, nil +} + func compileUint642(ctx *compileContext, isPtr bool) (*UintCode, error) { return &UintCode{typ: ctx.typ, bitSize: 64, isPtr: isPtr}, nil } @@ -1002,17 +1007,17 @@ func compileIntString2(ctx *compileContext) (*IntCode, error) { } func compileInt8String2(ctx *compileContext) (*IntCode, error) { - return &IntCode{typ: ctx.typ, bitSize: intSize, isString: true}, nil -} - -func compileInt16String2(ctx *compileContext) (*IntCode, error) { return &IntCode{typ: ctx.typ, bitSize: 8, isString: true}, nil } -func compileInt32String2(ctx *compileContext) (*IntCode, error) { +func compileInt16String2(ctx *compileContext) (*IntCode, error) { return &IntCode{typ: ctx.typ, bitSize: 16, isString: true}, nil } +func compileInt32String2(ctx *compileContext) (*IntCode, error) { + return &IntCode{typ: ctx.typ, bitSize: 32, isString: true}, nil +} + func compileInt64String2(ctx *compileContext) (*IntCode, error) { return &IntCode{typ: ctx.typ, bitSize: 64, isString: true}, nil } @@ -1022,17 +1027,17 @@ func compileUintString2(ctx *compileContext) (*UintCode, error) { } func compileUint8String2(ctx *compileContext) (*UintCode, error) { - return &UintCode{typ: ctx.typ, bitSize: intSize, isString: true}, nil -} - -func compileUint16String2(ctx *compileContext) (*UintCode, error) { return &UintCode{typ: ctx.typ, bitSize: 8, isString: true}, nil } -func compileUint32String2(ctx *compileContext) (*UintCode, error) { +func compileUint16String2(ctx *compileContext) (*UintCode, error) { return &UintCode{typ: ctx.typ, bitSize: 16, isString: true}, nil } +func compileUint32String2(ctx *compileContext) (*UintCode, error) { + return &UintCode{typ: ctx.typ, bitSize: 32, isString: true}, nil +} + func compileUint64String2(ctx *compileContext) (*UintCode, error) { return &UintCode{typ: ctx.typ, bitSize: 64, isString: true}, nil } diff --git a/internal/encoder/compiler.go b/internal/encoder/compiler.go index 7b83a0a..94b4034 100644 --- a/internal/encoder/compiler.go +++ b/internal/encoder/compiler.go @@ -102,11 +102,14 @@ func compileHead(ctx *compileContext) (*Opcode, error) { return nil, err } //pp.Println(code) - newCtx := *ctx - codes := code.ToOpcode(&newCtx) - codes.Last().Next = newEndOp(ctx) - //pp.Println(codes) + derefctx := *ctx + newCtx := &derefctx + codes := code.ToOpcode(newCtx) + codes.Last().Next = newEndOp(newCtx) + //pp.Println(codes.First()) fmt.Println(codes.First().Dump()) + linkRecursiveCode2(newCtx) + return codes.First(), nil typ := ctx.typ switch { diff --git a/internal/encoder/compiler_norace.go b/internal/encoder/compiler_norace.go index 0115e77..3a575bc 100644 --- a/internal/encoder/compiler_norace.go +++ b/internal/encoder/compiler_norace.go @@ -25,6 +25,8 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) { typ: copiedType, structTypeToCompiledCode: map[uintptr]*CompiledCode{}, structTypeToCode: map[uintptr]*StructCode{}, + structTypeToCodes: map[uintptr]Opcodes{}, + recursiveCodes: &Opcodes{}, }) if err != nil { return nil, err @@ -33,6 +35,8 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) { typ: copiedType, structTypeToCompiledCode: map[uintptr]*CompiledCode{}, structTypeToCode: map[uintptr]*StructCode{}, + structTypeToCodes: map[uintptr]Opcodes{}, + recursiveCodes: &Opcodes{}, escapeKey: true, }) if err != nil { diff --git a/internal/encoder/compiler_race.go b/internal/encoder/compiler_race.go index 38ff447..308cd70 100644 --- a/internal/encoder/compiler_race.go +++ b/internal/encoder/compiler_race.go @@ -31,6 +31,8 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) { typ: copiedType, structTypeToCompiledCode: map[uintptr]*CompiledCode{}, structTypeToCode: map[uintptr]*StructCode{}, + structTypeToCodes: map[uintptr]Opcodes{}, + recursiveCodes: &Opcodes{}, }) if err != nil { return nil, err @@ -39,6 +41,8 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) { typ: copiedType, structTypeToCompiledCode: map[uintptr]*CompiledCode{}, structTypeToCode: map[uintptr]*StructCode{}, + structTypeToCodes: map[uintptr]Opcodes{}, + recursiveCodes: &Opcodes{}, escapeKey: true, }) if err != nil { diff --git a/internal/encoder/context.go b/internal/encoder/context.go index 6a2b2b0..b1e7316 100644 --- a/internal/encoder/context.go +++ b/internal/encoder/context.go @@ -16,6 +16,8 @@ type compileContext struct { escapeKey bool structTypeToCompiledCode map[uintptr]*CompiledCode structTypeToCode map[uintptr]*StructCode + structTypeToCodes map[uintptr]Opcodes + recursiveCodes *Opcodes parent *compileContext } @@ -29,6 +31,8 @@ func (c *compileContext) context() *compileContext { escapeKey: c.escapeKey, structTypeToCompiledCode: c.structTypeToCompiledCode, structTypeToCode: c.structTypeToCode, + structTypeToCodes: c.structTypeToCodes, + recursiveCodes: c.recursiveCodes, parent: c, } } diff --git a/test/cover/cover_map_test.go b/test/cover/cover_map_test.go index 2cf08d2..0b9a5fe 100644 --- a/test/cover/cover_map_test.go +++ b/test/cover/cover_map_test.go @@ -2,6 +2,7 @@ package json_test import ( "bytes" + "fmt" "testing" "github.com/goccy/go-json" @@ -1881,19 +1882,21 @@ func TestCoverMap(t *testing.T) { for _, test := range tests { for _, indent := range []bool{true, false} { for _, htmlEscape := range []bool{true, false} { - var buf bytes.Buffer - enc := json.NewEncoder(&buf) - enc.SetEscapeHTML(htmlEscape) - if indent { - enc.SetIndent("", " ") - } - if err := enc.Encode(test.data); err != nil { - t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err) - } - stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape) - if buf.String() != stdresult { - t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String()) - } + t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) { + var buf bytes.Buffer + enc := json.NewEncoder(&buf) + enc.SetEscapeHTML(htmlEscape) + if indent { + enc.SetIndent("", " ") + } + if err := enc.Encode(test.data); err != nil { + t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err) + } + stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape) + if buf.String() != stdresult { + t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String()) + } + }) } } } diff --git a/test/cover/cover_slice_test.go b/test/cover/cover_slice_test.go index eb60d94..61de9a5 100644 --- a/test/cover/cover_slice_test.go +++ b/test/cover/cover_slice_test.go @@ -2,6 +2,7 @@ package json_test import ( "bytes" + "fmt" "testing" "github.com/goccy/go-json" @@ -2047,9 +2048,9 @@ func TestCoverSlice(t *testing.T) { }, } for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - for _, indent := range []bool{true, false} { - for _, htmlEscape := range []bool{true, false} { + for _, indent := range []bool{true, false} { + for _, htmlEscape := range []bool{true, false} { + t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) { var buf bytes.Buffer enc := json.NewEncoder(&buf) enc.SetEscapeHTML(htmlEscape) @@ -2063,8 +2064,8 @@ func TestCoverSlice(t *testing.T) { if buf.String() != stdresult { t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String()) } - } + }) } - }) + } } }