gosh/interceptor/interceptor.go

161 lines
4.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package interceptor
import (
"fmt"
"os"
"path/filepath"
"github.com/mattn/anko/env"
"github.com/mattn/anko/vm"
)
type Ankointerceptor struct {
importMap map[string]interface{}
libMap map[string]interface{}
liberalises []uintptr
ankoEnv *env.Env
}
func NewAnkointerceptor(ankoEnv *env.Env) *Ankointerceptor {
if ankoEnv == nil {
ankoEnv = env.NewEnv()
}
a := &Ankointerceptor{
importMap: make(map[string]interface{}),
libMap: make(map[string]interface{}),
liberalises: make([]uintptr, 0),
ankoEnv: ankoEnv,
}
_ = a.ankoEnv.Define("println", fmt.Println)
_ = a.ankoEnv.Define("loadLibrary", func(libPath string, libFuncMap map[string]funcType) interface{} {
return a.loadLibrary(libPath, libFuncMap)
})
a.importMap["fs"] = &FileModule{}
a.importMap["net"] = &NetModule{}
a.importMap["process"] = &ProcessModule{}
a.importMap["shell"] = &ShellModule{}
return a
}
// Exec executes the specified script within the Anko environment.
// It sets up the environment with necessary variables and methods,
// reads the script file, and executes it, returning the result or an error.
//
// Parameters:
// - script: Path to the script file to be executed.
//
// Returns:
// - interface{}: The result of the script execution.
// - error: An error if the script file cannot be read or execution fails.
func (a *Ankointerceptor) Exec(script string) (interface{}, error) {
st, err := os.Stat(script)
if err != nil {
return nil, err
}
if st.IsDir() {
toExec := filepath.Join(script, "index.ank")
return a.exec(toExec, script)
}
if !filepath.IsAbs(script) {
script = filepath.Join(os.Getenv("PWD"), script)
}
content, err := os.ReadFile(script)
if err != nil {
return nil, err
}
scriptContent := string(content)
result, err := a.exec(scriptContent, script)
if err != nil {
return nil, err
}
return result, nil
}
// ExecContent executes the specified script content within the Anko environment.
// It sets up the environment with necessary variables and methods,
// executes the script content, and returns the result or an error.
//
// Parameters:
// - scriptContent: The script content to be executed.
//
// Returns:
// - interface{}: The result of the script execution.
// - error: An error if the script execution fails.
func (a *Ankointerceptor) ExecContent(scriptContent string) (interface{}, error) {
result, err := a.exec(scriptContent, "")
if err != nil {
return nil, err
}
return result, nil
}
func (a *Ankointerceptor) exec(scriptContent string, scriptPath string) (interface{}, error) {
e := a.ankoEnv.Copy()
// Configure the Anko environment with required variables and methods
if scriptPath == "" {
cdir, _ := os.Getwd()
scriptPath = filepath.Join(cdir, "tmp")
}
_ = e.Define("Require", a.genRequireMethod(scriptPath))
_ = e.Define("__filename", scriptPath)
_ = e.Define("__dirname", filepath.Dir(scriptPath))
// Execute the script code in the prepared environment
result, err := vm.Execute(e, nil, scriptContent)
if err != nil {
fmt.Println(err)
return nil, err
}
return result, nil
}
// genRequireMethod 创建模块解析闭包函数
// 该函数用于处理模块路径解析和缓存机制:
// 1. 优先从importMap缓存中查找已加载模块
// 2. 对相对路径自动转换为基于basePath的绝对路径
// 3. 执行模块加载后自动缓存结果
// 参数:
//
// basePath - 模块基础路径,用于解析相对路径
//
// 返回:
//
// 闭包函数接收模块路径s返回解析后的模块对象
func (a *Ankointerceptor) genRequireMethod(basePath string) interface{} {
return func(s string) interface{} {
if r, ok := a.importMap[s]; ok {
return r
}
// 将相对路径转换为基于basePath的绝对路径
if !filepath.IsAbs(s) {
s = filepath.Join(filepath.Dir(basePath), s)
}
if _, ok := a.importMap[s]; ok {
return a.importMap[s]
}
// 执行模块加载并将结果缓存
result, err := a.Exec(s)
if err != nil {
panic(err)
}
a.importMap[s] = result
return result
}
}
func (a *Ankointerceptor) RegistModule(name string, m map[string]interface{}) {
_, ok := a.importMap[name]
if ok {
panic("Module already exists")
}
a.importMap[name] = m
}
func (a *Ankointerceptor) RegistStructAsModule(name string, s interface{}) {
_, ok := a.importMap[name]
if ok {
panic("Module already exists")
}
a.importMap[name] = s
}