encode.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. // A modified version of Go's JSON implementation.
  2. // Copyright 2010, 2020 The Go Authors. All rights reserved.
  3. // Use of this source code is governed by a BSD-style
  4. // license that can be found in the LICENSE file.
  5. package json
  6. import (
  7. "bytes"
  8. "encoding/base64"
  9. "errors"
  10. "math"
  11. "strconv"
  12. "unicode/utf8"
  13. "github.com/d5/tengo/v2"
  14. )
  15. // safeSet holds the value true if the ASCII character with the given array
  16. // position can be represented inside a JSON string without any further
  17. // escaping.
  18. //
  19. // All values are true except for the ASCII control characters (0-31), the
  20. // double quote ("), and the backslash character ("\").
  21. var safeSet = [utf8.RuneSelf]bool{
  22. ' ': true,
  23. '!': true,
  24. '"': false,
  25. '#': true,
  26. '$': true,
  27. '%': true,
  28. '&': true,
  29. '\'': true,
  30. '(': true,
  31. ')': true,
  32. '*': true,
  33. '+': true,
  34. ',': true,
  35. '-': true,
  36. '.': true,
  37. '/': true,
  38. '0': true,
  39. '1': true,
  40. '2': true,
  41. '3': true,
  42. '4': true,
  43. '5': true,
  44. '6': true,
  45. '7': true,
  46. '8': true,
  47. '9': true,
  48. ':': true,
  49. ';': true,
  50. '<': true,
  51. '=': true,
  52. '>': true,
  53. '?': true,
  54. '@': true,
  55. 'A': true,
  56. 'B': true,
  57. 'C': true,
  58. 'D': true,
  59. 'E': true,
  60. 'F': true,
  61. 'G': true,
  62. 'H': true,
  63. 'I': true,
  64. 'J': true,
  65. 'K': true,
  66. 'L': true,
  67. 'M': true,
  68. 'N': true,
  69. 'O': true,
  70. 'P': true,
  71. 'Q': true,
  72. 'R': true,
  73. 'S': true,
  74. 'T': true,
  75. 'U': true,
  76. 'V': true,
  77. 'W': true,
  78. 'X': true,
  79. 'Y': true,
  80. 'Z': true,
  81. '[': true,
  82. '\\': false,
  83. ']': true,
  84. '^': true,
  85. '_': true,
  86. '`': true,
  87. 'a': true,
  88. 'b': true,
  89. 'c': true,
  90. 'd': true,
  91. 'e': true,
  92. 'f': true,
  93. 'g': true,
  94. 'h': true,
  95. 'i': true,
  96. 'j': true,
  97. 'k': true,
  98. 'l': true,
  99. 'm': true,
  100. 'n': true,
  101. 'o': true,
  102. 'p': true,
  103. 'q': true,
  104. 'r': true,
  105. 's': true,
  106. 't': true,
  107. 'u': true,
  108. 'v': true,
  109. 'w': true,
  110. 'x': true,
  111. 'y': true,
  112. 'z': true,
  113. '{': true,
  114. '|': true,
  115. '}': true,
  116. '~': true,
  117. '\u007f': true,
  118. }
  119. var hex = "0123456789abcdef"
  120. // Encode returns the JSON encoding of the object.
  121. func Encode(o tengo.Object) ([]byte, error) {
  122. var b []byte
  123. switch o := o.(type) {
  124. case *tengo.Array:
  125. b = append(b, '[')
  126. len1 := len(o.Value) - 1
  127. for idx, elem := range o.Value {
  128. eb, err := Encode(elem)
  129. if err != nil {
  130. return nil, err
  131. }
  132. b = append(b, eb...)
  133. if idx < len1 {
  134. b = append(b, ',')
  135. }
  136. }
  137. b = append(b, ']')
  138. case *tengo.ImmutableArray:
  139. b = append(b, '[')
  140. len1 := len(o.Value) - 1
  141. for idx, elem := range o.Value {
  142. eb, err := Encode(elem)
  143. if err != nil {
  144. return nil, err
  145. }
  146. b = append(b, eb...)
  147. if idx < len1 {
  148. b = append(b, ',')
  149. }
  150. }
  151. b = append(b, ']')
  152. case *tengo.Map:
  153. b = append(b, '{')
  154. len1 := len(o.Value) - 1
  155. idx := 0
  156. for key, value := range o.Value {
  157. b = encodeString(b, key)
  158. b = append(b, ':')
  159. eb, err := Encode(value)
  160. if err != nil {
  161. return nil, err
  162. }
  163. b = append(b, eb...)
  164. if idx < len1 {
  165. b = append(b, ',')
  166. }
  167. idx++
  168. }
  169. b = append(b, '}')
  170. case *tengo.ImmutableMap:
  171. b = append(b, '{')
  172. len1 := len(o.Value) - 1
  173. idx := 0
  174. for key, value := range o.Value {
  175. b = encodeString(b, key)
  176. b = append(b, ':')
  177. eb, err := Encode(value)
  178. if err != nil {
  179. return nil, err
  180. }
  181. b = append(b, eb...)
  182. if idx < len1 {
  183. b = append(b, ',')
  184. }
  185. idx++
  186. }
  187. b = append(b, '}')
  188. case *tengo.Bool:
  189. if o.IsFalsy() {
  190. b = strconv.AppendBool(b, false)
  191. } else {
  192. b = strconv.AppendBool(b, true)
  193. }
  194. case *tengo.Bytes:
  195. b = append(b, '"')
  196. encodedLen := base64.StdEncoding.EncodedLen(len(o.Value))
  197. dst := make([]byte, encodedLen)
  198. base64.StdEncoding.Encode(dst, o.Value)
  199. b = append(b, dst...)
  200. b = append(b, '"')
  201. case *tengo.Char:
  202. b = strconv.AppendInt(b, int64(o.Value), 10)
  203. case *tengo.Float:
  204. var y []byte
  205. f := o.Value
  206. if math.IsInf(f, 0) || math.IsNaN(f) {
  207. return nil, errors.New("unsupported float value")
  208. }
  209. // Convert as if by ES6 number to string conversion.
  210. // This matches most other JSON generators.
  211. abs := math.Abs(f)
  212. fmt := byte('f')
  213. if abs != 0 {
  214. if abs < 1e-6 || abs >= 1e21 {
  215. fmt = 'e'
  216. }
  217. }
  218. y = strconv.AppendFloat(y, f, fmt, -1, 64)
  219. if fmt == 'e' {
  220. // clean up e-09 to e-9
  221. n := len(y)
  222. if n >= 4 && y[n-4] == 'e' && y[n-3] == '-' && y[n-2] == '0' {
  223. y[n-2] = y[n-1]
  224. y = y[:n-1]
  225. }
  226. }
  227. b = append(b, y...)
  228. case *tengo.Int:
  229. b = strconv.AppendInt(b, o.Value, 10)
  230. case *tengo.String:
  231. // string encoding bug is fixed with newly introduced function
  232. // encodeString(). See: https://github.com/d5/tengo/issues/268
  233. b = encodeString(b, o.Value)
  234. case *tengo.Time:
  235. y, err := o.Value.MarshalJSON()
  236. if err != nil {
  237. return nil, err
  238. }
  239. b = append(b, y...)
  240. case *tengo.Undefined:
  241. b = append(b, "null"...)
  242. default:
  243. // unknown type: ignore
  244. }
  245. return b, nil
  246. }
  247. // encodeString encodes given string as JSON string according to
  248. // https://www.json.org/img/string.png
  249. // Implementation is inspired by https://github.com/json-iterator/go
  250. // See encodeStringSlowPath() for more information.
  251. func encodeString(b []byte, val string) []byte {
  252. valLen := len(val)
  253. buf := bytes.NewBuffer(b)
  254. buf.WriteByte('"')
  255. // write string, the fast path, without utf8 and escape support
  256. i := 0
  257. for ; i < valLen; i++ {
  258. c := val[i]
  259. if c > 31 && c != '"' && c != '\\' {
  260. buf.WriteByte(c)
  261. } else {
  262. break
  263. }
  264. }
  265. if i == valLen {
  266. buf.WriteByte('"')
  267. return buf.Bytes()
  268. }
  269. encodeStringSlowPath(buf, i, val, valLen)
  270. buf.WriteByte('"')
  271. return buf.Bytes()
  272. }
  273. // encodeStringSlowPath is ported from Go 1.14.2 encoding/json package.
  274. // U+2028 U+2029 JSONP security holes can be fixed with addition call to
  275. // json.html_escape() thus it is removed from the implementation below.
  276. // Note: Invalid runes are not checked as they are checked in original
  277. // implementation.
  278. func encodeStringSlowPath(buf *bytes.Buffer, i int, val string, valLen int) {
  279. start := i
  280. for i < valLen {
  281. if b := val[i]; b < utf8.RuneSelf {
  282. if safeSet[b] {
  283. i++
  284. continue
  285. }
  286. if start < i {
  287. buf.WriteString(val[start:i])
  288. }
  289. buf.WriteByte('\\')
  290. switch b {
  291. case '\\', '"':
  292. buf.WriteByte(b)
  293. case '\n':
  294. buf.WriteByte('n')
  295. case '\r':
  296. buf.WriteByte('r')
  297. case '\t':
  298. buf.WriteByte('t')
  299. default:
  300. // This encodes bytes < 0x20 except for \t, \n and \r.
  301. // If escapeHTML is set, it also escapes <, >, and &
  302. // because they can lead to security holes when
  303. // user-controlled strings are rendered into JSON
  304. // and served to some browsers.
  305. buf.WriteString(`u00`)
  306. buf.WriteByte(hex[b>>4])
  307. buf.WriteByte(hex[b&0xF])
  308. }
  309. i++
  310. start = i
  311. continue
  312. }
  313. i++
  314. continue
  315. }
  316. if start < valLen {
  317. buf.WriteString(val[start:])
  318. }
  319. }