package jsruntime import ( "crypto/md5" "fmt" "github.com/dop251/goja" "github.com/go-errors/errors" "io/ioutil" "log" "net/url" "path/filepath" "strings" "sync" "time" ) type JsRuntime struct { MainSrc string UseFileName string UseSrc 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, useFileName string, extFileDir string, rels Relys, mode RunMode) (jr *JsRuntime, err error) { jr = &JsRuntime{ MainSrc: mainScript, Relys: rels, UseFileName: useFileName, 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 } } useSrc, _ := ioutil.ReadFile(jr.UseFileName) jr.UseSrc = string(useSrc) go func() { for { useSrc, _ := ioutil.ReadFile(jr.UseFileName) jr.UseSrc = string(useSrc) time.Sleep(time.Second * 2) } }() 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 timeoutSec := jr.TimeoutSec useSrc := jr.UseSrc mainSrc := jr.MainSrc isLock := true go func() { time.Sleep(time.Duration(timeoutSec) * time.Second) if isLock { log.Println("timeout") runtime.Interrupt("timeout") runtime.ClearInterrupt() } }() defer func() { isLock = false }() 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 }