mjs.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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. time.AfterFunc(durationMax, func() {
  74. vm.Interrupt(fmt.Sprintf("Exec script timeout (%v sec.)", durationMax/time.Second))
  75. })
  76. }
  77. var incBefore []string
  78. mods, currentMod, parentMod := make(map[string]goja.Value), name, ""
  79. checkIncBefore := func(name string) bool {
  80. for _, v := range incBefore {
  81. if v == name {
  82. return true
  83. }
  84. }
  85. return false
  86. }
  87. getMod := func(name string) goja.Value {
  88. if parentMod == name {
  89. err = fmt.Errorf("include loop detected %v in module %v", name, name)
  90. return nil
  91. }
  92. if mod, check := mods[name]; check {
  93. return mod
  94. } else {
  95. if !checkIncBefore(name) {
  96. var prog *jsProgram
  97. if prog, err = s.program(name); err != nil {
  98. err = fmt.Errorf("%v in module %v", err, parentMod)
  99. vm.Interrupt(nil)
  100. return nil
  101. } else {
  102. if prog.modified > modified {
  103. modified = prog.modified
  104. }
  105. if _, sErr := vm.RunProgram(prog.prog); sErr == nil {
  106. incBefore = append(incBefore, name)
  107. return mods[name]
  108. } else if err == nil && sErr.Error() != "<nil>" {
  109. err = fmt.Errorf("%v in module %v", sErr, parentMod)
  110. }
  111. return nil
  112. }
  113. }
  114. }
  115. return nil
  116. }
  117. vm.Set("runtime", vm)
  118. vm.Set("log", func(vals ...interface{}) {
  119. log.Println(vals...)
  120. })
  121. vm.Set("define", func(vals ...interface{}) {
  122. fCall, tmp := goja.FunctionCall{}, currentMod
  123. parentMod = tmp
  124. for _, v := range vals {
  125. if call, check := v.(func(goja.FunctionCall) goja.Value); check {
  126. currentMod = tmp
  127. parentMod = currentMod
  128. mods[currentMod] = call(fCall)
  129. return
  130. } else {
  131. currentMod = fmt.Sprint(v)
  132. fCall.Arguments = append(fCall.Arguments, getMod(currentMod))
  133. if err != nil && err.Error() != "<nil>" {
  134. return
  135. }
  136. }
  137. }
  138. })
  139. vm.Set("require", func(vals ...interface{}) {
  140. fCall := goja.FunctionCall{}
  141. for _, v := range vals {
  142. if call, check := v.(func(goja.FunctionCall) goja.Value); check {
  143. call(fCall)
  144. return
  145. } else {
  146. currentMod = fmt.Sprint(v)
  147. fCall.Arguments = append(fCall.Arguments, getMod(currentMod))
  148. if err != nil {
  149. return
  150. }
  151. }
  152. }
  153. })
  154. vm.Set("initError", func(errText string) error {
  155. return errors.New(errText)
  156. })
  157. vm.Set("fileExists", func(path string) bool {
  158. return s.callbackModified(path) > 0
  159. })
  160. vm.Set("include", func(vals ...interface{}) {
  161. if len(vals) == 0 {
  162. return
  163. }
  164. name := fmt.Sprint(vals[0])
  165. params := make(map[string]interface{})
  166. if len(vals) == 2 {
  167. if m, check := vals[1].(map[string]interface{}); check {
  168. params = m
  169. }
  170. }
  171. var prog *jsProgram
  172. if prog, err = s.program(name); err == nil {
  173. tmp := currentMod
  174. currentMod = prog.name
  175. if prog.modified > modified {
  176. modified = prog.modified
  177. }
  178. current := make(map[string]interface{})
  179. for k, v := range params {
  180. if val := vm.Get(k); val != nil {
  181. current[k] = val
  182. }
  183. vm.Set(k, v)
  184. }
  185. if _, sErr := vm.RunProgram(prog.prog); sErr == nil {
  186. for k, _ := range params {
  187. if c, check := current[k]; check {
  188. vm.Set(k, c)
  189. } else {
  190. vm.Set(k, nil)
  191. }
  192. }
  193. currentMod = tmp
  194. } else if err == nil {
  195. err = sErr
  196. vm.Interrupt(nil)
  197. }
  198. } else {
  199. err = fmt.Errorf("%v in module %v", err, currentMod)
  200. vm.Interrupt(err)
  201. }
  202. })
  203. vm.Set("exit", func(vals ...interface{}) {
  204. vm.Interrupt(err)
  205. })
  206. vm.Set("sleep", func(msec int) {
  207. time.Sleep(time.Millisecond * time.Duration(msec))
  208. })
  209. if _, exErr := vm.RunProgram(prog.prog); exErr != nil && exErr.Error() != "<nil>" && err == nil {
  210. err = exErr
  211. }
  212. return
  213. }