clone.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. package otto
  2. import (
  3. "fmt"
  4. )
  5. type cloner struct {
  6. runtime *runtime
  7. obj map[*object]*object
  8. objectstash map[*objectStash]*objectStash
  9. dclstash map[*dclStash]*dclStash
  10. fnstash map[*fnStash]*fnStash
  11. }
  12. func (rt *runtime) clone() *runtime {
  13. rt.lck.Lock()
  14. defer rt.lck.Unlock()
  15. out := &runtime{
  16. debugger: rt.debugger,
  17. random: rt.random,
  18. stackLimit: rt.stackLimit,
  19. traceLimit: rt.traceLimit,
  20. }
  21. c := cloner{
  22. runtime: out,
  23. obj: make(map[*object]*object),
  24. objectstash: make(map[*objectStash]*objectStash),
  25. dclstash: make(map[*dclStash]*dclStash),
  26. fnstash: make(map[*fnStash]*fnStash),
  27. }
  28. globalObject := c.object(rt.globalObject)
  29. out.globalStash = out.newObjectStash(globalObject, nil)
  30. out.globalObject = globalObject
  31. out.global = global{
  32. c.object(rt.global.Object),
  33. c.object(rt.global.Function),
  34. c.object(rt.global.Array),
  35. c.object(rt.global.String),
  36. c.object(rt.global.Boolean),
  37. c.object(rt.global.Number),
  38. c.object(rt.global.Math),
  39. c.object(rt.global.Date),
  40. c.object(rt.global.RegExp),
  41. c.object(rt.global.Error),
  42. c.object(rt.global.EvalError),
  43. c.object(rt.global.TypeError),
  44. c.object(rt.global.RangeError),
  45. c.object(rt.global.ReferenceError),
  46. c.object(rt.global.SyntaxError),
  47. c.object(rt.global.URIError),
  48. c.object(rt.global.JSON),
  49. c.object(rt.global.ObjectPrototype),
  50. c.object(rt.global.FunctionPrototype),
  51. c.object(rt.global.ArrayPrototype),
  52. c.object(rt.global.StringPrototype),
  53. c.object(rt.global.BooleanPrototype),
  54. c.object(rt.global.NumberPrototype),
  55. c.object(rt.global.DatePrototype),
  56. c.object(rt.global.RegExpPrototype),
  57. c.object(rt.global.ErrorPrototype),
  58. c.object(rt.global.EvalErrorPrototype),
  59. c.object(rt.global.TypeErrorPrototype),
  60. c.object(rt.global.RangeErrorPrototype),
  61. c.object(rt.global.ReferenceErrorPrototype),
  62. c.object(rt.global.SyntaxErrorPrototype),
  63. c.object(rt.global.URIErrorPrototype),
  64. }
  65. out.eval = out.globalObject.property["eval"].value.(Value).value.(*object)
  66. out.globalObject.prototype = out.global.ObjectPrototype
  67. // Not sure if this is necessary, but give some help to the GC
  68. c.runtime = nil
  69. c.obj = nil
  70. c.objectstash = nil
  71. c.dclstash = nil
  72. c.fnstash = nil
  73. return out
  74. }
  75. func (c *cloner) object(in *object) *object {
  76. if out, exists := c.obj[in]; exists {
  77. return out
  78. }
  79. out := &object{}
  80. c.obj[in] = out
  81. return in.objectClass.clone(in, out, c)
  82. }
  83. func (c *cloner) dclStash(in *dclStash) (*dclStash, bool) {
  84. if out, exists := c.dclstash[in]; exists {
  85. return out, true
  86. }
  87. out := &dclStash{}
  88. c.dclstash[in] = out
  89. return out, false
  90. }
  91. func (c *cloner) objectStash(in *objectStash) (*objectStash, bool) {
  92. if out, exists := c.objectstash[in]; exists {
  93. return out, true
  94. }
  95. out := &objectStash{}
  96. c.objectstash[in] = out
  97. return out, false
  98. }
  99. func (c *cloner) fnStash(in *fnStash) (*fnStash, bool) {
  100. if out, exists := c.fnstash[in]; exists {
  101. return out, true
  102. }
  103. out := &fnStash{}
  104. c.fnstash[in] = out
  105. return out, false
  106. }
  107. func (c *cloner) value(in Value) Value {
  108. out := in
  109. if value, ok := in.value.(*object); ok {
  110. out.value = c.object(value)
  111. }
  112. return out
  113. }
  114. func (c *cloner) valueArray(in []Value) []Value {
  115. out := make([]Value, len(in))
  116. for index, value := range in {
  117. out[index] = c.value(value)
  118. }
  119. return out
  120. }
  121. func (c *cloner) stash(in stasher) stasher {
  122. if in == nil {
  123. return nil
  124. }
  125. return in.clone(c)
  126. }
  127. func (c *cloner) property(in property) property {
  128. out := in
  129. switch value := in.value.(type) {
  130. case Value:
  131. out.value = c.value(value)
  132. case propertyGetSet:
  133. p := propertyGetSet{}
  134. if value[0] != nil {
  135. p[0] = c.object(value[0])
  136. }
  137. if value[1] != nil {
  138. p[1] = c.object(value[1])
  139. }
  140. out.value = p
  141. default:
  142. panic(fmt.Errorf("in.value.(Value) != true; in.value is %T", in.value))
  143. }
  144. return out
  145. }
  146. func (c *cloner) dclProperty(in dclProperty) dclProperty {
  147. out := in
  148. out.value = c.value(in.value)
  149. return out
  150. }