mjs.go 5.4 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. if exists {
  27. // check update need
  28. prog := val.(*jsProgram)
  29. // Why modified > prog.modified???
  30. if modified := s.callbackModified(name); modified == 0 || modified <= prog.modified {
  31. needUpdate = false
  32. return
  33. }
  34. }
  35. t := s.callbackModified(name)
  36. if t == 0 {
  37. err = fmt.Errorf("DOCUMENT %v NOT FOUND", name)
  38. return nil, nil, false
  39. }
  40. var content []byte
  41. if content, err = s.callbackContent(name); err != nil {
  42. return nil, nil, false
  43. }
  44. item := initProgram(name, string(content), t, s)
  45. return name, item, true
  46. })
  47. if check {
  48. prog = res.(*jsProgram)
  49. err = prog.compileErr
  50. } else {
  51. err = fmt.Errorf("Document %v is not defined", name)
  52. }
  53. return
  54. }
  55. func (s *Mjs) Exec(name string, data map[string]interface{}) (modified int64, err error) {
  56. defer func() {
  57. if err != nil && err.Error() == "<nil>" {
  58. err = nil
  59. }
  60. if r := recover(); r != nil && fmt.Sprint(r) != "runtime error: invalid memory address or nil pointer dereference" {
  61. err = fmt.Errorf("%v", r)
  62. }
  63. }()
  64. var prog *jsProgram
  65. if prog, err = s.program(name); err != nil {
  66. return
  67. }
  68. modified = prog.modified
  69. vm := goja.New()
  70. for k, v := range data {
  71. vm.Set(k, v)
  72. }
  73. time.AfterFunc(s.durationMax, func() {
  74. vm.Interrupt(fmt.Sprintf("Exec script timeout (%v sec.)", s.durationMax/time.Second))
  75. })
  76. var incBefore []string
  77. mods, currentMod, parentMod := make(map[string]goja.Value), name, ""
  78. checkIncBefore := func(name string) bool {
  79. for _, v := range incBefore {
  80. if v == name {
  81. return true
  82. }
  83. }
  84. return false
  85. }
  86. getMod := func(name string) goja.Value {
  87. if parentMod == name {
  88. err = fmt.Errorf("include loop detected %v in module %v", name, name)
  89. return nil
  90. }
  91. if mod, check := mods[name]; check {
  92. return mod
  93. } else {
  94. if !checkIncBefore(name) {
  95. var prog *jsProgram
  96. if prog, err = s.program(name); err != nil {
  97. err = fmt.Errorf("%v in module %v", err, parentMod)
  98. vm.Interrupt(nil)
  99. return nil
  100. } else {
  101. if prog.modified > modified {
  102. modified = prog.modified
  103. }
  104. if _, sErr := vm.RunProgram(prog.prog); sErr == nil {
  105. incBefore = append(incBefore, name)
  106. return mods[name]
  107. } else if err == nil && sErr.Error() != "<nil>" {
  108. err = fmt.Errorf("%v in module %v", sErr, parentMod)
  109. }
  110. return nil
  111. }
  112. }
  113. }
  114. return nil
  115. }
  116. vm.Set("runtime", vm)
  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. time.Sleep(time.Millisecond * time.Duration(msec))
  207. })
  208. if _, exErr := vm.RunProgram(prog.prog); exErr != nil && exErr.Error() != "<nil>" && err == nil {
  209. err = exErr
  210. }
  211. return
  212. }