work cover map and slice

This commit is contained in:
Masaaki Goshima 2021-11-25 13:10:01 +09:00
parent 6a00602e6a
commit f6d91525b2
No known key found for this signature in database
GPG Key ID: 6A53785055537153
7 changed files with 120 additions and 96 deletions

View File

@ -228,8 +228,8 @@ func (c *SliceCode) ToOpcode(ctx *compileContext) Opcodes {
size := c.typ.Elem().Size() size := c.typ.Elem().Size()
header := newSliceHeaderCode(ctx) header := newSliceHeaderCode(ctx)
ctx.incIndex() ctx.incIndex()
codes := c.value.ToOpcode(ctx) codes := c.value.ToOpcode(ctx.incIndent())
elemCode := newSliceElemCode(ctx.withType(c.typ.Elem()).incIndent(), header, size) elemCode := newSliceElemCode(ctx.withType(c.typ.Elem()), header, size)
ctx.incIndex() ctx.incIndex()
end := newOpCode(ctx, OpSliceEnd) end := newOpCode(ctx, OpSliceEnd)
ctx.incIndex() ctx.incIndex()
@ -292,7 +292,6 @@ func (c *MapCode) ToOpcode(ctx *compileContext) Opcodes {
// header => code => value => code => key => code => value => code => end // header => code => value => code => key => code => value => code => end
// ^ | // ^ |
// |_______________________| // |_______________________|
ctx = ctx.incIndent()
header := newMapHeaderCode(ctx) header := newMapHeaderCode(ctx)
ctx.incIndex() ctx.incIndex()
@ -300,13 +299,11 @@ func (c *MapCode) ToOpcode(ctx *compileContext) Opcodes {
value := newMapValueCode(ctx, header) value := newMapValueCode(ctx, header)
ctx.incIndex() ctx.incIndex()
valueCodes := c.value.ToOpcode(ctx) valueCodes := c.value.ToOpcode(ctx.incIndent())
key := newMapKeyCode(ctx, header) key := newMapKeyCode(ctx, header)
ctx.incIndex() ctx.incIndex()
ctx = ctx.decIndent()
end := newMapEndCode(ctx, header) end := newMapEndCode(ctx, header)
ctx.incIndex() ctx.incIndex()
@ -340,14 +337,11 @@ func (c *StructCode) ToOpcode(ctx *compileContext) Opcodes {
// header => code => structField => code => end // header => code => structField => code => end
// ^ | // ^ |
// |__________| // |__________|
var recursive *Opcode
compiled := &CompiledCode{}
if c.isRecursive { if c.isRecursive {
recursive = newRecursiveCode(ctx, compiled) recursive := newRecursiveCode(ctx, &CompiledCode{})
recursive.Type = c.typ
ctx.incIndex() ctx.incIndex()
} *ctx.recursiveCodes = append(*ctx.recursiveCodes, recursive)
if len(c.recursiveCodes) > 0 {
c.linkRecursiveCode(compiled, c.recursiveCodes)
return Opcodes{recursive} return Opcodes{recursive}
} }
codes := Opcodes{} codes := Opcodes{}
@ -389,12 +383,27 @@ func (c *StructCode) ToOpcode(ctx *compileContext) Opcodes {
} }
codes = append(codes, fieldCodes...) codes = append(codes, fieldCodes...)
} }
ctx = ctx.decIndent() if len(codes) == 0 {
if c.isRecursive { end := &Opcode{
c.recursiveCodes = codes Op: OpStructEnd,
c.linkRecursiveCode(compiled, c.recursiveCodes) Idx: opcodeOffset(ctx.ptrIndex),
return Opcodes{recursive} 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 return codes
} }
@ -402,14 +411,11 @@ func (c *StructCode) ToAnonymousOpcode(ctx *compileContext) Opcodes {
// header => code => structField => code => end // header => code => structField => code => end
// ^ | // ^ |
// |__________| // |__________|
var recursive *Opcode
compiled := &CompiledCode{}
if c.isRecursive { if c.isRecursive {
recursive = newRecursiveCode(ctx, compiled) recursive := newRecursiveCode(ctx, &CompiledCode{})
recursive.Type = c.typ
ctx.incIndex() ctx.incIndex()
} *ctx.recursiveCodes = append(*ctx.recursiveCodes, recursive)
if len(c.recursiveCodes) > 0 {
c.linkRecursiveCode(compiled, c.recursiveCodes)
return Opcodes{recursive} return Opcodes{recursive}
} }
codes := Opcodes{} codes := Opcodes{}
@ -440,36 +446,35 @@ func (c *StructCode) ToAnonymousOpcode(ctx *compileContext) Opcodes {
prevField = fieldCodes.First() prevField = fieldCodes.First()
codes = append(codes, fieldCodes...) codes = append(codes, fieldCodes...)
} }
if c.isRecursive {
c.recursiveCodes = codes
c.linkRecursiveCode(compiled, c.recursiveCodes)
return Opcodes{recursive}
}
return codes return codes
} }
func (c *StructCode) linkRecursiveCode(compiled *CompiledCode, codes Opcodes) { func linkRecursiveCode2(ctx *compileContext) {
compiled.Code = copyOpcode(codes.First()) for _, recursive := range *ctx.recursiveCodes {
code := compiled.Code typeptr := uintptr(unsafe.Pointer(recursive.Type))
code.End.Next = newEndOp(&compileContext{}) codes := ctx.structTypeToCodes[typeptr]
code.Op = code.Op.PtrHeadToHead() compiled := recursive.Jmp
compiled.Code = copyOpcode(codes.First())
code := compiled.Code
code.End.Next = newEndOp(&compileContext{})
code.Op = code.Op.PtrHeadToHead()
beforeLastCode := code.End beforeLastCode := code.End
lastCode := beforeLastCode.Next lastCode := beforeLastCode.Next
lastCode.Idx = beforeLastCode.Idx + uintptrSize lastCode.Idx = beforeLastCode.Idx + uintptrSize
lastCode.ElemIdx = lastCode.Idx + uintptrSize lastCode.ElemIdx = lastCode.Idx + uintptrSize
lastCode.Length = lastCode.Idx + 2*uintptrSize lastCode.Length = lastCode.Idx + 2*uintptrSize
code.End.Next.Op = OpRecursiveEnd
// extend length to alloc slot for elemIdx + length // extend length to alloc slot for elemIdx + length
totalLength := uintptr((codes.Last().MaxIdx() / uintptrSize) + 3) totalLength := uintptr(recursive.TotalLength()) + 3
nextTotalLength := uintptr(code.TotalLength() + 3) nextTotalLength := uintptr(codes.First().TotalLength()) + 3
code.End.Next.Op = OpRecursiveEnd compiled.CurLen = totalLength
compiled.NextLen = nextTotalLength
compiled.CurLen = totalLength compiled.Linked = true
compiled.NextLen = nextTotalLength }
compiled.Linked = true
} }
func (c *StructCode) removeFieldsByTags(tags runtime.StructTags) { func (c *StructCode) removeFieldsByTags(tags runtime.StructTags) {
@ -567,9 +572,9 @@ func (c *StructFieldCode) ToOpcode(ctx *compileContext, isFirstField, isEndField
ctx.incIndex() ctx.incIndex()
var codes Opcodes var codes Opcodes
if c.isAnonymous { if c.isAnonymous {
codes = c.value.(AnonymousCode).ToAnonymousOpcode(ctx) codes = c.value.(AnonymousCode).ToAnonymousOpcode(ctx.withType(c.typ))
} else { } else {
codes = c.value.ToOpcode(ctx) codes = c.value.ToOpcode(ctx.withType(c.typ))
} }
if isFirstField { if isFirstField {
op := optimizeStructHeader(codes.First(), c.tag) op := optimizeStructHeader(codes.First(), c.tag)
@ -673,9 +678,9 @@ func (c *StructFieldCode) ToAnonymousOpcode(ctx *compileContext, isFirstField, i
ctx.incIndex() ctx.incIndex()
var codes Opcodes var codes Opcodes
if c.isAnonymous { if c.isAnonymous {
codes = c.value.(AnonymousCode).ToAnonymousOpcode(ctx) codes = c.value.(AnonymousCode).ToAnonymousOpcode(ctx.withType(c.typ))
} else { } else {
codes = c.value.ToOpcode(ctx) codes = c.value.ToOpcode(ctx.withType(c.typ))
} }
if isFirstField { if isFirstField {
op := optimizeStructHeader(codes.First(), c.tag) op := optimizeStructHeader(codes.First(), c.tag)
@ -746,7 +751,7 @@ func (c *MarshalJSONCode) Type() CodeType2 {
} }
func (c *MarshalJSONCode) ToOpcode(ctx *compileContext) Opcodes { func (c *MarshalJSONCode) ToOpcode(ctx *compileContext) Opcodes {
code := newOpCode(ctx, OpMarshalJSON) code := newOpCode(ctx.withType(c.typ), OpMarshalJSON)
typ := c.typ typ := c.typ
if isPtrMarshalJSONType(typ) { if isPtrMarshalJSONType(typ) {
code.Flags |= AddrForMarshalerFlags code.Flags |= AddrForMarshalerFlags
@ -772,7 +777,7 @@ func (c *MarshalTextCode) Type() CodeType2 {
} }
func (c *MarshalTextCode) ToOpcode(ctx *compileContext) Opcodes { func (c *MarshalTextCode) ToOpcode(ctx *compileContext) Opcodes {
code := newOpCode(ctx, OpMarshalText) code := newOpCode(ctx.withType(c.typ), OpMarshalText)
typ := c.typ typ := c.typ
if !typ.Implements(marshalTextType) && runtime.PtrTo(typ).Implements(marshalTextType) { if !typ.Implements(marshalTextType) && runtime.PtrTo(typ).Implements(marshalTextType) {
code.Flags |= AddrForMarshalerFlags code.Flags |= AddrForMarshalerFlags
@ -797,14 +802,14 @@ func (c *PtrCode) Type() CodeType2 {
} }
func (c *PtrCode) ToOpcode(ctx *compileContext) Opcodes { 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().Op = convertPtrOp(codes.First())
codes.First().PtrNum = c.ptrNum codes.First().PtrNum = c.ptrNum
return codes return codes
} }
func (c *PtrCode) ToAnonymousOpcode(ctx *compileContext) Opcodes { 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().Op = convertPtrOp(codes.First())
codes.First().PtrNum = c.ptrNum codes.First().PtrNum = c.ptrNum
return codes return codes
@ -954,17 +959,17 @@ func compileInt2(ctx *compileContext, isPtr bool) (*IntCode, error) {
} }
func compileInt82(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 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 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) { func compileInt642(ctx *compileContext, isPtr bool) (*IntCode, error) {
return &IntCode{typ: ctx.typ, bitSize: 64, isPtr: isPtr}, nil 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) { 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 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 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) { func compileUint642(ctx *compileContext, isPtr bool) (*UintCode, error) {
return &UintCode{typ: ctx.typ, bitSize: 64, isPtr: isPtr}, nil 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) { 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 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 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) { func compileInt64String2(ctx *compileContext) (*IntCode, error) {
return &IntCode{typ: ctx.typ, bitSize: 64, isString: true}, nil 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) { 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 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 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) { func compileUint64String2(ctx *compileContext) (*UintCode, error) {
return &UintCode{typ: ctx.typ, bitSize: 64, isString: true}, nil return &UintCode{typ: ctx.typ, bitSize: 64, isString: true}, nil
} }

View File

@ -102,11 +102,14 @@ func compileHead(ctx *compileContext) (*Opcode, error) {
return nil, err return nil, err
} }
//pp.Println(code) //pp.Println(code)
newCtx := *ctx derefctx := *ctx
codes := code.ToOpcode(&newCtx) newCtx := &derefctx
codes.Last().Next = newEndOp(ctx) codes := code.ToOpcode(newCtx)
//pp.Println(codes) codes.Last().Next = newEndOp(newCtx)
//pp.Println(codes.First())
fmt.Println(codes.First().Dump()) fmt.Println(codes.First().Dump())
linkRecursiveCode2(newCtx)
return codes.First(), nil
typ := ctx.typ typ := ctx.typ
switch { switch {

View File

@ -25,6 +25,8 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
typ: copiedType, typ: copiedType,
structTypeToCompiledCode: map[uintptr]*CompiledCode{}, structTypeToCompiledCode: map[uintptr]*CompiledCode{},
structTypeToCode: map[uintptr]*StructCode{}, structTypeToCode: map[uintptr]*StructCode{},
structTypeToCodes: map[uintptr]Opcodes{},
recursiveCodes: &Opcodes{},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -33,6 +35,8 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
typ: copiedType, typ: copiedType,
structTypeToCompiledCode: map[uintptr]*CompiledCode{}, structTypeToCompiledCode: map[uintptr]*CompiledCode{},
structTypeToCode: map[uintptr]*StructCode{}, structTypeToCode: map[uintptr]*StructCode{},
structTypeToCodes: map[uintptr]Opcodes{},
recursiveCodes: &Opcodes{},
escapeKey: true, escapeKey: true,
}) })
if err != nil { if err != nil {

View File

@ -31,6 +31,8 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
typ: copiedType, typ: copiedType,
structTypeToCompiledCode: map[uintptr]*CompiledCode{}, structTypeToCompiledCode: map[uintptr]*CompiledCode{},
structTypeToCode: map[uintptr]*StructCode{}, structTypeToCode: map[uintptr]*StructCode{},
structTypeToCodes: map[uintptr]Opcodes{},
recursiveCodes: &Opcodes{},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -39,6 +41,8 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
typ: copiedType, typ: copiedType,
structTypeToCompiledCode: map[uintptr]*CompiledCode{}, structTypeToCompiledCode: map[uintptr]*CompiledCode{},
structTypeToCode: map[uintptr]*StructCode{}, structTypeToCode: map[uintptr]*StructCode{},
structTypeToCodes: map[uintptr]Opcodes{},
recursiveCodes: &Opcodes{},
escapeKey: true, escapeKey: true,
}) })
if err != nil { if err != nil {

View File

@ -16,6 +16,8 @@ type compileContext struct {
escapeKey bool escapeKey bool
structTypeToCompiledCode map[uintptr]*CompiledCode structTypeToCompiledCode map[uintptr]*CompiledCode
structTypeToCode map[uintptr]*StructCode structTypeToCode map[uintptr]*StructCode
structTypeToCodes map[uintptr]Opcodes
recursiveCodes *Opcodes
parent *compileContext parent *compileContext
} }
@ -29,6 +31,8 @@ func (c *compileContext) context() *compileContext {
escapeKey: c.escapeKey, escapeKey: c.escapeKey,
structTypeToCompiledCode: c.structTypeToCompiledCode, structTypeToCompiledCode: c.structTypeToCompiledCode,
structTypeToCode: c.structTypeToCode, structTypeToCode: c.structTypeToCode,
structTypeToCodes: c.structTypeToCodes,
recursiveCodes: c.recursiveCodes,
parent: c, parent: c,
} }
} }

View File

@ -2,6 +2,7 @@ package json_test
import ( import (
"bytes" "bytes"
"fmt"
"testing" "testing"
"github.com/goccy/go-json" "github.com/goccy/go-json"
@ -1881,19 +1882,21 @@ func TestCoverMap(t *testing.T) {
for _, test := range tests { for _, test := range tests {
for _, indent := range []bool{true, false} { for _, indent := range []bool{true, false} {
for _, htmlEscape := range []bool{true, false} { for _, htmlEscape := range []bool{true, false} {
var buf bytes.Buffer t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
enc := json.NewEncoder(&buf) var buf bytes.Buffer
enc.SetEscapeHTML(htmlEscape) enc := json.NewEncoder(&buf)
if indent { enc.SetEscapeHTML(htmlEscape)
enc.SetIndent("", " ") 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) 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 { stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
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()) 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())
}
})
} }
} }
} }

View File

@ -2,6 +2,7 @@ package json_test
import ( import (
"bytes" "bytes"
"fmt"
"testing" "testing"
"github.com/goccy/go-json" "github.com/goccy/go-json"
@ -2047,9 +2048,9 @@ func TestCoverSlice(t *testing.T) {
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { for _, indent := range []bool{true, false} {
for _, indent := range []bool{true, false} { for _, htmlEscape := 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 var buf bytes.Buffer
enc := json.NewEncoder(&buf) enc := json.NewEncoder(&buf)
enc.SetEscapeHTML(htmlEscape) enc.SetEscapeHTML(htmlEscape)
@ -2063,8 +2064,8 @@ func TestCoverSlice(t *testing.T) {
if buf.String() != stdresult { 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.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
} }
} })
} }
}) }
} }
} }