builtin.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. package otto
  2. import (
  3. "encoding/hex"
  4. "math"
  5. "net/url"
  6. "regexp"
  7. "strconv"
  8. "strings"
  9. "unicode/utf16"
  10. "unicode/utf8"
  11. )
  12. // Global
  13. func builtinGlobal_eval(call FunctionCall) Value {
  14. src := call.Argument(0)
  15. if !src.IsString() {
  16. return src
  17. }
  18. runtime := call.runtime
  19. program := runtime.cmpl_parseOrThrow(src.string(), nil)
  20. if !call.eval {
  21. // Not a direct call to eval, so we enter the global ExecutionContext
  22. runtime.enterGlobalScope()
  23. defer runtime.leaveScope()
  24. }
  25. returnValue := runtime.cmpl_evaluate_nodeProgram(program, true)
  26. if returnValue.isEmpty() {
  27. return Value{}
  28. }
  29. return returnValue
  30. }
  31. func builtinGlobal_isNaN(call FunctionCall) Value {
  32. value := call.Argument(0).float64()
  33. return toValue_bool(math.IsNaN(value))
  34. }
  35. func builtinGlobal_isFinite(call FunctionCall) Value {
  36. value := call.Argument(0).float64()
  37. return toValue_bool(!math.IsNaN(value) && !math.IsInf(value, 0))
  38. }
  39. func digitValue(chr rune) int {
  40. switch {
  41. case '0' <= chr && chr <= '9':
  42. return int(chr - '0')
  43. case 'a' <= chr && chr <= 'z':
  44. return int(chr - 'a' + 10)
  45. case 'A' <= chr && chr <= 'Z':
  46. return int(chr - 'A' + 10)
  47. }
  48. return 36 // Larger than any legal digit value
  49. }
  50. func builtinGlobal_parseInt(call FunctionCall) Value {
  51. input := strings.Trim(call.Argument(0).string(), builtinString_trim_whitespace)
  52. if len(input) == 0 {
  53. return NaNValue()
  54. }
  55. radix := int(toInt32(call.Argument(1)))
  56. negative := false
  57. switch input[0] {
  58. case '+':
  59. input = input[1:]
  60. case '-':
  61. negative = true
  62. input = input[1:]
  63. }
  64. strip := true
  65. if radix == 0 {
  66. radix = 10
  67. } else {
  68. if radix < 2 || radix > 36 {
  69. return NaNValue()
  70. } else if radix != 16 {
  71. strip = false
  72. }
  73. }
  74. switch len(input) {
  75. case 0:
  76. return NaNValue()
  77. case 1:
  78. default:
  79. if strip {
  80. if input[0] == '0' && (input[1] == 'x' || input[1] == 'X') {
  81. input = input[2:]
  82. radix = 16
  83. }
  84. }
  85. }
  86. base := radix
  87. index := 0
  88. for ; index < len(input); index++ {
  89. digit := digitValue(rune(input[index])) // If not ASCII, then an error anyway
  90. if digit >= base {
  91. break
  92. }
  93. }
  94. input = input[0:index]
  95. value, err := strconv.ParseInt(input, radix, 64)
  96. if err != nil {
  97. if err.(*strconv.NumError).Err == strconv.ErrRange {
  98. base := float64(base)
  99. // Could just be a very large number (e.g. 0x8000000000000000)
  100. var value float64
  101. for _, chr := range input {
  102. digit := float64(digitValue(chr))
  103. if digit >= base {
  104. return NaNValue()
  105. }
  106. value = value*base + digit
  107. }
  108. if negative {
  109. value *= -1
  110. }
  111. return toValue_float64(value)
  112. }
  113. return NaNValue()
  114. }
  115. if negative {
  116. value *= -1
  117. }
  118. return toValue_int64(value)
  119. }
  120. var parseFloat_matchBadSpecial = regexp.MustCompile(`[\+\-]?(?:[Ii]nf$|infinity)`)
  121. var parseFloat_matchValid = regexp.MustCompile(`[0-9eE\+\-\.]|Infinity`)
  122. func builtinGlobal_parseFloat(call FunctionCall) Value {
  123. // Caveat emptor: This implementation does NOT match the specification
  124. input := strings.Trim(call.Argument(0).string(), builtinString_trim_whitespace)
  125. if parseFloat_matchBadSpecial.MatchString(input) {
  126. return NaNValue()
  127. }
  128. value, err := strconv.ParseFloat(input, 64)
  129. if err != nil {
  130. for end := len(input); end > 0; end-- {
  131. input := input[0:end]
  132. if !parseFloat_matchValid.MatchString(input) {
  133. return NaNValue()
  134. }
  135. value, err = strconv.ParseFloat(input, 64)
  136. if err == nil {
  137. break
  138. }
  139. }
  140. if err != nil {
  141. return NaNValue()
  142. }
  143. }
  144. return toValue_float64(value)
  145. }
  146. // encodeURI/decodeURI
  147. func _builtinGlobal_encodeURI(call FunctionCall, escape *regexp.Regexp) Value {
  148. value := call.Argument(0)
  149. var input []uint16
  150. switch vl := value.value.(type) {
  151. case []uint16:
  152. input = vl
  153. default:
  154. input = utf16.Encode([]rune(value.string()))
  155. }
  156. if len(input) == 0 {
  157. return toValue_string("")
  158. }
  159. output := []byte{}
  160. length := len(input)
  161. encode := make([]byte, 4)
  162. for index := 0; index < length; {
  163. value := input[index]
  164. decode := utf16.Decode(input[index : index+1])
  165. if value >= 0xDC00 && value <= 0xDFFF {
  166. panic(call.runtime.panicURIError("URI malformed"))
  167. }
  168. if value >= 0xD800 && value <= 0xDBFF {
  169. index += 1
  170. if index >= length {
  171. panic(call.runtime.panicURIError("URI malformed"))
  172. }
  173. // input = ..., value, value1, ...
  174. value1 := input[index]
  175. if value1 < 0xDC00 || value1 > 0xDFFF {
  176. panic(call.runtime.panicURIError("URI malformed"))
  177. }
  178. decode = []rune{((rune(value) - 0xD800) * 0x400) + (rune(value1) - 0xDC00) + 0x10000}
  179. }
  180. index += 1
  181. size := utf8.EncodeRune(encode, decode[0])
  182. encode := encode[0:size]
  183. output = append(output, encode...)
  184. }
  185. {
  186. value := escape.ReplaceAllFunc(output, func(target []byte) []byte {
  187. // Probably a better way of doing this
  188. if target[0] == ' ' {
  189. return []byte("%20")
  190. }
  191. return []byte(url.QueryEscape(string(target)))
  192. })
  193. return toValue_string(string(value))
  194. }
  195. }
  196. var encodeURI_Regexp = regexp.MustCompile(`([^~!@#$&*()=:/,;?+'])`)
  197. func builtinGlobal_encodeURI(call FunctionCall) Value {
  198. return _builtinGlobal_encodeURI(call, encodeURI_Regexp)
  199. }
  200. var encodeURIComponent_Regexp = regexp.MustCompile(`([^~!*()'])`)
  201. func builtinGlobal_encodeURIComponent(call FunctionCall) Value {
  202. return _builtinGlobal_encodeURI(call, encodeURIComponent_Regexp)
  203. }
  204. // 3B/2F/3F/3A/40/26/3D/2B/24/2C/23
  205. var decodeURI_guard = regexp.MustCompile(`(?i)(?:%)(3B|2F|3F|3A|40|26|3D|2B|24|2C|23)`)
  206. func _decodeURI(input string, reserve bool) (string, bool) {
  207. if reserve {
  208. input = decodeURI_guard.ReplaceAllString(input, "%25$1")
  209. }
  210. input = strings.Replace(input, "+", "%2B", -1) // Ugly hack to make QueryUnescape work with our use case
  211. output, err := url.QueryUnescape(input)
  212. if err != nil || !utf8.ValidString(output) {
  213. return "", true
  214. }
  215. return output, false
  216. }
  217. func builtinGlobal_decodeURI(call FunctionCall) Value {
  218. output, err := _decodeURI(call.Argument(0).string(), true)
  219. if err {
  220. panic(call.runtime.panicURIError("URI malformed"))
  221. }
  222. return toValue_string(output)
  223. }
  224. func builtinGlobal_decodeURIComponent(call FunctionCall) Value {
  225. output, err := _decodeURI(call.Argument(0).string(), false)
  226. if err {
  227. panic(call.runtime.panicURIError("URI malformed"))
  228. }
  229. return toValue_string(output)
  230. }
  231. // escape/unescape
  232. func builtin_shouldEscape(chr byte) bool {
  233. if 'A' <= chr && chr <= 'Z' || 'a' <= chr && chr <= 'z' || '0' <= chr && chr <= '9' {
  234. return false
  235. }
  236. return !strings.ContainsRune("*_+-./", rune(chr))
  237. }
  238. const escapeBase16 = "0123456789ABCDEF"
  239. func builtin_escape(input string) string {
  240. output := make([]byte, 0, len(input))
  241. length := len(input)
  242. for index := 0; index < length; {
  243. if builtin_shouldEscape(input[index]) {
  244. chr, width := utf8.DecodeRuneInString(input[index:])
  245. chr16 := utf16.Encode([]rune{chr})[0]
  246. if 256 > chr16 {
  247. output = append(output, '%',
  248. escapeBase16[chr16>>4],
  249. escapeBase16[chr16&15],
  250. )
  251. } else {
  252. output = append(output, '%', 'u',
  253. escapeBase16[chr16>>12],
  254. escapeBase16[(chr16>>8)&15],
  255. escapeBase16[(chr16>>4)&15],
  256. escapeBase16[chr16&15],
  257. )
  258. }
  259. index += width
  260. } else {
  261. output = append(output, input[index])
  262. index += 1
  263. }
  264. }
  265. return string(output)
  266. }
  267. func builtin_unescape(input string) string {
  268. output := make([]rune, 0, len(input))
  269. length := len(input)
  270. for index := 0; index < length; {
  271. if input[index] == '%' {
  272. if index <= length-6 && input[index+1] == 'u' {
  273. byte16, err := hex.DecodeString(input[index+2 : index+6])
  274. if err == nil {
  275. value := uint16(byte16[0])<<8 + uint16(byte16[1])
  276. chr := utf16.Decode([]uint16{value})[0]
  277. output = append(output, chr)
  278. index += 6
  279. continue
  280. }
  281. }
  282. if index <= length-3 {
  283. byte8, err := hex.DecodeString(input[index+1 : index+3])
  284. if err == nil {
  285. value := uint16(byte8[0])
  286. chr := utf16.Decode([]uint16{value})[0]
  287. output = append(output, chr)
  288. index += 3
  289. continue
  290. }
  291. }
  292. }
  293. output = append(output, rune(input[index]))
  294. index += 1
  295. }
  296. return string(output)
  297. }
  298. func builtinGlobal_escape(call FunctionCall) Value {
  299. return toValue_string(builtin_escape(call.Argument(0).string()))
  300. }
  301. func builtinGlobal_unescape(call FunctionCall) Value {
  302. return toValue_string(builtin_unescape(call.Argument(0).string()))
  303. }