You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

190 lines
3.4 KiB

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
}