builtin_json.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. package otto
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "strings"
  7. )
  8. type builtinJSONParseContext struct {
  9. reviver Value
  10. call FunctionCall
  11. }
  12. func builtinJSONParse(call FunctionCall) Value {
  13. ctx := builtinJSONParseContext{
  14. call: call,
  15. }
  16. revive := false
  17. if reviver := call.Argument(1); reviver.isCallable() {
  18. revive = true
  19. ctx.reviver = reviver
  20. }
  21. var root interface{}
  22. err := json.Unmarshal([]byte(call.Argument(0).string()), &root)
  23. if err != nil {
  24. panic(call.runtime.panicSyntaxError(err.Error()))
  25. }
  26. value, exists := builtinJSONParseWalk(ctx, root)
  27. if !exists {
  28. value = Value{}
  29. }
  30. if revive {
  31. root := ctx.call.runtime.newObject()
  32. root.put("", value, false)
  33. return builtinJSONReviveWalk(ctx, root, "")
  34. }
  35. return value
  36. }
  37. func builtinJSONReviveWalk(ctx builtinJSONParseContext, holder *object, name string) Value {
  38. value := holder.get(name)
  39. if obj := value.object(); obj != nil {
  40. if isArray(obj) {
  41. length := int64(objectLength(obj))
  42. for index := range length {
  43. idxName := arrayIndexToString(index)
  44. idxValue := builtinJSONReviveWalk(ctx, obj, idxName)
  45. if idxValue.IsUndefined() {
  46. obj.delete(idxName, false)
  47. } else {
  48. obj.defineProperty(idxName, idxValue, 0o111, false)
  49. }
  50. }
  51. } else {
  52. obj.enumerate(false, func(name string) bool {
  53. enumVal := builtinJSONReviveWalk(ctx, obj, name)
  54. if enumVal.IsUndefined() {
  55. obj.delete(name, false)
  56. } else {
  57. obj.defineProperty(name, enumVal, 0o111, false)
  58. }
  59. return true
  60. })
  61. }
  62. }
  63. return ctx.reviver.call(ctx.call.runtime, objectValue(holder), name, value)
  64. }
  65. func builtinJSONParseWalk(ctx builtinJSONParseContext, rawValue interface{}) (Value, bool) {
  66. switch value := rawValue.(type) {
  67. case nil:
  68. return nullValue, true
  69. case bool:
  70. return boolValue(value), true
  71. case string:
  72. return stringValue(value), true
  73. case float64:
  74. return float64Value(value), true
  75. case []interface{}:
  76. arrayValue := make([]Value, len(value))
  77. for index, rawValue := range value {
  78. if value, exists := builtinJSONParseWalk(ctx, rawValue); exists {
  79. arrayValue[index] = value
  80. }
  81. }
  82. return objectValue(ctx.call.runtime.newArrayOf(arrayValue)), true
  83. case map[string]interface{}:
  84. obj := ctx.call.runtime.newObject()
  85. for name, rawValue := range value {
  86. if value, exists := builtinJSONParseWalk(ctx, rawValue); exists {
  87. obj.put(name, value, false)
  88. }
  89. }
  90. return objectValue(obj), true
  91. }
  92. return Value{}, false
  93. }
  94. type builtinJSONStringifyContext struct {
  95. replacerFunction *Value
  96. gap string
  97. stack []*object
  98. propertyList []string
  99. call FunctionCall
  100. }
  101. func builtinJSONStringify(call FunctionCall) Value {
  102. ctx := builtinJSONStringifyContext{
  103. call: call,
  104. stack: []*object{nil},
  105. }
  106. replacer := call.Argument(1).object()
  107. if replacer != nil {
  108. if isArray(replacer) {
  109. length := objectLength(replacer)
  110. seen := map[string]bool{}
  111. propertyList := make([]string, length)
  112. length = 0
  113. for index := range propertyList {
  114. value := replacer.get(arrayIndexToString(int64(index)))
  115. switch value.kind {
  116. case valueObject:
  117. switch value.value.(*object).class {
  118. case classStringName, classNumberName:
  119. default:
  120. continue
  121. }
  122. case valueString, valueNumber:
  123. default:
  124. continue
  125. }
  126. name := value.string()
  127. if seen[name] {
  128. continue
  129. }
  130. seen[name] = true
  131. length++
  132. propertyList[index] = name
  133. }
  134. ctx.propertyList = propertyList[0:length]
  135. } else if replacer.class == classFunctionName {
  136. value := objectValue(replacer)
  137. ctx.replacerFunction = &value
  138. }
  139. }
  140. if spaceValue, exists := call.getArgument(2); exists {
  141. if spaceValue.kind == valueObject {
  142. switch spaceValue.value.(*object).class {
  143. case classStringName:
  144. spaceValue = stringValue(spaceValue.string())
  145. case classNumberName:
  146. spaceValue = spaceValue.numberValue()
  147. }
  148. }
  149. switch spaceValue.kind {
  150. case valueString:
  151. value := spaceValue.string()
  152. if len(value) > 10 {
  153. ctx.gap = value[0:10]
  154. } else {
  155. ctx.gap = value
  156. }
  157. case valueNumber:
  158. value := spaceValue.number().int64
  159. if value > 10 {
  160. value = 10
  161. } else if value < 0 {
  162. value = 0
  163. }
  164. ctx.gap = strings.Repeat(" ", int(value))
  165. }
  166. }
  167. holder := call.runtime.newObject()
  168. holder.put("", call.Argument(0), false)
  169. value, exists := builtinJSONStringifyWalk(ctx, "", holder)
  170. if !exists {
  171. return Value{}
  172. }
  173. valueJSON, err := json.Marshal(value)
  174. if err != nil {
  175. panic(call.runtime.panicTypeError("JSON.stringify marshal: %s", err))
  176. }
  177. if ctx.gap != "" {
  178. valueJSON1 := bytes.Buffer{}
  179. if err = json.Indent(&valueJSON1, valueJSON, "", ctx.gap); err != nil {
  180. panic(call.runtime.panicTypeError("JSON.stringify indent: %s", err))
  181. }
  182. valueJSON = valueJSON1.Bytes()
  183. }
  184. return stringValue(string(valueJSON))
  185. }
  186. func builtinJSONStringifyWalk(ctx builtinJSONStringifyContext, key string, holder *object) (interface{}, bool) {
  187. value := holder.get(key)
  188. if value.IsObject() {
  189. obj := value.object()
  190. if toJSON := obj.get("toJSON"); toJSON.IsFunction() {
  191. value = toJSON.call(ctx.call.runtime, value, key)
  192. } else if obj.objectClass.marshalJSON != nil {
  193. // If the object is a GoStruct or something that implements json.Marshaler
  194. marshaler := obj.objectClass.marshalJSON(obj)
  195. if marshaler != nil {
  196. return marshaler, true
  197. }
  198. }
  199. }
  200. if ctx.replacerFunction != nil {
  201. value = ctx.replacerFunction.call(ctx.call.runtime, objectValue(holder), key, value)
  202. }
  203. if value.kind == valueObject {
  204. switch value.value.(*object).class {
  205. case classBooleanName:
  206. value = value.object().value.(Value)
  207. case classStringName:
  208. value = stringValue(value.string())
  209. case classNumberName:
  210. value = value.numberValue()
  211. }
  212. }
  213. switch value.kind {
  214. case valueBoolean:
  215. return value.bool(), true
  216. case valueString:
  217. return value.string(), true
  218. case valueNumber:
  219. integer := value.number()
  220. switch integer.kind {
  221. case numberInteger:
  222. return integer.int64, true
  223. case numberFloat:
  224. return integer.float64, true
  225. default:
  226. return nil, true
  227. }
  228. case valueNull:
  229. return nil, true
  230. case valueObject:
  231. objHolder := value.object()
  232. if value := value.object(); nil != value {
  233. for _, obj := range ctx.stack {
  234. if objHolder == obj {
  235. panic(ctx.call.runtime.panicTypeError("Converting circular structure to JSON"))
  236. }
  237. }
  238. ctx.stack = append(ctx.stack, value)
  239. defer func() { ctx.stack = ctx.stack[:len(ctx.stack)-1] }()
  240. }
  241. if isArray(objHolder) {
  242. var length uint32
  243. switch value := objHolder.get(propertyLength).value.(type) {
  244. case uint32:
  245. length = value
  246. case int:
  247. if value >= 0 {
  248. length = uint32(value)
  249. }
  250. default:
  251. panic(ctx.call.runtime.panicTypeError(fmt.Sprintf("JSON.stringify: invalid length: %v (%[1]T)", value)))
  252. }
  253. array := make([]interface{}, length)
  254. for index := range array {
  255. name := arrayIndexToString(int64(index))
  256. value, _ := builtinJSONStringifyWalk(ctx, name, objHolder)
  257. array[index] = value
  258. }
  259. return array, true
  260. } else if objHolder.class != classFunctionName {
  261. obj := map[string]interface{}{}
  262. if ctx.propertyList != nil {
  263. for _, name := range ctx.propertyList {
  264. value, exists := builtinJSONStringifyWalk(ctx, name, objHolder)
  265. if exists {
  266. obj[name] = value
  267. }
  268. }
  269. } else {
  270. // Go maps are without order, so this doesn't conform to the ECMA ordering
  271. // standard, but oh well...
  272. objHolder.enumerate(false, func(name string) bool {
  273. value, exists := builtinJSONStringifyWalk(ctx, name, objHolder)
  274. if exists {
  275. obj[name] = value
  276. }
  277. return true
  278. })
  279. }
  280. return obj, true
  281. }
  282. }
  283. return nil, false
  284. }