stash.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. package otto
  2. import (
  3. "fmt"
  4. )
  5. // stasher is implemented by types which can stash data.
  6. type stasher interface {
  7. hasBinding(name string) bool //
  8. createBinding(name string, deletable bool, value Value) // CreateMutableBinding
  9. setBinding(name string, value Value, strict bool) // SetMutableBinding
  10. getBinding(name string, throw bool) Value // GetBindingValue
  11. deleteBinding(name string) bool //
  12. setValue(name string, value Value, throw bool) // createBinding + setBinding
  13. outer() stasher
  14. runtime() *runtime
  15. newReference(name string, strict bool, atv at) referencer
  16. clone(cloner *cloner) stasher
  17. }
  18. type objectStash struct {
  19. rt *runtime
  20. outr stasher
  21. object *object
  22. }
  23. func (s *objectStash) runtime() *runtime {
  24. return s.rt
  25. }
  26. func (rt *runtime) newObjectStash(obj *object, outer stasher) *objectStash {
  27. if obj == nil {
  28. obj = rt.newBaseObject()
  29. obj.class = "environment"
  30. }
  31. return &objectStash{
  32. rt: rt,
  33. outr: outer,
  34. object: obj,
  35. }
  36. }
  37. func (s *objectStash) clone(c *cloner) stasher {
  38. out, exists := c.objectStash(s)
  39. if exists {
  40. return out
  41. }
  42. *out = objectStash{
  43. c.runtime,
  44. c.stash(s.outr),
  45. c.object(s.object),
  46. }
  47. return out
  48. }
  49. func (s *objectStash) hasBinding(name string) bool {
  50. return s.object.hasProperty(name)
  51. }
  52. func (s *objectStash) createBinding(name string, deletable bool, value Value) {
  53. if s.object.hasProperty(name) {
  54. panic(hereBeDragons())
  55. }
  56. mode := propertyMode(0o111)
  57. if !deletable {
  58. mode = propertyMode(0o110)
  59. }
  60. // TODO False?
  61. s.object.defineProperty(name, value, mode, false)
  62. }
  63. func (s *objectStash) setBinding(name string, value Value, strict bool) {
  64. s.object.put(name, value, strict)
  65. }
  66. func (s *objectStash) setValue(name string, value Value, throw bool) {
  67. if !s.hasBinding(name) {
  68. s.createBinding(name, true, value) // Configurable by default
  69. } else {
  70. s.setBinding(name, value, throw)
  71. }
  72. }
  73. func (s *objectStash) getBinding(name string, throw bool) Value {
  74. if s.object.hasProperty(name) {
  75. return s.object.get(name)
  76. }
  77. if throw { // strict?
  78. panic(s.rt.panicReferenceError("Not Defined", name))
  79. }
  80. return Value{}
  81. }
  82. func (s *objectStash) deleteBinding(name string) bool {
  83. return s.object.delete(name, false)
  84. }
  85. func (s *objectStash) outer() stasher {
  86. return s.outr
  87. }
  88. func (s *objectStash) newReference(name string, strict bool, atv at) referencer {
  89. return newPropertyReference(s.rt, s.object, name, strict, atv)
  90. }
  91. type dclStash struct {
  92. rt *runtime
  93. outr stasher
  94. property map[string]dclProperty
  95. }
  96. type dclProperty struct {
  97. value Value
  98. mutable bool
  99. deletable bool
  100. readable bool
  101. }
  102. func (rt *runtime) newDeclarationStash(outer stasher) *dclStash {
  103. return &dclStash{
  104. rt: rt,
  105. outr: outer,
  106. property: map[string]dclProperty{},
  107. }
  108. }
  109. func (s *dclStash) clone(c *cloner) stasher {
  110. out, exists := c.dclStash(s)
  111. if exists {
  112. return out
  113. }
  114. prop := make(map[string]dclProperty, len(s.property))
  115. for index, value := range s.property {
  116. prop[index] = c.dclProperty(value)
  117. }
  118. *out = dclStash{
  119. c.runtime,
  120. c.stash(s.outr),
  121. prop,
  122. }
  123. return out
  124. }
  125. func (s *dclStash) hasBinding(name string) bool {
  126. _, exists := s.property[name]
  127. return exists
  128. }
  129. func (s *dclStash) runtime() *runtime {
  130. return s.rt
  131. }
  132. func (s *dclStash) createBinding(name string, deletable bool, value Value) {
  133. if _, exists := s.property[name]; exists {
  134. panic(fmt.Errorf("createBinding: %s: already exists", name))
  135. }
  136. s.property[name] = dclProperty{
  137. value: value,
  138. mutable: true,
  139. deletable: deletable,
  140. readable: false,
  141. }
  142. }
  143. func (s *dclStash) setBinding(name string, value Value, strict bool) {
  144. prop, exists := s.property[name]
  145. if !exists {
  146. panic(fmt.Errorf("setBinding: %s: missing", name))
  147. }
  148. if prop.mutable {
  149. prop.value = value
  150. s.property[name] = prop
  151. } else {
  152. s.rt.typeErrorResult(strict)
  153. }
  154. }
  155. func (s *dclStash) setValue(name string, value Value, throw bool) {
  156. if !s.hasBinding(name) {
  157. s.createBinding(name, false, value) // NOT deletable by default
  158. } else {
  159. s.setBinding(name, value, throw)
  160. }
  161. }
  162. // FIXME This is called a __lot__.
  163. func (s *dclStash) getBinding(name string, throw bool) Value {
  164. prop, exists := s.property[name]
  165. if !exists {
  166. panic(fmt.Errorf("getBinding: %s: missing", name))
  167. }
  168. if !prop.mutable && !prop.readable {
  169. if throw { // strict?
  170. panic(s.rt.panicTypeError("getBinding property %s not mutable and not readable", name))
  171. }
  172. return Value{}
  173. }
  174. return prop.value
  175. }
  176. func (s *dclStash) deleteBinding(name string) bool {
  177. prop, exists := s.property[name]
  178. if !exists {
  179. return true
  180. }
  181. if !prop.deletable {
  182. return false
  183. }
  184. delete(s.property, name)
  185. return true
  186. }
  187. func (s *dclStash) outer() stasher {
  188. return s.outr
  189. }
  190. func (s *dclStash) newReference(name string, strict bool, _ at) referencer {
  191. return &stashReference{
  192. name: name,
  193. base: s,
  194. }
  195. }
  196. // ========
  197. // _fnStash
  198. // ========
  199. type fnStash struct {
  200. dclStash
  201. arguments *object
  202. indexOfArgumentName map[string]string
  203. }
  204. func (rt *runtime) newFunctionStash(outer stasher) *fnStash {
  205. return &fnStash{
  206. dclStash: dclStash{
  207. rt: rt,
  208. outr: outer,
  209. property: map[string]dclProperty{},
  210. },
  211. }
  212. }
  213. func (s *fnStash) clone(c *cloner) stasher {
  214. out, exists := c.fnStash(s)
  215. if exists {
  216. return out
  217. }
  218. dclStash := s.dclStash.clone(c).(*dclStash)
  219. index := make(map[string]string, len(s.indexOfArgumentName))
  220. for name, value := range s.indexOfArgumentName {
  221. index[name] = value
  222. }
  223. *out = fnStash{
  224. dclStash: *dclStash,
  225. arguments: c.object(s.arguments),
  226. indexOfArgumentName: index,
  227. }
  228. return out
  229. }
  230. // getStashProperties returns the properties from stash.
  231. func getStashProperties(stash stasher) []string {
  232. switch vars := stash.(type) {
  233. case *dclStash:
  234. keys := make([]string, 0, len(vars.property))
  235. for k := range vars.property {
  236. keys = append(keys, k)
  237. }
  238. return keys
  239. case *fnStash:
  240. keys := make([]string, 0, len(vars.property))
  241. for k := range vars.property {
  242. keys = append(keys, k)
  243. }
  244. return keys
  245. case *objectStash:
  246. keys := make([]string, 0, len(vars.object.property))
  247. for k := range vars.object.property {
  248. keys = append(keys, k)
  249. }
  250. return keys
  251. default:
  252. panic("unknown stash type")
  253. }
  254. }