mjs.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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("define", func(vals ...interface{}) {
  118. fCall, tmp := goja.FunctionCall{}, currentMod
  119. parentMod = tmp
  120. for _, v := range vals {
  121. if call, check := v.(func(goja.FunctionCall) goja.Value); check {
  122. currentMod = tmp
  123. parentMod = currentMod
  124. mods[currentMod] = call(fCall)
  125. return
  126. } else {
  127. currentMod = fmt.Sprint(v)
  128. fCall.Arguments = append(fCall.Arguments, getMod(currentMod))
  129. if err != nil && err.Error() != "<nil>" {
  130. return
  131. }
  132. }
  133. }
  134. })
  135. vm.Set("require", func(vals ...interface{}) {
  136. fCall := goja.FunctionCall{}
  137. for _, v := range vals {
  138. if call, check := v.(func(goja.FunctionCall) goja.Value); check {
  139. call(fCall)
  140. return
  141. } else {
  142. currentMod = fmt.Sprint(v)
  143. fCall.Arguments = append(fCall.Arguments, getMod(currentMod))
  144. if err != nil {
  145. return
  146. }
  147. }
  148. }
  149. })
  150. vm.Set("initError", func(errText string) error {
  151. return errors.New(errText)
  152. })
  153. vm.Set("fileExists", func(path string) bool {
  154. return s.callbackModified(path) > 0
  155. })
  156. vm.Set("include", func(vals ...interface{}) {
  157. if len(vals) == 0 {
  158. return
  159. }
  160. name := fmt.Sprint(vals[0])
  161. params := make(map[string]interface{})
  162. if len(vals) == 2 {
  163. if m, check := vals[1].(map[string]interface{}); check {
  164. params = m
  165. }
  166. }
  167. var prog *jsProgram
  168. if prog, err = s.program(name); err == nil {
  169. tmp := currentMod
  170. currentMod = prog.name
  171. if prog.modified > modified {
  172. modified = prog.modified
  173. }
  174. current := make(map[string]interface{})
  175. for k, v := range params {
  176. if val := vm.Get(k); val != nil {
  177. current[k] = val
  178. }
  179. vm.Set(k, v)
  180. }
  181. if _, sErr := vm.RunProgram(prog.prog); sErr == nil {
  182. for k, _ := range params {
  183. if c, check := current[k]; check {
  184. vm.Set(k, c)
  185. } else {
  186. vm.Set(k, nil)
  187. }
  188. }
  189. currentMod = tmp
  190. } else if err == nil {
  191. err = sErr
  192. vm.Interrupt(nil)
  193. }
  194. } else {
  195. err = fmt.Errorf("%v in module %v", err, currentMod)
  196. vm.Interrupt(err)
  197. }
  198. })
  199. vm.Set("exit", func(vals ...interface{}) {
  200. vm.Interrupt(err)
  201. })
  202. vm.Set("sleep", func(msec int) {
  203. log.Println("sleep")
  204. time.Sleep(time.Millisecond * time.Duration(msec))
  205. })
  206. if _, exErr := vm.RunProgram(prog.prog); exErr != nil && exErr.Error() != "<nil>" && err == nil {
  207. err = exErr
  208. }
  209. return
  210. }