package jsruntime import ( "crypto/md5" "fmt" "github.com/dop251/goja" "github.com/go-errors/errors" "io/ioutil" "log" "net/url" "path/filepath" "strings" "sync" ) type JsRuntime struct { MainSrc string UseSrcFun func() string ExtFileDir string Relys Relys TimeoutSec int64 runtime *goja.Runtime mutex sync.Mutex } type Rely struct { Src string } type Relys []Rely type RunMode int const ( ModeSync RunMode = 1 //ModeAsync RunMode = 2 ) func NewJsRuntime(mainScript string, extFileDir string, rels Relys, mode RunMode) (jr *JsRuntime, err error) { jr = &JsRuntime{ MainSrc: mainScript, Relys: rels, ExtFileDir: extFileDir, TimeoutSec: 30, } jr.runtime = goja.New() jr.EnableTimeoutFunc() jr.EnableIntervalFun() jr.EnableRequestFunc() jr.EnableConsoleFun() jr.SetVariable("GoRunCodeByFile", func(filePath string) { code, err := ioutil.ReadFile(filepath.Join(jr.ExtFileDir, filePath)) if err != nil { log.Println("GoRunCodeByFile Read Error:", err) } err = jr.RunCode(string(code)) if err != nil { log.Println("GoRunCodeByFile Run Error:", err) } }) err = jr.InitJsCode() if err != nil { log.Println("InitJsCode", err) } return jr, nil } func (jr *JsRuntime) InitJsCode() error { for e := range jr.Relys { //start := time.Now().UnixNano() err := jr.RunCode(jr.Relys[e].Src) //end := time.Now().UnixNano() //log.Println(e, "加载时间:", end-start) if err != nil { log.Println("initjscode", err) return err } } return nil } func (jr *JsRuntime) SetVariable(name string, value interface{}) { jr.runtime.Set(name, value) } var codeMap sync.Map func (jr *JsRuntime) md5(data string) string { return fmt.Sprintf("%x", md5.Sum([]byte(data))) } func (jr *JsRuntime) RunCode(code string) error { id := jr.md5(code) var p interface{} var ok bool p, ok = codeMap.Load(id) if !ok { pro, err := goja.Compile("", code, false) if err != nil { return err } codeMap.Store(id, pro) p = pro } program := p.(*goja.Program) _, err := jr.runtime.RunProgram(program) return err } func (jr *JsRuntime) Render(filePath, href, tplSrc string, cb func(data string)) (err error) { jr.mutex.Lock() defer jr.mutex.Unlock() runtime := jr.runtime mainSrc := jr.MainSrc isLock := true //timeoutSec := jr.TimeoutSec defer func() { isLock = false jr.runtime.ClearInterrupt() }() var useSrc string if jr.UseSrcFun != nil { useSrc = jr.UseSrcFun() } if useSrc != "" { err = jr.RunCode(useSrc) if err != nil { return errors.New(err.Error() + "1") } } url, err := url.Parse(href) if err != nil { return errors.New(err.Error() + "2") } url2, err := url.Parse(strings.Replace(filePath, `\`, "/", -1)) if err != nil { return errors.New(err.Error() + "3") } runtime.Set("GoHtmlSrc", tplSrc) runtime.Set("GoHref", href) runtime.Set("GoQuery", url.Query().Encode()) runtime.Set("GoPath", url2.Path) runtime.Set("GoReturn", func(data string) { cb(data) }) err = jr.RunCode(mainSrc) if err != nil { return errors.New(err.Error() + "4") } return }