builtin_weakmap.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. package goja
  2. import "sync"
  3. type weakMap struct {
  4. // need to synchronise access to the data map because it may be accessed
  5. // from the finalizer goroutine
  6. sync.Mutex
  7. data map[uint64]Value
  8. }
  9. type weakMapObject struct {
  10. baseObject
  11. m *weakMap
  12. }
  13. func newWeakMap() *weakMap {
  14. return &weakMap{
  15. data: make(map[uint64]Value),
  16. }
  17. }
  18. func (wmo *weakMapObject) init() {
  19. wmo.baseObject.init()
  20. wmo.m = newWeakMap()
  21. }
  22. func (wm *weakMap) removeId(id uint64) {
  23. wm.Lock()
  24. delete(wm.data, id)
  25. wm.Unlock()
  26. }
  27. func (wm *weakMap) set(key *Object, value Value) {
  28. refs := key.getWeakCollRefs()
  29. wm.Lock()
  30. wm.data[refs.id()] = value
  31. wm.Unlock()
  32. refs.add(wm)
  33. }
  34. func (wm *weakMap) get(key *Object) Value {
  35. refs := key.weakColls
  36. if refs == nil {
  37. return nil
  38. }
  39. wm.Lock()
  40. ret := wm.data[refs.id()]
  41. wm.Unlock()
  42. return ret
  43. }
  44. func (wm *weakMap) remove(key *Object) bool {
  45. refs := key.weakColls
  46. if refs == nil {
  47. return false
  48. }
  49. id := refs.id()
  50. wm.Lock()
  51. _, exists := wm.data[id]
  52. if exists {
  53. delete(wm.data, id)
  54. }
  55. wm.Unlock()
  56. if exists {
  57. refs.remove(wm)
  58. }
  59. return exists
  60. }
  61. func (wm *weakMap) has(key *Object) bool {
  62. refs := key.weakColls
  63. if refs == nil {
  64. return false
  65. }
  66. id := refs.id()
  67. wm.Lock()
  68. _, exists := wm.data[id]
  69. wm.Unlock()
  70. return exists
  71. }
  72. func (r *Runtime) weakMapProto_delete(call FunctionCall) Value {
  73. thisObj := r.toObject(call.This)
  74. wmo, ok := thisObj.self.(*weakMapObject)
  75. if !ok {
  76. panic(r.NewTypeError("Method WeakMap.prototype.delete called on incompatible receiver %s", thisObj.String()))
  77. }
  78. key, ok := call.Argument(0).(*Object)
  79. if ok && wmo.m.remove(key) {
  80. return valueTrue
  81. }
  82. return valueFalse
  83. }
  84. func (r *Runtime) weakMapProto_get(call FunctionCall) Value {
  85. thisObj := r.toObject(call.This)
  86. wmo, ok := thisObj.self.(*weakMapObject)
  87. if !ok {
  88. panic(r.NewTypeError("Method WeakMap.prototype.get called on incompatible receiver %s", thisObj.String()))
  89. }
  90. var res Value
  91. if key, ok := call.Argument(0).(*Object); ok {
  92. res = wmo.m.get(key)
  93. }
  94. if res == nil {
  95. return _undefined
  96. }
  97. return res
  98. }
  99. func (r *Runtime) weakMapProto_has(call FunctionCall) Value {
  100. thisObj := r.toObject(call.This)
  101. wmo, ok := thisObj.self.(*weakMapObject)
  102. if !ok {
  103. panic(r.NewTypeError("Method WeakMap.prototype.has called on incompatible receiver %s", thisObj.String()))
  104. }
  105. key, ok := call.Argument(0).(*Object)
  106. if ok && wmo.m.has(key) {
  107. return valueTrue
  108. }
  109. return valueFalse
  110. }
  111. func (r *Runtime) weakMapProto_set(call FunctionCall) Value {
  112. thisObj := r.toObject(call.This)
  113. wmo, ok := thisObj.self.(*weakMapObject)
  114. if !ok {
  115. panic(r.NewTypeError("Method WeakMap.prototype.set called on incompatible receiver %s", thisObj.String()))
  116. }
  117. key := r.toObject(call.Argument(0))
  118. wmo.m.set(key, call.Argument(1))
  119. return call.This
  120. }
  121. func (r *Runtime) needNew(name string) *Object {
  122. return r.NewTypeError("Constructor %s requires 'new'", name)
  123. }
  124. func (r *Runtime) getPrototypeFromCtor(newTarget, defCtor, defProto *Object) *Object {
  125. if newTarget == defCtor {
  126. return defProto
  127. }
  128. proto := newTarget.self.getStr("prototype", nil)
  129. if obj, ok := proto.(*Object); ok {
  130. return obj
  131. }
  132. return defProto
  133. }
  134. func (r *Runtime) builtin_newWeakMap(args []Value, newTarget *Object) *Object {
  135. if newTarget == nil {
  136. panic(r.needNew("WeakMap"))
  137. }
  138. proto := r.getPrototypeFromCtor(newTarget, r.global.WeakMap, r.global.WeakMapPrototype)
  139. o := &Object{runtime: r}
  140. wmo := &weakMapObject{}
  141. wmo.class = classWeakMap
  142. wmo.val = o
  143. wmo.extensible = true
  144. o.self = wmo
  145. wmo.prototype = proto
  146. wmo.init()
  147. if len(args) > 0 {
  148. if arg := args[0]; arg != nil && arg != _undefined && arg != _null {
  149. adder := wmo.getStr("set", nil)
  150. iter := r.getIterator(arg, nil)
  151. i0 := valueInt(0)
  152. i1 := valueInt(1)
  153. if adder == r.global.weakMapAdder {
  154. r.iterate(iter, func(item Value) {
  155. itemObj := r.toObject(item)
  156. k := itemObj.self.getIdx(i0, nil)
  157. v := nilSafe(itemObj.self.getIdx(i1, nil))
  158. wmo.m.set(r.toObject(k), v)
  159. })
  160. } else {
  161. adderFn := toMethod(adder)
  162. if adderFn == nil {
  163. panic(r.NewTypeError("WeakMap.set in missing"))
  164. }
  165. r.iterate(iter, func(item Value) {
  166. itemObj := r.toObject(item)
  167. k := itemObj.self.getIdx(i0, nil)
  168. v := itemObj.self.getIdx(i1, nil)
  169. adderFn(FunctionCall{This: o, Arguments: []Value{k, v}})
  170. })
  171. }
  172. }
  173. }
  174. return o
  175. }
  176. func (r *Runtime) createWeakMapProto(val *Object) objectImpl {
  177. o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject)
  178. o._putProp("constructor", r.global.WeakMap, true, false, true)
  179. r.global.weakMapAdder = r.newNativeFunc(r.weakMapProto_set, nil, "set", nil, 2)
  180. o._putProp("set", r.global.weakMapAdder, true, false, true)
  181. o._putProp("delete", r.newNativeFunc(r.weakMapProto_delete, nil, "delete", nil, 1), true, false, true)
  182. o._putProp("has", r.newNativeFunc(r.weakMapProto_has, nil, "has", nil, 1), true, false, true)
  183. o._putProp("get", r.newNativeFunc(r.weakMapProto_get, nil, "get", nil, 1), true, false, true)
  184. o._putSym(symToStringTag, valueProp(asciiString(classWeakMap), false, false, true))
  185. return o
  186. }
  187. func (r *Runtime) createWeakMap(val *Object) objectImpl {
  188. o := r.newNativeConstructOnly(val, r.builtin_newWeakMap, r.global.WeakMapPrototype, "WeakMap", 0)
  189. return o
  190. }
  191. func (r *Runtime) initWeakMap() {
  192. r.global.WeakMapPrototype = r.newLazyObject(r.createWeakMapProto)
  193. r.global.WeakMap = r.newLazyObject(r.createWeakMap)
  194. r.addToGlobal("WeakMap", r.global.WeakMap)
  195. }