blob: 719b1f95dba7e5a1558a468c5f2ae4d17cebd24a [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_"
type ParameterInfo struct {
Name string
Type dst.Expr
DefaultValueAsString string
TypeName string
}
type PackagedParameterInfo struct {
ParameterInfo
PackageName string
}
// EnhanceParameterNames enhance the parameter names if they are missing
func EnhanceParameterNames(fields *dst.FieldList, isResult bool) []*ParameterInfo {
if fields == nil {
return nil
}
result := make([]*ParameterInfo, 0)
for i, f := range fields.List {
defineName := fmt.Sprintf("skywalking_param_%d", i)
if isResult {
defineName = fmt.Sprintf("skywalking_result_%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))
break
}
}
}
return result
}
func EnhanceParameterNamesWithPackagePrefix(pkg string, fields *dst.FieldList, isResult bool) []*PackagedParameterInfo {
params := EnhanceParameterNames(fields, isResult)
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),
}
var defaultNil = "nil"
switch n := tp.(type) {
case *dst.StarExpr:
result.DefaultValueAsString = defaultNil
case *dst.UnaryExpr:
if n.Op == token.INT || n.Op == token.FLOAT {
result.DefaultValueAsString = "0"
} else {
result.DefaultValueAsString = defaultNil
}
default:
result.DefaultValueAsString = defaultNil
}
return result
}
func (p *PackagedParameterInfo) PackagedType() dst.Expr {
return addPackagePrefixForArgsAndClone(p.PackageName, p.Type)
}
func (p *PackagedParameterInfo) PackagedTypeName() string {
return GenerateTypeNameByExp(p.PackagedType())
}
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)
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
default:
return dst.Clone(tp).(dst.Expr)
}
}