在运行时从作为参数传递的函数中读取函数体。

huangapple go评论91阅读模式
英文:

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 (
	&quot;fmt&quot;
	&quot;testing&quot;
)

type TestStruct struct {}

func (TestStruct) New() *TestStruct {
	return &amp;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]) // &lt;&lt; read switch string option &quot;one&quot;
	fmt.Println(val[1]) // &lt;&lt; read switch string option &quot;two&quot;
}

// Testing
func TestReflection(t *testing.T) {
	TestStruct{}.New().CreateFunction(func(args []string) {
		sw := &quot;one&quot;
		switch sw {
		case &quot;one&quot;:
			fmt.Println(&quot;some text&quot;)
		case &quot;two&quot;:
			fmt.Println(&quot;some text&quot;)
		}
	})
}

答案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 (
	&quot;fmt&quot;
	&quot;go/ast&quot;
	&quot;go/parser&quot;
	&quot;go/token&quot;
	&quot;io&quot;
	&quot;log&quot;
	&quot;os&quot;
	&quot;reflect&quot;
	&quot;runtime&quot;
	&quot;testing&quot;
)

type TestStruct struct{}

func (TestStruct) New() *TestStruct {
	return &amp;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 := &amp;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(&quot;code of runFunc:&quot;, string(buf))
	}
	// Read code block, switch values from function pointer
	// val := reflect.ValueOf(t)....?
	// fmt.Println(val[0]) // &lt;&lt; read switch string option &quot;one&quot;
	// fmt.Println(val[1]) // &lt;&lt; read switch string option &quot;two&quot;
}

// Testing
func TestReflection(t *testing.T) {
	TestStruct{}.New().CreateFunction(func(args []string) {
		sw := &quot;one&quot;
		switch sw {
		case &quot;one&quot;:
			fmt.Println(&quot;some text&quot;)
		case &quot;two&quot;:
			fmt.Println(&quot;some text&quot;)
		}
	})
}

output:

$ go test -v -run TestReflection
=== RUN   TestReflection
/xxxxxxxxxx.go 82
code of runFunc: {
sw := &quot;one&quot;
switch sw {
case &quot;one&quot;:
fmt.Println(&quot;some text&quot;)
case &quot;two&quot;:
fmt.Println(&quot;some text&quot;)
}
--- PASS: TestReflection (0.00s)
PASS
ok  	maint	0.501s

huangapple
  • 本文由 发表于 2021年11月1日 05:35:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/69790783.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定