mjs.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. package mjs
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "log"
  7. "time"
  8. "git.ali33.ru/fcg-xvii/go-tools/cache"
  9. "github.com/dop251/goja"
  10. )
  11. func New(modified func(name string) int64, content func(name string) ([]byte, error)) *Mjs {
  12. return &Mjs{
  13. callbackModified: modified,
  14. callbackContent: content,
  15. cache: cache.NewCacheMap(context.Background(), 0, time.Hour*6),
  16. }
  17. }
  18. type Mjs struct {
  19. callbackModified func(name string) int64
  20. callbackContent func(name string) ([]byte, error)
  21. cache *cache.CacheMap
  22. }
  23. func (s *Mjs) program(name string) (prog *jsProgram, err error) {
  24. res, check := s.cache.GetCheck(name, func(key, val interface{}, exists bool) (rKey, rVal interface{}, needUpdate bool) {
  25. if exists {
  26. // check update need
  27. prog := val.(*jsProgram)
  28. // Why modified > prog.modified???
  29. if modified := s.callbackModified(name); modified == 0 || modified <= prog.modified {
  30. needUpdate = false
  31. return
  32. }
  33. }
  34. t := s.callbackModified(name)
  35. if t == 0 {
  36. err = fmt.Errorf("DOCUMENT %v NOT FOUND", name)
  37. return nil, nil, false
  38. }
  39. var content []byte
  40. if content, err = s.callbackContent(name); err != nil {
  41. return nil, nil, false
  42. }
  43. item := initProgram(name, string(content), t, s)
  44. return name, item, true
  45. })
  46. if check {
  47. prog = res.(*jsProgram)
  48. err = prog.compileErr
  49. } else {
  50. err = fmt.Errorf("Document %v is not defined", name)
  51. }
  52. return
  53. }
  54. func (s *Mjs) Exec(name string, data map[string]interface{}, durationMax time.Duration) (modified int64, err error) {
  55. defer func() {
  56. if err != nil && err.Error() == "<nil>" {
  57. err = nil
  58. }
  59. if r := recover(); r != nil && fmt.Sprint(r) != "runtime error: invalid memory address or nil pointer dereference" {
  60. err = fmt.Errorf("%v", r)
  61. }
  62. }()
  63. var prog *jsProgram
  64. if prog, err = s.program(name); err != nil {
  65. return
  66. }
  67. modified = prog.modified
  68. vm := goja.New()
  69. for k, v := range data {
  70. vm.Set(k, v)
  71. }
  72. if durationMax > 0 {
  73. log.Println("DMAX", durationMax)
  74. time.AfterFunc(durationMax, func() {
  75. log.Println("DCOMPLERE")
  76. vm.Interrupt(fmt.Sprintf("Exec script timeout (%v sec.)", durationMax/time.Second))
  77. })
  78. }
  79. var incBefore []string
  80. mods, currentMod, parentMod := make(map[string]goja.Value), name, ""
  81. checkIncBefore := func(name string) bool {
  82. for _, v := range incBefore {
  83. if v == name {
  84. return true
  85. }
  86. }
  87. return false
  88. }
  89. getMod := func(name string) goja.Value {
  90. if parentMod == name {
  91. err = fmt.Errorf("include loop detected %v in module %v", name, name)
  92. return nil
  93. }
  94. if mod, check := mods[name]; check {
  95. return mod
  96. } else {
  97. if !checkIncBefore(name) {
  98. var prog *jsProgram
  99. if prog, err = s.program(name); err != nil {
  100. err = fmt.Errorf("%v in module %v", err, parentMod)
  101. vm.Interrupt(nil)
  102. return nil
  103. } else {
  104. if prog.modified > modified {
  105. modified = prog.modified
  106. }
  107. if _, sErr := vm.RunProgram(prog.prog); sErr == nil {
  108. incBefore = append(incBefore, name)
  109. return mods[name]
  110. } else if err == nil && sErr.Error() != "<nil>" {
  111. err = fmt.Errorf("%v in module %v", sErr, parentMod)
  112. }
  113. return nil
  114. }
  115. }
  116. }
  117. return nil
  118. }
  119. vm.Set("runtime", vm)
  120. vm.Set("log", func(vals ...interface{}) {
  121. log.Println(vals...)
  122. })
  123. vm.Set("define", func(vals ...interface{}) {
  124. fCall, tmp := goja.FunctionCall{}, currentMod
  125. parentMod = tmp
  126. for _, v := range vals {
  127. if call, check := v.(func(goja.FunctionCall) goja.Value); check {
  128. currentMod = tmp
  129. parentMod = currentMod
  130. mods[currentMod] = call(fCall)
  131. return
  132. } else {
  133. currentMod = fmt.Sprint(v)
  134. fCall.Arguments = append(fCall.Arguments, getMod(currentMod))
  135. if err != nil && err.Error() != "<nil>" {
  136. return
  137. }
  138. }
  139. }
  140. })
  141. vm.Set("require", func(vals ...interface{}) {
  142. fCall := goja.FunctionCall{}
  143. for _, v := range vals {
  144. if call, check := v.(func(goja.FunctionCall) goja.Value); check {
  145. call(fCall)
  146. return
  147. } else {
  148. currentMod = fmt.Sprint(v)
  149. fCall.Arguments = append(fCall.Arguments, getMod(currentMod))
  150. if err != nil {
  151. return
  152. }
  153. }
  154. }
  155. })
  156. vm.Set("initError", func(errText string) error {
  157. return errors.New(errText)
  158. })
  159. vm.Set("fileExists", func(path string) bool {
  160. return s.callbackModified(path) > 0
  161. })
  162. vm.Set("include", func(vals ...interface{}) {
  163. if len(vals) == 0 {
  164. return
  165. }
  166. name := fmt.Sprint(vals[0])
  167. params := make(map[string]interface{})
  168. if len(vals) == 2 {
  169. if m, check := vals[1].(map[string]interface{}); check {
  170. params = m
  171. }
  172. }
  173. var prog *jsProgram
  174. if prog, err = s.program(name); err == nil {
  175. tmp := currentMod
  176. currentMod = prog.name
  177. if prog.modified > modified {
  178. modified = prog.modified
  179. }
  180. current := make(map[string]interface{})
  181. for k, v := range params {
  182. if val := vm.Get(k); val != nil {
  183. current[k] = val
  184. }
  185. vm.Set(k, v)
  186. }
  187. if _, sErr := vm.RunProgram(prog.prog); sErr == nil {
  188. for k, _ := range params {
  189. if c, check := current[k]; check {
  190. vm.Set(k, c)
  191. } else {
  192. vm.Set(k, nil)
  193. }
  194. }
  195. currentMod = tmp
  196. } else if err == nil {
  197. err = sErr
  198. vm.Interrupt(nil)
  199. }
  200. } else {
  201. err = fmt.Errorf("%v in module %v", err, currentMod)
  202. vm.Interrupt(err)
  203. }
  204. })
  205. vm.Set("exit", func(vals ...interface{}) {
  206. vm.Interrupt(err)
  207. })
  208. vm.Set("sleep", func(msec int) {
  209. time.Sleep(time.Millisecond * time.Duration(msec))
  210. })
  211. if _, exErr := vm.RunProgram(prog.prog); exErr != nil && exErr.Error() != "<nil>" && err == nil {
  212. err = exErr
  213. }
  214. log.Println("FINISHED...", err)
  215. return
  216. }