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.

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