builtin_string.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. package goja
  2. import (
  3. "bytes"
  4. "github.com/dop251/goja/parser"
  5. "golang.org/x/text/collate"
  6. "golang.org/x/text/language"
  7. "golang.org/x/text/unicode/norm"
  8. "math"
  9. "strings"
  10. "unicode/utf8"
  11. )
  12. var (
  13. collator = collate.New(language.Und)
  14. )
  15. func (r *Runtime) builtin_String(call FunctionCall) Value {
  16. if len(call.Arguments) > 0 {
  17. arg := call.Arguments[0]
  18. if _, ok := arg.assertString(); ok {
  19. return arg
  20. }
  21. return arg.ToString()
  22. } else {
  23. return newStringValue("")
  24. }
  25. }
  26. func (r *Runtime) _newString(s valueString) *Object {
  27. v := &Object{runtime: r}
  28. o := &stringObject{}
  29. o.class = classString
  30. o.val = v
  31. o.extensible = true
  32. v.self = o
  33. o.prototype = r.global.StringPrototype
  34. if s != nil {
  35. o.value = s
  36. }
  37. o.init()
  38. return v
  39. }
  40. func (r *Runtime) builtin_newString(args []Value) *Object {
  41. var s valueString
  42. if len(args) > 0 {
  43. s = args[0].ToString()
  44. } else {
  45. s = stringEmpty
  46. }
  47. return r._newString(s)
  48. }
  49. func searchSubstringUTF8(str, search string) (ret [][]int) {
  50. searchPos := 0
  51. l := len(str)
  52. if searchPos < l {
  53. p := strings.Index(str[searchPos:], search)
  54. if p != -1 {
  55. p += searchPos
  56. searchPos = p + len(search)
  57. ret = append(ret, []int{p, searchPos})
  58. }
  59. }
  60. return
  61. }
  62. func (r *Runtime) stringproto_toStringValueOf(this Value, funcName string) Value {
  63. if str, ok := this.assertString(); ok {
  64. return str
  65. }
  66. if obj, ok := this.(*Object); ok {
  67. if strObj, ok := obj.self.(*stringObject); ok {
  68. return strObj.value
  69. }
  70. }
  71. r.typeErrorResult(true, "String.prototype.%s is called on incompatible receiver", funcName)
  72. return nil
  73. }
  74. func (r *Runtime) stringproto_toString(call FunctionCall) Value {
  75. return r.stringproto_toStringValueOf(call.This, "toString")
  76. }
  77. func (r *Runtime) stringproto_valueOf(call FunctionCall) Value {
  78. return r.stringproto_toStringValueOf(call.This, "valueOf")
  79. }
  80. func (r *Runtime) string_fromcharcode(call FunctionCall) Value {
  81. b := make([]byte, len(call.Arguments))
  82. for i, arg := range call.Arguments {
  83. chr := toUInt16(arg)
  84. if chr >= utf8.RuneSelf {
  85. bb := make([]uint16, len(call.Arguments))
  86. for j := 0; i < i; j++ {
  87. bb[j] = uint16(b[j])
  88. }
  89. bb[i] = chr
  90. i++
  91. for j, arg := range call.Arguments[i:] {
  92. bb[i+j] = toUInt16(arg)
  93. }
  94. return unicodeString(bb)
  95. }
  96. b[i] = byte(chr)
  97. }
  98. return asciiString(b)
  99. }
  100. func (r *Runtime) stringproto_charAt(call FunctionCall) Value {
  101. r.checkObjectCoercible(call.This)
  102. s := call.This.ToString()
  103. pos := call.Argument(0).ToInteger()
  104. if pos < 0 || pos >= s.length() {
  105. return stringEmpty
  106. }
  107. return newStringValue(string(s.charAt(pos)))
  108. }
  109. func (r *Runtime) stringproto_charCodeAt(call FunctionCall) Value {
  110. r.checkObjectCoercible(call.This)
  111. s := call.This.ToString()
  112. pos := call.Argument(0).ToInteger()
  113. if pos < 0 || pos >= s.length() {
  114. return _NaN
  115. }
  116. return intToValue(int64(s.charAt(pos) & 0xFFFF))
  117. }
  118. func (r *Runtime) stringproto_concat(call FunctionCall) Value {
  119. r.checkObjectCoercible(call.This)
  120. strs := make([]valueString, len(call.Arguments)+1)
  121. strs[0] = call.This.ToString()
  122. _, allAscii := strs[0].(asciiString)
  123. totalLen := strs[0].length()
  124. for i, arg := range call.Arguments {
  125. s := arg.ToString()
  126. if allAscii {
  127. _, allAscii = s.(asciiString)
  128. }
  129. strs[i+1] = s
  130. totalLen += s.length()
  131. }
  132. if allAscii {
  133. buf := bytes.NewBuffer(make([]byte, 0, totalLen))
  134. for _, s := range strs {
  135. buf.WriteString(s.String())
  136. }
  137. return asciiString(buf.String())
  138. } else {
  139. buf := make([]uint16, totalLen)
  140. pos := int64(0)
  141. for _, s := range strs {
  142. switch s := s.(type) {
  143. case asciiString:
  144. for i := 0; i < len(s); i++ {
  145. buf[pos] = uint16(s[i])
  146. pos++
  147. }
  148. case unicodeString:
  149. copy(buf[pos:], s)
  150. pos += s.length()
  151. }
  152. }
  153. return unicodeString(buf)
  154. }
  155. }
  156. func (r *Runtime) stringproto_indexOf(call FunctionCall) Value {
  157. r.checkObjectCoercible(call.This)
  158. value := call.This.ToString()
  159. target := call.Argument(0).ToString()
  160. pos := call.Argument(1).ToInteger()
  161. if pos < 0 {
  162. pos = 0
  163. } else {
  164. l := value.length()
  165. if pos > l {
  166. pos = l
  167. }
  168. }
  169. return intToValue(value.index(target, pos))
  170. }
  171. func (r *Runtime) stringproto_lastIndexOf(call FunctionCall) Value {
  172. r.checkObjectCoercible(call.This)
  173. value := call.This.ToString()
  174. target := call.Argument(0).ToString()
  175. numPos := call.Argument(1).ToNumber()
  176. var pos int64
  177. if f, ok := numPos.assertFloat(); ok && math.IsNaN(f) {
  178. pos = value.length()
  179. } else {
  180. pos = numPos.ToInteger()
  181. if pos < 0 {
  182. pos = 0
  183. } else {
  184. l := value.length()
  185. if pos > l {
  186. pos = l
  187. }
  188. }
  189. }
  190. return intToValue(value.lastIndex(target, pos))
  191. }
  192. func (r *Runtime) stringproto_localeCompare(call FunctionCall) Value {
  193. r.checkObjectCoercible(call.This)
  194. this := norm.NFD.String(call.This.String())
  195. that := norm.NFD.String(call.Argument(0).String())
  196. return intToValue(int64(collator.CompareString(this, that)))
  197. }
  198. func (r *Runtime) stringproto_match(call FunctionCall) Value {
  199. r.checkObjectCoercible(call.This)
  200. s := call.This.ToString()
  201. regexp := call.Argument(0)
  202. var rx *regexpObject
  203. if regexp, ok := regexp.(*Object); ok {
  204. rx, _ = regexp.self.(*regexpObject)
  205. }
  206. if rx == nil {
  207. rx = r.builtin_newRegExp([]Value{regexp}).self.(*regexpObject)
  208. }
  209. if rx.global {
  210. rx.putStr("lastIndex", intToValue(0), false)
  211. var a []Value
  212. var previousLastIndex int64
  213. for {
  214. match, result := rx.execRegexp(s)
  215. if !match {
  216. break
  217. }
  218. thisIndex := rx.getStr("lastIndex").ToInteger()
  219. if thisIndex == previousLastIndex {
  220. previousLastIndex++
  221. rx.putStr("lastIndex", intToValue(previousLastIndex), false)
  222. } else {
  223. previousLastIndex = thisIndex
  224. }
  225. a = append(a, s.substring(int64(result[0]), int64(result[1])))
  226. }
  227. if len(a) == 0 {
  228. return _null
  229. }
  230. return r.newArrayValues(a)
  231. } else {
  232. return rx.exec(s)
  233. }
  234. }
  235. func (r *Runtime) stringproto_replace(call FunctionCall) Value {
  236. s := call.This.ToString()
  237. var str string
  238. var isASCII bool
  239. if astr, ok := s.(asciiString); ok {
  240. str = string(astr)
  241. isASCII = true
  242. } else {
  243. str = s.String()
  244. }
  245. searchValue := call.Argument(0)
  246. replaceValue := call.Argument(1)
  247. var found [][]int
  248. if searchValue, ok := searchValue.(*Object); ok {
  249. if regexp, ok := searchValue.self.(*regexpObject); ok {
  250. find := 1
  251. if regexp.global {
  252. find = -1
  253. }
  254. if isASCII {
  255. found = regexp.pattern.FindAllSubmatchIndexASCII(str, find)
  256. } else {
  257. found = regexp.pattern.FindAllSubmatchIndexUTF8(str, find)
  258. }
  259. if found == nil {
  260. return s
  261. }
  262. }
  263. }
  264. if found == nil {
  265. found = searchSubstringUTF8(str, searchValue.String())
  266. }
  267. if len(found) == 0 {
  268. return s
  269. }
  270. var buf bytes.Buffer
  271. lastIndex := 0
  272. var rcall func(FunctionCall) Value
  273. if replaceValue, ok := replaceValue.(*Object); ok {
  274. if c, ok := replaceValue.self.assertCallable(); ok {
  275. rcall = c
  276. }
  277. }
  278. if rcall != nil {
  279. for _, item := range found {
  280. if item[0] != lastIndex {
  281. buf.WriteString(str[lastIndex:item[0]])
  282. }
  283. matchCount := len(item) / 2
  284. argumentList := make([]Value, matchCount+2)
  285. for index := 0; index < matchCount; index++ {
  286. offset := 2 * index
  287. if item[offset] != -1 {
  288. if isASCII {
  289. argumentList[index] = asciiString(str[item[offset]:item[offset+1]])
  290. } else {
  291. argumentList[index] = newStringValue(str[item[offset]:item[offset+1]])
  292. }
  293. } else {
  294. argumentList[index] = _undefined
  295. }
  296. }
  297. argumentList[matchCount] = valueInt(item[0])
  298. argumentList[matchCount+1] = s
  299. replacement := rcall(FunctionCall{
  300. This: _undefined,
  301. Arguments: argumentList,
  302. }).String()
  303. buf.WriteString(replacement)
  304. lastIndex = item[1]
  305. }
  306. } else {
  307. newstring := replaceValue.String()
  308. for _, item := range found {
  309. if item[0] != lastIndex {
  310. buf.WriteString(str[lastIndex:item[0]])
  311. }
  312. matches := len(item) / 2
  313. for i := 0; i < len(newstring); i++ {
  314. if newstring[i] == '$' && i < len(newstring)-1 {
  315. ch := newstring[i+1]
  316. switch ch {
  317. case '$':
  318. buf.WriteByte('$')
  319. case '`':
  320. buf.WriteString(str[0:item[0]])
  321. case '\'':
  322. buf.WriteString(str[item[1]:])
  323. case '&':
  324. buf.WriteString(str[item[0]:item[1]])
  325. default:
  326. matchNumber := 0
  327. l := 0
  328. for _, ch := range newstring[i+1:] {
  329. if ch >= '0' && ch <= '9' {
  330. m := matchNumber*10 + int(ch-'0')
  331. if m >= matches {
  332. break
  333. }
  334. matchNumber = m
  335. l++
  336. } else {
  337. break
  338. }
  339. }
  340. if l > 0 {
  341. offset := 2 * matchNumber
  342. if offset < len(item) && item[offset] != -1 {
  343. buf.WriteString(str[item[offset]:item[offset+1]])
  344. }
  345. i += l - 1
  346. } else {
  347. buf.WriteByte('$')
  348. buf.WriteByte(ch)
  349. }
  350. }
  351. i++
  352. } else {
  353. buf.WriteByte(newstring[i])
  354. }
  355. }
  356. lastIndex = item[1]
  357. }
  358. }
  359. if lastIndex != len(str) {
  360. buf.WriteString(str[lastIndex:])
  361. }
  362. return newStringValue(buf.String())
  363. }
  364. func (r *Runtime) stringproto_search(call FunctionCall) Value {
  365. r.checkObjectCoercible(call.This)
  366. s := call.This.ToString()
  367. regexp := call.Argument(0)
  368. var rx *regexpObject
  369. if regexp, ok := regexp.(*Object); ok {
  370. rx, _ = regexp.self.(*regexpObject)
  371. }
  372. if rx == nil {
  373. rx = r.builtin_newRegExp([]Value{regexp}).self.(*regexpObject)
  374. }
  375. match, result := rx.execRegexp(s)
  376. if !match {
  377. return intToValue(-1)
  378. }
  379. return intToValue(int64(result[0]))
  380. }
  381. func (r *Runtime) stringproto_slice(call FunctionCall) Value {
  382. r.checkObjectCoercible(call.This)
  383. s := call.This.ToString()
  384. l := s.length()
  385. start := call.Argument(0).ToInteger()
  386. var end int64
  387. if arg1 := call.Argument(1); arg1 != _undefined {
  388. end = arg1.ToInteger()
  389. } else {
  390. end = l
  391. }
  392. if start < 0 {
  393. start += l
  394. if start < 0 {
  395. start = 0
  396. }
  397. } else {
  398. if start > l {
  399. start = l
  400. }
  401. }
  402. if end < 0 {
  403. end += l
  404. if end < 0 {
  405. end = 0
  406. }
  407. } else {
  408. if end > l {
  409. end = l
  410. }
  411. }
  412. if end > start {
  413. return s.substring(start, end)
  414. }
  415. return stringEmpty
  416. }
  417. func (r *Runtime) stringproto_split(call FunctionCall) Value {
  418. r.checkObjectCoercible(call.This)
  419. s := call.This.ToString()
  420. separatorValue := call.Argument(0)
  421. limitValue := call.Argument(1)
  422. limit := -1
  423. if limitValue != _undefined {
  424. limit = int(toUInt32(limitValue))
  425. }
  426. if limit == 0 {
  427. return r.newArrayValues(nil)
  428. }
  429. if separatorValue == _undefined {
  430. return r.newArrayValues([]Value{s})
  431. }
  432. var search *regexpObject
  433. if o, ok := separatorValue.(*Object); ok {
  434. search, _ = o.self.(*regexpObject)
  435. }
  436. if search != nil {
  437. targetLength := s.length()
  438. valueArray := []Value{}
  439. result := search.pattern.FindAllSubmatchIndex(s, -1)
  440. lastIndex := 0
  441. found := 0
  442. for _, match := range result {
  443. if match[0] == match[1] {
  444. // FIXME Ugh, this is a hack
  445. if match[0] == 0 || int64(match[0]) == targetLength {
  446. continue
  447. }
  448. }
  449. if lastIndex != match[0] {
  450. valueArray = append(valueArray, s.substring(int64(lastIndex), int64(match[0])))
  451. found++
  452. } else if lastIndex == match[0] {
  453. if lastIndex != -1 {
  454. valueArray = append(valueArray, stringEmpty)
  455. found++
  456. }
  457. }
  458. lastIndex = match[1]
  459. if found == limit {
  460. goto RETURN
  461. }
  462. captureCount := len(match) / 2
  463. for index := 1; index < captureCount; index++ {
  464. offset := index * 2
  465. var value Value
  466. if match[offset] != -1 {
  467. value = s.substring(int64(match[offset]), int64(match[offset+1]))
  468. } else {
  469. value = _undefined
  470. }
  471. valueArray = append(valueArray, value)
  472. found++
  473. if found == limit {
  474. goto RETURN
  475. }
  476. }
  477. }
  478. if found != limit {
  479. if int64(lastIndex) != targetLength {
  480. valueArray = append(valueArray, s.substring(int64(lastIndex), targetLength))
  481. } else {
  482. valueArray = append(valueArray, stringEmpty)
  483. }
  484. }
  485. RETURN:
  486. return r.newArrayValues(valueArray)
  487. } else {
  488. separator := separatorValue.String()
  489. excess := false
  490. str := s.String()
  491. if limit > len(str) {
  492. limit = len(str)
  493. }
  494. splitLimit := limit
  495. if limit > 0 {
  496. splitLimit = limit + 1
  497. excess = true
  498. }
  499. split := strings.SplitN(str, separator, splitLimit)
  500. if excess && len(split) > limit {
  501. split = split[:limit]
  502. }
  503. valueArray := make([]Value, len(split))
  504. for index, value := range split {
  505. valueArray[index] = newStringValue(value)
  506. }
  507. return r.newArrayValues(valueArray)
  508. }
  509. }
  510. func (r *Runtime) stringproto_substring(call FunctionCall) Value {
  511. r.checkObjectCoercible(call.This)
  512. s := call.This.ToString()
  513. l := s.length()
  514. intStart := call.Argument(0).ToInteger()
  515. var intEnd int64
  516. if end := call.Argument(1); end != _undefined {
  517. intEnd = end.ToInteger()
  518. } else {
  519. intEnd = l
  520. }
  521. if intStart < 0 {
  522. intStart = 0
  523. } else if intStart > l {
  524. intStart = l
  525. }
  526. if intEnd < 0 {
  527. intEnd = 0
  528. } else if intEnd > l {
  529. intEnd = l
  530. }
  531. if intStart > intEnd {
  532. intStart, intEnd = intEnd, intStart
  533. }
  534. return s.substring(intStart, intEnd)
  535. }
  536. func (r *Runtime) stringproto_toLowerCase(call FunctionCall) Value {
  537. r.checkObjectCoercible(call.This)
  538. s := call.This.ToString()
  539. return s.toLower()
  540. }
  541. func (r *Runtime) stringproto_toUpperCase(call FunctionCall) Value {
  542. r.checkObjectCoercible(call.This)
  543. s := call.This.ToString()
  544. return s.toUpper()
  545. }
  546. func (r *Runtime) stringproto_trim(call FunctionCall) Value {
  547. r.checkObjectCoercible(call.This)
  548. s := call.This.ToString()
  549. return newStringValue(strings.Trim(s.String(), parser.WhitespaceChars))
  550. }
  551. func (r *Runtime) stringproto_substr(call FunctionCall) Value {
  552. s := call.This.ToString()
  553. start := call.Argument(0).ToInteger()
  554. var length int64
  555. sl := int64(s.length())
  556. if arg := call.Argument(1); arg != _undefined {
  557. length = arg.ToInteger()
  558. } else {
  559. length = sl
  560. }
  561. if start < 0 {
  562. start = max(sl+start, 0)
  563. }
  564. length = min(max(length, 0), sl-start)
  565. if length <= 0 {
  566. return stringEmpty
  567. }
  568. return s.substring(start, start+length)
  569. }
  570. func (r *Runtime) initString() {
  571. r.global.StringPrototype = r.builtin_newString([]Value{stringEmpty})
  572. o := r.global.StringPrototype.self
  573. o.(*stringObject).prototype = r.global.ObjectPrototype
  574. o._putProp("toString", r.newNativeFunc(r.stringproto_toString, nil, "toString", nil, 0), true, false, true)
  575. o._putProp("valueOf", r.newNativeFunc(r.stringproto_valueOf, nil, "valueOf", nil, 0), true, false, true)
  576. o._putProp("charAt", r.newNativeFunc(r.stringproto_charAt, nil, "charAt", nil, 1), true, false, true)
  577. o._putProp("charCodeAt", r.newNativeFunc(r.stringproto_charCodeAt, nil, "charCodeAt", nil, 1), true, false, true)
  578. o._putProp("concat", r.newNativeFunc(r.stringproto_concat, nil, "concat", nil, 1), true, false, true)
  579. o._putProp("indexOf", r.newNativeFunc(r.stringproto_indexOf, nil, "indexOf", nil, 1), true, false, true)
  580. o._putProp("lastIndexOf", r.newNativeFunc(r.stringproto_lastIndexOf, nil, "lastIndexOf", nil, 1), true, false, true)
  581. o._putProp("localeCompare", r.newNativeFunc(r.stringproto_localeCompare, nil, "localeCompare", nil, 1), true, false, true)
  582. o._putProp("match", r.newNativeFunc(r.stringproto_match, nil, "match", nil, 1), true, false, true)
  583. o._putProp("replace", r.newNativeFunc(r.stringproto_replace, nil, "replace", nil, 2), true, false, true)
  584. o._putProp("search", r.newNativeFunc(r.stringproto_search, nil, "search", nil, 1), true, false, true)
  585. o._putProp("slice", r.newNativeFunc(r.stringproto_slice, nil, "slice", nil, 2), true, false, true)
  586. o._putProp("split", r.newNativeFunc(r.stringproto_split, nil, "split", nil, 2), true, false, true)
  587. o._putProp("substring", r.newNativeFunc(r.stringproto_substring, nil, "substring", nil, 2), true, false, true)
  588. o._putProp("toLowerCase", r.newNativeFunc(r.stringproto_toLowerCase, nil, "toLowerCase", nil, 0), true, false, true)
  589. o._putProp("toLocaleLowerCase", r.newNativeFunc(r.stringproto_toLowerCase, nil, "toLocaleLowerCase", nil, 0), true, false, true)
  590. o._putProp("toUpperCase", r.newNativeFunc(r.stringproto_toUpperCase, nil, "toUpperCase", nil, 0), true, false, true)
  591. o._putProp("toLocaleUpperCase", r.newNativeFunc(r.stringproto_toUpperCase, nil, "toLocaleUpperCase", nil, 0), true, false, true)
  592. o._putProp("trim", r.newNativeFunc(r.stringproto_trim, nil, "trim", nil, 0), true, false, true)
  593. // Annex B
  594. o._putProp("substr", r.newNativeFunc(r.stringproto_substr, nil, "substr", nil, 2), true, false, true)
  595. r.global.String = r.newNativeFunc(r.builtin_String, r.builtin_newString, "String", r.global.StringPrototype, 1)
  596. o = r.global.String.self
  597. o._putProp("fromCharCode", r.newNativeFunc(r.string_fromcharcode, nil, "fromCharCode", nil, 1), true, false, true)
  598. r.addToGlobal("String", r.global.String)
  599. r.stringSingleton = r.builtin_new(r.global.String, nil).self.(*stringObject)
  600. }