builtin_string.go 16 KB

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