英文:
Read function body from a function passed as argument at runtime
问题
我有一些动态生成和编译的代码-然后我需要在运行时读取函数体,这可能吗?
package example
import (
"fmt"
"testing"
)
type TestStruct struct {}
func (TestStruct) New() *TestStruct {
return &TestStruct{}
}
// CreateFunction func
func (t *TestStruct) CreateFunction(runFunc func(args []string)) {
// Read code block, switch values from function pointer
//val := reflect.ValueOf(t)....?
fmt.Println(val[0]) // << read switch string option "one"
fmt.Println(val[1]) // << read switch string option "two"
}
// Testing
func TestReflection(t *testing.T) {
TestStruct{}.New().CreateFunction(func(args []string) {
sw := "one"
switch sw {
case "one":
fmt.Println("some text")
case "two":
fmt.Println("some text")
}
})
}
这段代码中,你想在运行时读取函数体。你可以使用反射来实现这个目的。通过使用reflect
包中的ValueOf
函数,你可以获取函数的值,并进一步操作它。在你的代码中,你可以尝试使用reflect.ValueOf(runFunc)
来获取runFunc
的值,并从中读取代码块。然后,你可以根据需要处理这些值。
英文:
I have some dynamic code generated and compiled - then I need to read function body at runtime, is that possible?
package example
import (
"fmt"
"testing"
)
type TestStruct struct {}
func (TestStruct) New() *TestStruct {
return &TestStruct{}
}
// CreateFunction func
func (t *TestStruct) CreateFunction(runFunc func(args []string)) {
// Read code block, switch values from function pointer
//val := reflect.ValueOf(t)....?
fmt.Println(val[0]) // << read switch string option "one"
fmt.Println(val[1]) // << read switch string option "two"
}
// Testing
func TestReflection(t *testing.T) {
TestStruct{}.New().CreateFunction(func(args []string) {
sw := "one"
switch sw {
case "one":
fmt.Println("some text")
case "two":
fmt.Println("some text")
}
})
}
答案1
得分: 1
如果是为了测试目的,下面的代码是可行的。我只是打印了函数体的内容,你可以尝试解析函数体中的SwitchStmt
并获取你需要的内容。
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"io"
"log"
"os"
"reflect"
"runtime"
"testing"
)
type TestStruct struct{}
func (TestStruct) New() *TestStruct {
return &TestStruct{}
}
type FindBlockByLine struct {
Fset *token.FileSet
Line int
Block *ast.BlockStmt
}
func (f *FindBlockByLine) Visit(node ast.Node) ast.Visitor {
if node == nil {
return nil
}
if blockStmt, ok := node.(*ast.BlockStmt); ok {
stmtStartingPosition := blockStmt.Pos()
stmtLine := f.Fset.Position(stmtStartingPosition).Line
if stmtLine == f.Line {
f.Block = blockStmt
return nil
}
}
return f
}
// CreateFunction func
func (t *TestStruct) CreateFunction(runFunc func(args []string)) {
p := reflect.ValueOf(runFunc).Pointer()
fc := runtime.FuncForPC(p)
// 获取runFunc的文件名和行号
filename, line := fc.FileLine(p)
fset := token.NewFileSet()
// 解析文件为AST树
node, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
if err != nil {
log.Fatal(err)
}
// 遍历并查找函数块
find := &FindBlockByLine{Fset: fset, Line: line}
ast.Walk(find, node)
if find.Block != nil {
fp, err := os.Open(filename)
if err != nil {
log.Fatal(err)
}
defer fp.Close()
fp.Seek(int64(find.Block.Lbrace-1), 0)
buf := make([]byte, int64(find.Block.Rbrace-find.Block.Lbrace))
_, err = io.ReadFull(fp, buf)
if err != nil {
log.Fatal(err)
}
fmt.Println("runFunc的代码:", string(buf))
}
// 读取代码块,从函数指针中获取值
// val := reflect.ValueOf(t)....?
// fmt.Println(val[0]) // << 读取switch语句的选项 "one"
// fmt.Println(val[1]) // << 读取switch语句的选项 "two"
}
// 测试
func TestReflection(t *testing.T) {
TestStruct{}.New().CreateFunction(func(args []string) {
sw := "one"
switch sw {
case "one":
fmt.Println("一些文本")
case "two":
fmt.Println("一些文本")
}
})
}
输出:
$ go test -v -run TestReflection
=== RUN TestReflection
/xxxxxxxxxx.go 82
runFunc的代码: {
sw := "one";
switch sw {
case "one":
fmt.Println("一些文本")
case "two":
fmt.Println("一些文本")
}
}
--- PASS: TestReflection (0.00s)
PASS
ok maint 0.501s
英文:
If it is for testing purposes, the following code is feasible. I just printed out the body of function, you could try to parse the SwitchStmt
in function body and get what you need.
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"io"
"log"
"os"
"reflect"
"runtime"
"testing"
)
type TestStruct struct{}
func (TestStruct) New() *TestStruct {
return &TestStruct{}
}
type FindBlockByLine struct {
Fset *token.FileSet
Line int
Block *ast.BlockStmt
}
func (f *FindBlockByLine) Visit(node ast.Node) ast.Visitor {
if node == nil {
return nil
}
if blockStmt, ok := node.(*ast.BlockStmt); ok {
stmtStartingPosition := blockStmt.Pos()
stmtLine := f.Fset.Position(stmtStartingPosition).Line
if stmtLine == f.Line {
f.Block = blockStmt
return nil
}
}
return f
}
// CreateFunction func
func (t *TestStruct) CreateFunction(runFunc func(args []string)) {
p := reflect.ValueOf(runFunc).Pointer()
fc := runtime.FuncForPC(p)
// get filename and line number of runFunc
filename, line := fc.FileLine(p)
fset := token.NewFileSet()
// parse file to AST tree
node, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
if err != nil {
log.Fatal(err)
}
// walk and find the function block
find := &FindBlockByLine{Fset: fset, Line: line}
ast.Walk(find, node)
if find.Block != nil {
fp, err := os.Open(filename)
if err != nil {
log.Fatal(err)
}
defer fp.Close()
fp.Seek(int64(find.Block.Lbrace-1), 0)
buf := make([]byte, int64(find.Block.Rbrace-find.Block.Lbrace))
_, err = io.ReadFull(fp, buf)
if err != nil {
log.Fatal(err)
}
fmt.Println("code of runFunc:", string(buf))
}
// Read code block, switch values from function pointer
// val := reflect.ValueOf(t)....?
// fmt.Println(val[0]) // << read switch string option "one"
// fmt.Println(val[1]) // << read switch string option "two"
}
// Testing
func TestReflection(t *testing.T) {
TestStruct{}.New().CreateFunction(func(args []string) {
sw := "one"
switch sw {
case "one":
fmt.Println("some text")
case "two":
fmt.Println("some text")
}
})
}
output:
$ go test -v -run TestReflection
=== RUN TestReflection
/xxxxxxxxxx.go 82
code of runFunc: {
sw := "one"
switch sw {
case "one":
fmt.Println("some text")
case "two":
fmt.Println("some text")
}
--- PASS: TestReflection (0.00s)
PASS
ok maint 0.501s
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论