mjs.go 5.4 KB

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