blob: 2c0a023426acd5a3731bb09b1736c5f8b1890f2f [file] [log] [blame]
// Licensed to Apache Software Foundation (ASF) under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Apache Software Foundation (ASF) licenses this file to you 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 tools
import (
"fmt"
"go/token"
"github.com/dave/dst"
"github.com/dave/dst/decorator"
)
const interfaceName = "interface{}"
const OtherPackageRefPrefix = "swref_"
const parameterAppender = ", "
type ParameterInfo struct {
Name string
Type dst.Expr
TypeName string
}
type PackagedParameterInfo struct {
ParameterInfo
PackageName string
}
type FieldListType int
const (
FieldListTypeParam FieldListType = iota
FieldListTypeResult
FieldListTypeRecv
)
func (f FieldListType) String() string {
switch f {
case FieldListTypeRecv:
return "recv"
case FieldListTypeParam:
return "param"
case FieldListTypeResult:
return "result"
}
return ""
}
// EnhanceParameterNames enhance the parameter names if they are missing
func EnhanceParameterNames(fields *dst.FieldList, fieldType FieldListType) []*ParameterInfo {
if fields == nil {
return nil
}
result := make([]*ParameterInfo, 0)
for i, f := range fields.List {
var defineName string
switch fieldType {
case FieldListTypeParam:
defineName = fmt.Sprintf("skywalking_param_%d", i)
case FieldListTypeResult:
defineName = fmt.Sprintf("skywalking_result_%d", i)
case FieldListTypeRecv:
defineName = fmt.Sprintf("skywalking_recv_%d", i)
}
if len(f.Names) == 0 {
f.Names = []*dst.Ident{{Name: defineName}}
result = append(result, newParameterInfo(defineName, f.Type))
} else {
for _, n := range f.Names {
if n.Name == "_" {
*n = *dst.NewIdent(defineName)
break
}
}
for _, n := range f.Names {
result = append(result, newParameterInfo(n.Name, f.Type))
}
}
}
return result
}
func EnhanceParameterNamesWithPackagePrefix(pkg string, fields *dst.FieldList, fieldListType FieldListType) []*PackagedParameterInfo {
params := EnhanceParameterNames(fields, fieldListType)
result := make([]*PackagedParameterInfo, 0)
for _, p := range params {
result = append(result, &PackagedParameterInfo{ParameterInfo: *p, PackageName: pkg})
}
return result
}
func GoStringToStats(goString string) []dst.Stmt {
parsed, err := decorator.Parse(fmt.Sprintf(`
package main
func main() {
%s
}`, goString))
if err != nil {
panic(fmt.Sprintf("parsing go failure: %v\n%s", err, goString))
}
return parsed.Decls[0].(*dst.FuncDecl).Body.List
}
func GoStringToDecls(goString string) []dst.Decl {
parsed, err := decorator.Parse(fmt.Sprintf(`
package main
%s`, goString))
if err != nil {
panic(fmt.Sprintf("parsing go failure: %v\n%s", err, goString))
}
return parsed.Decls
}
func InsertStmtsBeforeBody(body *dst.BlockStmt, tmpl string, data interface{}) {
body.List = append(GoStringToStats(ExecuteTemplate(tmpl, data)), body.List...)
}
func newParameterInfo(name string, tp dst.Expr) *ParameterInfo {
result := &ParameterInfo{
Name: name,
Type: tp,
TypeName: GenerateTypeNameByExp(tp),
}
return result
}
func (p *PackagedParameterInfo) PackagedType() dst.Expr {
return addPackagePrefixForArgsAndClone(p.PackageName, p.Type)
}
func (p *PackagedParameterInfo) PackagedTypeName() string {
return GenerateTypeNameByExp(p.PackagedType())
}
// nolint
func GenerateTypeNameByExp(exp dst.Expr) string {
var data string
switch n := exp.(type) {
case *dst.StarExpr:
data = "*" + GenerateTypeNameByExp(n.X)
case *dst.TypeAssertExpr:
data = GenerateTypeNameByExp(n.X)
case *dst.InterfaceType:
data = interfaceName
case *dst.Ident:
data = n.Name
case *dst.SelectorExpr:
data = GenerateTypeNameByExp(n.X) + "." + GenerateTypeNameByExp(n.Sel)
case *dst.Ellipsis:
data = "[]" + GenerateTypeNameByExp(n.Elt)
case *dst.ArrayType:
data = "[]" + GenerateTypeNameByExp(n.Elt)
case *dst.FuncType:
data = "func("
if n.Params != nil && len(n.Params.List) > 0 {
for i, f := range n.Params.List {
if i > 0 {
data += parameterAppender
}
data += GenerateTypeNameByExp(f.Type)
}
}
data += ")"
if n.Results != nil && len(n.Results.List) > 0 {
data += "("
for i, f := range n.Results.List {
if i > 0 {
data += parameterAppender
}
data += GenerateTypeNameByExp(f.Type)
}
data += ")"
}
case *dst.ChanType:
if n.Dir == dst.SEND {
data += token.CHAN.String() + token.ARROW.String()
} else if n.Dir == dst.RECV {
data += token.ARROW.String() + token.CHAN.String()
} else {
data += token.CHAN.String()
}
data += " " + GenerateTypeNameByExp(n.Value)
return data
default:
return ""
}
return data
}
func addPackagePrefixForArgsAndClone(pkg string, tp dst.Expr) dst.Expr {
switch t := tp.(type) {
case *dst.Ident:
if IsBasicDataType(t.Name) {
return dst.Clone(tp).(dst.Expr)
}
// otherwise, add the package prefix
return &dst.SelectorExpr{
X: dst.NewIdent(pkg),
Sel: dst.NewIdent(t.Name),
}
case *dst.StarExpr:
expr := dst.Clone(tp).(*dst.StarExpr)
expr.X = addPackagePrefixForArgsAndClone(pkg, t.X)
return expr
case *dst.Ellipsis:
expr := dst.Clone(tp).(*dst.Ellipsis)
expr.Elt = addPackagePrefixForArgsAndClone(pkg, t.Elt)
return expr
case *dst.SelectorExpr:
exp := dst.Clone(tp).(*dst.SelectorExpr)
// if also contains a package prefix, then it could be reffed a package with same name
// Such as current package name is "grpc", and ref another package named "grpc"
// Usually it's used on a wrapper plugin
if sel, ok := t.X.(*dst.Ident); ok && sel.Name == pkg {
exp.X = dst.NewIdent(OtherPackageRefPrefix + pkg)
}
return exp
case *dst.ChanType:
expr := dst.Clone(tp).(*dst.ChanType)
return expr
default:
return dst.Clone(tp).(dst.Expr)
}
}