builtin_string.go 16 KB

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