Skip to content

Commit

Permalink
feat(code_gen): support generating deepcopy apis (cloudwego#924)
Browse files Browse the repository at this point in the history
  • Loading branch information
jayantxie authored May 17, 2023
1 parent b8fb2e8 commit 9263e2f
Showing 8 changed files with 265 additions and 17 deletions.
25 changes: 25 additions & 0 deletions pkg/utils/strings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2023 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
*
* 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 utils

import "unsafe"

func StringDeepCopy(s string) string {
buf := []byte(s)
ns := (*string)(unsafe.Pointer(&buf))
return *ns
}
2 changes: 2 additions & 0 deletions tool/cmd/kitex/args/args.go
Original file line number Diff line number Diff line change
@@ -112,6 +112,8 @@ func (a *Arguments) buildFlags(version string) *flag.FlagSet {
"Use custom template to generate codes.")
f.StringVar(&a.GenPath, "gen-path", generator.KitexGenPath,
"Specify a code gen path.")
f.BoolVar(&a.DeepCopyAPI, "deep-copy-api", false,
"Generate codes with injecting deep copy method.")
a.RecordCmd = os.Args
a.Version = version
a.ThriftOptions = append(a.ThriftOptions,
2 changes: 2 additions & 0 deletions tool/internal_pkg/generator/generator.go
Original file line number Diff line number Diff line change
@@ -129,6 +129,8 @@ type Config struct {
TemplateDir string

GenPath string

DeepCopyAPI bool
}

// Pack packs the Config into a slice of "key=val" strings.
2 changes: 1 addition & 1 deletion tool/internal_pkg/generator/generator_test.go
Original file line number Diff line number Diff line change
@@ -63,7 +63,7 @@ func TestConfig_Pack(t *testing.T) {
{
name: "some",
fields: fields{Features: []feature{feature(999)}, ThriftPluginTimeLimit: 30 * time.Second},
wantRes: []string{"Verbose=false", "GenerateMain=false", "GenerateInvoker=false", "Version=", "NoFastAPI=false", "ModuleName=", "ServiceName=", "Use=", "IDLType=", "Includes=", "ThriftOptions=", "ProtobufOptions=", "IDL=", "OutputPath=", "PackagePrefix=", "CombineService=false", "CopyIDL=false", "ProtobufPlugins=", "Features=999", "FrugalPretouch=false", "ThriftPluginTimeLimit=30s", "ExtensionFile=", "Record=false", "RecordCmd=", "TemplateDir=", "GenPath="},
wantRes: []string{"Verbose=false", "GenerateMain=false", "GenerateInvoker=false", "Version=", "NoFastAPI=false", "ModuleName=", "ServiceName=", "Use=", "IDLType=", "Includes=", "ThriftOptions=", "ProtobufOptions=", "IDL=", "OutputPath=", "PackagePrefix=", "CombineService=false", "CopyIDL=false", "ProtobufPlugins=", "Features=999", "FrugalPretouch=false", "ThriftPluginTimeLimit=30s", "ExtensionFile=", "Record=false", "RecordCmd=", "TemplateDir=", "GenPath=", "DeepCopyAPI=false"},
},
}
for _, tt := range tests {
10 changes: 9 additions & 1 deletion tool/internal_pkg/pluginmode/thriftgo/file_tpl.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 40 additions & 8 deletions tool/internal_pkg/pluginmode/thriftgo/patcher.go
Original file line number Diff line number Diff line change
@@ -52,13 +52,14 @@ var KitexUnusedProtection = struct{}{}
var protectionInsertionPoint = "KitexUnusedProtection"

type patcher struct {
noFastAPI bool
utils *golang.CodeUtils
module string
copyIDL bool
version string
record bool
recordCmd []string
noFastAPI bool
utils *golang.CodeUtils
module string
copyIDL bool
version string
record bool
recordCmd []string
deepCopyAPI bool

fileTpl *template.Template
refTpl *template.Template
@@ -71,6 +72,7 @@ func (p *patcher) buildTemplates() (err error) {
m["IsBinaryOrStringType"] = p.isBinaryOrStringType
m["Version"] = func() string { return p.version }
m["GenerateFastAPIs"] = func() bool { return !p.noFastAPI && p.utils.Template() != "slim" }
m["GenerateDeepCopyAPIs"] = func() bool { return p.deepCopyAPI }
m["GenerateArgsResultTypes"] = func() bool { return p.utils.Template() == "slim" }
m["ImportPathTo"] = generator.ImportPathTo
m["ToPackageNames"] = func(imports map[string]string) (res []string) {
@@ -93,6 +95,18 @@ func (p *patcher) buildTemplates() (err error) {
m["IsNil"] = func(i interface{}) bool {
return i == nil || reflect.ValueOf(i).IsNil()
}
m["SourceTarget"] = func(s string) string {
// p.XXX
if strings.HasPrefix(s, "p.") {
return "src." + s[2:]
}
// _key, _val
return s[1:]
}
m["FieldName"] = func(s string) string {
// p.XXX
return strings.ToLower(s[2:3]) + s[3:]
}

tpl := template.New("kitex").Funcs(m)
allTemplates := basicTemplates
@@ -103,8 +117,10 @@ func (p *patcher) buildTemplates() (err error) {
templates.FieldIsSet)
} else {
allTemplates = append(allTemplates, structLikeCodec,
deepCopyAPI,
structLikeFastRead,
structLikeFastReadField,
structLikeDeepCopy,
structLikeFastWrite,
structLikeFastWriteNocopy,
structLikeLength,
@@ -117,6 +133,13 @@ func (p *patcher) buildTemplates() (err error) {
fieldFastReadMap,
fieldFastReadSet,
fieldFastReadList,
fieldDeepCopy,
fieldDeepCopyStructLike,
fieldDeepCopyContainer,
fieldDeepCopyMap,
fieldDeepCopyList,
fieldDeepCopySet,
fieldDeepCopyBaseType,
fieldFastWrite,
fieldLength,
fieldFastWriteStructLike,
@@ -217,8 +240,17 @@ func (p *patcher) patch(req *plugin.Request) (patches []*plugin.Generated, err e
if err = fileTpl.ExecuteTemplate(&buf, "file", data); err != nil {
return nil, fmt.Errorf("%q: %w", ast.Filename, err)
}
content := buf.String()
// if kutils is not used, remove the dependency.
if !strings.Contains(content, "kutils.StringDeepCopy") {
kutilsImp := `kutils "github.com/cloudwego/kitex/pkg/utils"`
idx := strings.Index(content, kutilsImp)
if idx > 0 {
content = content[:idx-1] + content[idx+len(kutilsImp):]
}
}
patches = append(patches, &plugin.Generated{
Content: buf.String(),
Content: content,
Name: &target,
})

15 changes: 8 additions & 7 deletions tool/internal_pkg/pluginmode/thriftgo/plugin.go
Original file line number Diff line number Diff line change
@@ -116,13 +116,14 @@ func Run() int {
}

p := &patcher{
noFastAPI: conv.Config.NoFastAPI,
utils: conv.Utils,
module: conv.Config.ModuleName,
copyIDL: conv.Config.CopyIDL,
version: conv.Config.Version,
record: conv.Config.Record,
recordCmd: conv.Config.RecordCmd,
noFastAPI: conv.Config.NoFastAPI,
utils: conv.Utils,
module: conv.Config.ModuleName,
copyIDL: conv.Config.CopyIDL,
version: conv.Config.Version,
record: conv.Config.Record,
recordCmd: conv.Config.RecordCmd,
deepCopyAPI: conv.Config.DeepCopyAPI,
}
patches, err := p.patch(req)
if err != nil {
178 changes: 178 additions & 0 deletions tool/internal_pkg/pluginmode/thriftgo/struct_tpl.go
Original file line number Diff line number Diff line change
@@ -32,6 +32,12 @@ const structLikeCodec = `
{{- end}}{{/* define "StructLikeCodec" */}}
`

const deepCopyAPI = `
{{define "DeepCopyAPI"}}
{{template "StructLikeDeepCopy" .}}
{{- end}}{{/* define "DeepCopyAPI" */}}
`

const structLikeFastRead = `
{{define "StructLikeFastRead"}}
{{- $TypeName := .GoName}}
@@ -186,6 +192,27 @@ func (p *{{$TypeName}}) FastReadField{{Str .ID}}(buf []byte) (int, error) {
{{- end}}{{/* define "StructLikeFastReadField" */}}
`

// TODO: check required
const structLikeDeepCopy = `
{{define "StructLikeDeepCopy"}}
{{- $TypeName := .GoName}}
func (p *{{$TypeName}}) DeepCopy(s interface{}) error {
{{if gt (len .Fields) 0 -}}
src, ok := s.(*{{$TypeName}})
if !ok {
return fmt.Errorf("%T's type not matched %T", s, p)
}
{{- end -}}
{{- range .Fields}}
{{- $ctx := MkRWCtx .}}
{{ template "FieldDeepCopy" $ctx}}
{{- end}}{{/* range .Fields */}}
{{/* line break */}}
return nil
}
{{- end}}{{/* define "StructLikeDeepCopy" */}}
`

const structLikeFastWrite = `
{{define "StructLikeFastWrite"}}
{{- $TypeName := .GoName}}
@@ -472,6 +499,157 @@ const fieldFastReadList = `
{{- end}}{{/* define "FieldFastReadList" */}}
`

const fieldDeepCopy = `
{{define "FieldDeepCopy"}}
{{- if .Type.Category.IsStructLike}}
{{- template "FieldDeepCopyStructLike" .}}
{{- else if .Type.Category.IsContainerType}}
{{- template "FieldDeepCopyContainer" .}}
{{- else}}{{/* IsBaseType */}}
{{- template "FieldDeepCopyBaseType" .}}
{{- end}}
{{- end}}{{/* define "FieldDeepCopy" */}}
`

const fieldDeepCopyStructLike = `
{{define "FieldDeepCopyStructLike"}}
{{- $Src := SourceTarget .Target}}
{{- if .NeedDecl}}
var {{.Target}} *{{.TypeName.Deref}}
{{- else}}
var _{{FieldName .Target}} *{{.TypeName.Deref}}
{{- end}}
if {{$Src}} != nil {
{{- if .NeedDecl}}{{.Target}}{{else}}_{{FieldName .Target}}{{end}} = &{{.TypeName.Deref}}{}
if err := {{- if .NeedDecl}}{{.Target}}{{else}}_{{FieldName .Target}}{{end}}.DeepCopy({{$Src}}); err != nil {
return err
}
}
{{if not .NeedDecl}}{{- .Target}} = _{{FieldName .Target}}{{end}}
{{- end}}{{/* define "FieldDeepCopyStructLike" */}}
`

const fieldDeepCopyContainer = `
{{define "FieldDeepCopyContainer"}}
{{- if eq "Map" .TypeID}}
{{- template "FieldDeepCopyMap" .}}
{{- else if eq "List" .TypeID}}
{{- template "FieldDeepCopyList" .}}
{{- else}}
{{- template "FieldDeepCopySet" .}}
{{- end}}
{{- end}}{{/* define "FieldDeepCopyContainer" */}}
`

const fieldDeepCopyMap = `
{{define "FieldDeepCopyMap"}}
{{- $Src := SourceTarget .Target}}
{{- if .NeedDecl}}var {{.Target}} {{.TypeName}}{{- end}}
if {{$Src}} != nil {
{{.Target}} = make({{.TypeName}}, len({{$Src}}))
{{- $key := .GenID "_key"}}
{{- $val := .GenID "_val"}}
for {{SourceTarget $key}}, {{SourceTarget $val}} := range {{$Src}} {
{{- $ctx := .KeyCtx.WithDecl.WithTarget $key}}
{{- template "FieldDeepCopy" $ctx}}
{{/* line break */}}
{{- $ctx := .ValCtx.WithDecl.WithTarget $val}}
{{- template "FieldDeepCopy" $ctx}}
{{- if and .ValCtx.Type.Category.IsStructLike Features.ValueTypeForSIC}}
{{$val = printf "*%s" $val}}
{{- end}}
{{.Target}}[{{$key}}] = {{$val}}
}
}
{{- end}}{{/* define "FieldDeepCopyMap" */}}
`

const fieldDeepCopyList = `
{{define "FieldDeepCopyList"}}
{{- $Src := SourceTarget .Target}}
{{if .NeedDecl}}var {{.Target}} {{.TypeName}}{{end}}
if {{$Src}} != nil {
{{.Target}} = make({{.TypeName}}, 0, len({{$Src}}))
{{- $val := .GenID "_elem"}}
for _, {{SourceTarget $val}} := range {{$Src}} {
{{- $ctx := .ValCtx.WithDecl.WithTarget $val}}
{{- template "FieldDeepCopy" $ctx}}
{{- if and .ValCtx.Type.Category.IsStructLike Features.ValueTypeForSIC}}
{{$val = printf "*%s" $val}}
{{- end}}
{{.Target}} = append({{.Target}}, {{$val}})
}
}
{{- end}}{{/* define "FieldDeepCopyList" */}}
`

const fieldDeepCopySet = `
{{define "FieldDeepCopySet"}}
{{- $Src := SourceTarget .Target}}
{{if .NeedDecl}}var {{.Target}} {{.TypeName}}{{end}}
if {{$Src}} != nil {
{{.Target}} = make({{.TypeName}}, 0, len({{$Src}}))
{{- $val := .GenID "_elem"}}
for _, {{SourceTarget $val}} := range {{$Src}} {
{{- $ctx := .ValCtx.WithDecl.WithTarget $val}}
{{- template "FieldDeepCopy" $ctx}}
{{- if and .ValCtx.Type.Category.IsStructLike Features.ValueTypeForSIC}}
{{$val = printf "*%s" $val}}
{{- end}}
{{.Target}} = append({{.Target}}, {{$val}})
}
}
{{- end}}{{/* define "FieldDeepCopySet" */}}
`

const fieldDeepCopyBaseType = `
{{define "FieldDeepCopyBaseType"}}
{{- $Src := SourceTarget .Target}}
{{- if .NeedDecl}}
var {{.Target}} {{.TypeName}}
{{- end}}
{{- if .IsPointer}}
if {{$Src}} != nil {
{{- if .Type.Category.IsBinary}}
if len(*{{$Src}}) != 0 {
tmp := make([]byte, len(*{{$Src}}))
copy(tmp, *{{$Src}})
{{.Target}} = &tmp
}
{{- else if .Type.Category.IsString}}
if *{{$Src}} != "" {
tmp := kutils.StringDeepCopy(*{{$Src}})
{{.Target}} = &tmp
}
{{- else}}
tmp := *{{$Src}}
{{.Target}} = &tmp
{{- end}}
}
{{- else}}
{{- if .Type.Category.IsBinary}}
if len({{$Src}}) != 0 {
tmp := make([]byte, len({{$Src}}))
copy(tmp, {{$Src}})
{{.Target}} = tmp
}
{{- else if .Type.Category.IsString}}
if {{$Src}} != "" {
{{.Target}} = kutils.StringDeepCopy({{$Src}})
}
{{- else}}
{{.Target}} = {{$Src}}
{{- end}}
{{- end}}
{{- end}}{{/* define "FieldDeepCopyBaseType" */}}
`

const fieldFastWrite = `
{{define "FieldFastWrite"}}
{{- if .Type.Category.IsStructLike}}

0 comments on commit 9263e2f

Please sign in to comment.