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.

266 lines
5.5 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. package jsruntime
  2. import (
  3. "crypto/md5"
  4. "fmt"
  5. "github.com/dop251/goja"
  6. "html"
  7. "io/ioutil"
  8. "log"
  9. "net/url"
  10. "path/filepath"
  11. "strconv"
  12. "strings"
  13. "sync"
  14. "time"
  15. )
  16. type JsRuntime struct {
  17. MainSrc string
  18. UseSrcFun func() string
  19. ExtFileDir string
  20. Relys Relys
  21. TimeoutSec int64
  22. runtime *goja.Runtime
  23. mutex sync.Mutex
  24. RenderCallBackFun func(string)
  25. IsDebugModel bool
  26. }
  27. type Rely struct {
  28. Src string
  29. }
  30. type Relys []Rely
  31. type RunMode int
  32. const (
  33. ModeSync RunMode = 1
  34. //ModeAsync RunMode = 2
  35. )
  36. func NewJsRuntime(mainScript string, extFileDir string, rels Relys, mode RunMode) (jr *JsRuntime, err error) {
  37. jr = &JsRuntime{
  38. MainSrc: mainScript,
  39. Relys: rels,
  40. ExtFileDir: extFileDir,
  41. TimeoutSec: 30,
  42. }
  43. jr.runtime = goja.New()
  44. jr.EnableTimeoutFunc()
  45. jr.EnableIntervalFun()
  46. jr.EnableRequestFunc()
  47. jr.EnableConsoleFun()
  48. jr.SetVariable("GoReadFile", func(filePath string) string {
  49. code, err := ioutil.ReadFile(filepath.Join(jr.ExtFileDir, filePath))
  50. if err != nil {
  51. jr.PrintError(fmt.Sprintln("GoRunCodeByFile Read Error:", err.Error()), string(code))
  52. }
  53. return string(code)
  54. })
  55. jr.SetVariable("GoRunCode", func(code string) {
  56. err = jr.RunCode(string(code))
  57. if err != nil {
  58. jr.PrintError(fmt.Sprintln("GoRunCode Run Error:", err.Error()), code)
  59. }
  60. })
  61. jr.SetVariable("GoRunCodeByFile", func(filePath string) {
  62. code, err := ioutil.ReadFile(filepath.Join(jr.ExtFileDir, filePath))
  63. if err != nil {
  64. jr.PrintError(fmt.Sprintln("GoRunCodeByFile Read Error:", err.Error()), string(code))
  65. }
  66. err = jr.RunCode(string(code))
  67. if err != nil {
  68. jr.PrintError(fmt.Sprintln("GoRunCodeByFile Run Error:", err.Error()), string(code))
  69. }
  70. })
  71. err = jr.InitJsCode()
  72. if err != nil {
  73. log.Println("InitJsCode", err)
  74. }
  75. return jr, nil
  76. }
  77. func (jr *JsRuntime) PrintError(msg string, code string) {
  78. if !jr.IsDebugModel {
  79. log.Println(msg)
  80. }
  81. var errLine string
  82. var line = -1
  83. evalErr := strings.Split(msg, "<eval>")
  84. if len(evalErr) > 1 {
  85. evalErr = strings.Split(evalErr[1], ":")
  86. if len(evalErr) > 1 {
  87. line, _ = strconv.Atoi(evalErr[1])
  88. }
  89. }
  90. if line == -1 {
  91. evalErr = strings.Split(msg, ": Line ")
  92. if len(evalErr) > 1 {
  93. evalErr = strings.Split(evalErr[1], ":")
  94. if len(evalErr) > 0 {
  95. line, _ = strconv.Atoi(evalErr[0])
  96. }
  97. }
  98. }
  99. //log.Println("error line", line,evalErr)
  100. if line > -1 {
  101. lines := strings.Split(string(code), "\n")
  102. linesStart := line - 15
  103. linesEnd := line + 15
  104. if linesStart < 0 {
  105. linesStart = 0
  106. }
  107. if len(lines)-1 < linesEnd {
  108. linesEnd = len(lines)
  109. }
  110. log.Println("error line linesEnd", linesEnd)
  111. //linesEnd = linesStart + linesEnd
  112. for e := range lines[linesStart:linesEnd] {
  113. if e+linesStart+1 == line {
  114. lines[linesStart:linesEnd][e] = fmt.Sprintf(`%2d: >>>>>> %s`, e+linesStart, lines[linesStart:linesEnd][e])
  115. } else {
  116. lines[linesStart:linesEnd][e] = fmt.Sprintf(`%2d: %s`, e+linesStart, lines[linesStart:linesEnd][e])
  117. }
  118. }
  119. errLine = strings.Join(lines[linesStart:linesEnd], "\n")
  120. }
  121. if jr.RenderCallBackFun != nil {
  122. jr.RenderCallBackFun(fmt.Sprintf(`
  123. <body style="background: #eeeeee;padding: 5%%;width:85%%;">
  124. <h3 style="color: red;">%s</h3>
  125. <div style="background: #fff;">
  126. <pre style="width:95%%;font-size: 14px; padding: 2%%; ">%s<pre>
  127. </div>
  128. </body>
  129. `, html.EscapeString(msg), html.EscapeString(errLine)))
  130. jr.RenderCallBackFun = func(s string) {
  131. }
  132. }
  133. }
  134. func (jr *JsRuntime) InitJsCode() error {
  135. for e := range jr.Relys {
  136. //start := time.Now().UnixNano()
  137. err := jr.RunCode(jr.Relys[e].Src)
  138. //end := time.Now().UnixNano()
  139. //log.Println(e, "加载时间:", end-start)
  140. if err != nil {
  141. return err
  142. }
  143. }
  144. return nil
  145. }
  146. func (jr *JsRuntime) SetVariable(name string, value interface{}) {
  147. jr.runtime.Set(name, value)
  148. }
  149. var codeMap sync.Map
  150. func (jr *JsRuntime) md5(data string) string {
  151. return fmt.Sprintf("%x", md5.Sum([]byte(data)))
  152. }
  153. func (jr *JsRuntime) RunCode(code string) error {
  154. id := jr.md5(code)
  155. var p interface{}
  156. var ok bool
  157. p, ok = codeMap.Load(id)
  158. if !ok {
  159. pro, err := goja.Compile("", code, false)
  160. if err != nil {
  161. log.Println("Compile Error")
  162. return err
  163. }
  164. codeMap.Store(id, pro)
  165. p = pro
  166. }
  167. program := p.(*goja.Program)
  168. _, err := jr.runtime.RunProgram(program)
  169. //log.Println("RunCode result", result)
  170. return err
  171. }
  172. func (jr *JsRuntime) Render(filePath, href, tplSrc string, GoExtData interface{}, cb func(data string)) (err error) {
  173. jr.RenderCallBackFun = cb
  174. jr.mutex.Lock()
  175. defer jr.mutex.Unlock()
  176. runtime := jr.runtime
  177. mainSrc := jr.MainSrc
  178. isLock := true
  179. //timeoutSec := jr.TimeoutSec
  180. defer func() {
  181. isLock = false
  182. jr.runtime.ClearInterrupt()
  183. }()
  184. var useSrc string
  185. if jr.UseSrcFun != nil {
  186. useSrc = jr.UseSrcFun()
  187. }
  188. if useSrc != "" {
  189. err = jr.RunCode(useSrc)
  190. if err != nil {
  191. return err
  192. }
  193. }
  194. url, err := url.Parse(href)
  195. if err != nil {
  196. return err
  197. }
  198. url2, err := url.Parse(strings.Replace(filePath, `\`, "/", -1))
  199. if err != nil {
  200. return err
  201. }
  202. runtime.Set("GoExtData", GoExtData)
  203. runtime.Set("GoHtmlSrc", tplSrc)
  204. runtime.Set("GoHref", href)
  205. runtime.Set("GoQuery", url.Query().Encode())
  206. runtime.Set("GoPath", url2.Path)
  207. runtime.Set("GoReturn", func(data string) {
  208. jr.RenderCallBackFun(data)
  209. })
  210. t := time.AfterFunc(time.Duration(jr.TimeoutSec), func() {
  211. jr.runtime.Interrupt("time out")
  212. jr.runtime.ClearInterrupt()
  213. })
  214. defer t.Stop()
  215. err = jr.RunCode(mainSrc)
  216. if err != nil {
  217. return err
  218. }
  219. return
  220. }