script.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. package otto
  2. import (
  3. "bytes"
  4. "encoding/gob"
  5. "errors"
  6. )
  7. // ErrVersion is an error which represents a version mismatch.
  8. var ErrVersion = errors.New("version mismatch")
  9. var scriptVersion = "2014-04-13/1"
  10. // Script is a handle for some (reusable) JavaScript.
  11. // Passing a Script value to a run method will evaluate the JavaScript.
  12. type Script struct {
  13. version string
  14. program *nodeProgram
  15. filename string
  16. src string
  17. }
  18. // Compile will parse the given source and return a Script value or nil and
  19. // an error if there was a problem during compilation.
  20. //
  21. // script, err := vm.Compile("", `var abc; if (!abc) abc = 0; abc += 2; abc;`)
  22. // vm.Run(script)
  23. func (o *Otto) Compile(filename string, src interface{}) (*Script, error) {
  24. return o.CompileWithSourceMap(filename, src, nil)
  25. }
  26. // CompileWithSourceMap does the same thing as Compile, but with the obvious
  27. // difference of applying a source map.
  28. func (o *Otto) CompileWithSourceMap(filename string, src, sm interface{}) (*Script, error) {
  29. program, err := o.runtime.parse(filename, src, sm)
  30. if err != nil {
  31. return nil, err
  32. }
  33. node := cmplParse(program)
  34. script := &Script{
  35. version: scriptVersion,
  36. program: node,
  37. filename: filename,
  38. src: program.File.Source(),
  39. }
  40. return script, nil
  41. }
  42. func (s *Script) String() string {
  43. return "// " + s.filename + "\n" + s.src
  44. }
  45. // MarshalBinary will marshal a script into a binary form. A marshalled script
  46. // that is later unmarshalled can be executed on the same version of the otto runtime.
  47. //
  48. // The binary format can change at any time and should be considered unspecified and opaque.
  49. func (s *Script) marshalBinary() ([]byte, error) {
  50. var bfr bytes.Buffer
  51. encoder := gob.NewEncoder(&bfr)
  52. err := encoder.Encode(s.version)
  53. if err != nil {
  54. return nil, err
  55. }
  56. err = encoder.Encode(s.program)
  57. if err != nil {
  58. return nil, err
  59. }
  60. err = encoder.Encode(s.filename)
  61. if err != nil {
  62. return nil, err
  63. }
  64. err = encoder.Encode(s.src)
  65. if err != nil {
  66. return nil, err
  67. }
  68. return bfr.Bytes(), nil
  69. }
  70. // UnmarshalBinary will vivify a marshalled script into something usable. If the script was
  71. // originally marshalled on a different version of the otto runtime, then this method
  72. // will return an error.
  73. //
  74. // The binary format can change at any time and should be considered unspecified and opaque.
  75. func (s *Script) unmarshalBinary(data []byte) (err error) { //nolint:nonamedreturns
  76. decoder := gob.NewDecoder(bytes.NewReader(data))
  77. defer func() {
  78. if err != nil {
  79. s.version = ""
  80. s.program = nil
  81. s.filename = ""
  82. s.src = ""
  83. }
  84. }()
  85. if err = decoder.Decode(&s.version); err != nil {
  86. return err
  87. }
  88. if s.version != scriptVersion {
  89. return ErrVersion
  90. }
  91. if err = decoder.Decode(&s.program); err != nil {
  92. return err
  93. }
  94. if err = decoder.Decode(&s.filename); err != nil {
  95. return err
  96. }
  97. return decoder.Decode(&s.src)
  98. }