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.

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