builtin_string.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116
  1. package goja
  2. import (
  3. "github.com/dop251/goja/unistring"
  4. "math"
  5. "strings"
  6. "sync"
  7. "unicode/utf16"
  8. "unicode/utf8"
  9. "github.com/dop251/goja/parser"
  10. "golang.org/x/text/collate"
  11. "golang.org/x/text/language"
  12. "golang.org/x/text/unicode/norm"
  13. )
  14. func (r *Runtime) collator() *collate.Collator {
  15. collator := r._collator
  16. if collator == nil {
  17. collator = collate.New(language.Und)
  18. r._collator = collator
  19. }
  20. return collator
  21. }
  22. func toString(arg Value) String {
  23. if s, ok := arg.(String); ok {
  24. return s
  25. }
  26. if s, ok := arg.(*Symbol); ok {
  27. return s.descriptiveString()
  28. }
  29. return arg.toString()
  30. }
  31. func (r *Runtime) builtin_String(call FunctionCall) Value {
  32. if len(call.Arguments) > 0 {
  33. return toString(call.Arguments[0])
  34. } else {
  35. return stringEmpty
  36. }
  37. }
  38. func (r *Runtime) _newString(s String, proto *Object) *Object {
  39. v := &Object{runtime: r}
  40. o := &stringObject{}
  41. o.class = classString
  42. o.val = v
  43. o.extensible = true
  44. v.self = o
  45. o.prototype = proto
  46. if s != nil {
  47. o.value = s
  48. }
  49. o.init()
  50. return v
  51. }
  52. func (r *Runtime) builtin_newString(args []Value, proto *Object) *Object {
  53. var s String
  54. if len(args) > 0 {
  55. s = args[0].toString()
  56. } else {
  57. s = stringEmpty
  58. }
  59. return r._newString(s, proto)
  60. }
  61. func (r *Runtime) stringproto_toStringValueOf(this Value, funcName string) Value {
  62. if str, ok := this.(String); ok {
  63. return str
  64. }
  65. if obj, ok := this.(*Object); ok {
  66. if strObj, ok := obj.self.(*stringObject); ok {
  67. return strObj.value
  68. }
  69. if reflectObj, ok := obj.self.(*objectGoReflect); ok && reflectObj.class == classString {
  70. if toString := reflectObj.toString; toString != nil {
  71. return toString()
  72. }
  73. if valueOf := reflectObj.valueOf; valueOf != nil {
  74. return valueOf()
  75. }
  76. }
  77. if obj == r.global.StringPrototype {
  78. return stringEmpty
  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) stringproto_iterator(call FunctionCall) Value {
  91. r.checkObjectCoercible(call.This)
  92. return r.createStringIterator(call.This.toString())
  93. }
  94. func (r *Runtime) string_fromcharcode(call FunctionCall) Value {
  95. b := make([]byte, len(call.Arguments))
  96. for i, arg := range call.Arguments {
  97. chr := toUint16(arg)
  98. if chr >= utf8.RuneSelf {
  99. bb := make([]uint16, len(call.Arguments)+1)
  100. bb[0] = unistring.BOM
  101. bb1 := bb[1:]
  102. for j := 0; j < i; j++ {
  103. bb1[j] = uint16(b[j])
  104. }
  105. bb1[i] = chr
  106. i++
  107. for j, arg := range call.Arguments[i:] {
  108. bb1[i+j] = toUint16(arg)
  109. }
  110. return unicodeString(bb)
  111. }
  112. b[i] = byte(chr)
  113. }
  114. return asciiString(b)
  115. }
  116. func (r *Runtime) string_fromcodepoint(call FunctionCall) Value {
  117. var sb StringBuilder
  118. for _, arg := range call.Arguments {
  119. num := arg.ToNumber()
  120. var c rune
  121. if numInt, ok := num.(valueInt); ok {
  122. if numInt < 0 || numInt > utf8.MaxRune {
  123. panic(r.newError(r.getRangeError(), "Invalid code point %d", numInt))
  124. }
  125. c = rune(numInt)
  126. } else {
  127. panic(r.newError(r.getRangeError(), "Invalid code point %s", num))
  128. }
  129. sb.WriteRune(c)
  130. }
  131. return sb.String()
  132. }
  133. func (r *Runtime) string_raw(call FunctionCall) Value {
  134. cooked := call.Argument(0).ToObject(r)
  135. raw := nilSafe(cooked.self.getStr("raw", nil)).ToObject(r)
  136. literalSegments := toLength(raw.self.getStr("length", nil))
  137. if literalSegments <= 0 {
  138. return stringEmpty
  139. }
  140. var stringElements StringBuilder
  141. nextIndex := int64(0)
  142. numberOfSubstitutions := int64(len(call.Arguments) - 1)
  143. for {
  144. nextSeg := nilSafe(raw.self.getIdx(valueInt(nextIndex), nil)).toString()
  145. stringElements.WriteString(nextSeg)
  146. if nextIndex+1 == literalSegments {
  147. return stringElements.String()
  148. }
  149. if nextIndex < numberOfSubstitutions {
  150. stringElements.WriteString(nilSafe(call.Arguments[nextIndex+1]).toString())
  151. }
  152. nextIndex++
  153. }
  154. }
  155. func (r *Runtime) stringproto_at(call FunctionCall) Value {
  156. r.checkObjectCoercible(call.This)
  157. s := call.This.toString()
  158. pos := call.Argument(0).ToInteger()
  159. length := int64(s.Length())
  160. if pos < 0 {
  161. pos = length + pos
  162. }
  163. if pos >= length || pos < 0 {
  164. return _undefined
  165. }
  166. return s.Substring(int(pos), int(pos+1))
  167. }
  168. func (r *Runtime) stringproto_charAt(call FunctionCall) Value {
  169. r.checkObjectCoercible(call.This)
  170. s := call.This.toString()
  171. pos := call.Argument(0).ToInteger()
  172. if pos < 0 || pos >= int64(s.Length()) {
  173. return stringEmpty
  174. }
  175. return s.Substring(int(pos), int(pos+1))
  176. }
  177. func (r *Runtime) stringproto_charCodeAt(call FunctionCall) Value {
  178. r.checkObjectCoercible(call.This)
  179. s := call.This.toString()
  180. pos := call.Argument(0).ToInteger()
  181. if pos < 0 || pos >= int64(s.Length()) {
  182. return _NaN
  183. }
  184. return intToValue(int64(s.CharAt(toIntStrict(pos)) & 0xFFFF))
  185. }
  186. func (r *Runtime) stringproto_codePointAt(call FunctionCall) Value {
  187. r.checkObjectCoercible(call.This)
  188. s := call.This.toString()
  189. p := call.Argument(0).ToInteger()
  190. size := s.Length()
  191. if p < 0 || p >= int64(size) {
  192. return _undefined
  193. }
  194. pos := toIntStrict(p)
  195. first := s.CharAt(pos)
  196. if isUTF16FirstSurrogate(first) {
  197. pos++
  198. if pos < size {
  199. second := s.CharAt(pos)
  200. if isUTF16SecondSurrogate(second) {
  201. return intToValue(int64(utf16.DecodeRune(rune(first), rune(second))))
  202. }
  203. }
  204. }
  205. return intToValue(int64(first & 0xFFFF))
  206. }
  207. func (r *Runtime) stringproto_concat(call FunctionCall) Value {
  208. r.checkObjectCoercible(call.This)
  209. strs := make([]String, len(call.Arguments)+1)
  210. a, u := devirtualizeString(call.This.toString())
  211. allAscii := true
  212. totalLen := 0
  213. if u == nil {
  214. strs[0] = a
  215. totalLen = len(a)
  216. } else {
  217. strs[0] = u
  218. totalLen = u.Length()
  219. allAscii = false
  220. }
  221. for i, arg := range call.Arguments {
  222. a, u := devirtualizeString(arg.toString())
  223. if u != nil {
  224. allAscii = false
  225. totalLen += u.Length()
  226. strs[i+1] = u
  227. } else {
  228. totalLen += a.Length()
  229. strs[i+1] = a
  230. }
  231. }
  232. if allAscii {
  233. var buf strings.Builder
  234. buf.Grow(totalLen)
  235. for _, s := range strs {
  236. buf.WriteString(s.String())
  237. }
  238. return asciiString(buf.String())
  239. } else {
  240. buf := make([]uint16, totalLen+1)
  241. buf[0] = unistring.BOM
  242. pos := 1
  243. for _, s := range strs {
  244. switch s := s.(type) {
  245. case asciiString:
  246. for i := 0; i < len(s); i++ {
  247. buf[pos] = uint16(s[i])
  248. pos++
  249. }
  250. case unicodeString:
  251. copy(buf[pos:], s[1:])
  252. pos += s.Length()
  253. }
  254. }
  255. return unicodeString(buf)
  256. }
  257. }
  258. func (r *Runtime) stringproto_endsWith(call FunctionCall) Value {
  259. r.checkObjectCoercible(call.This)
  260. s := call.This.toString()
  261. searchString := call.Argument(0)
  262. if isRegexp(searchString) {
  263. panic(r.NewTypeError("First argument to String.prototype.endsWith must not be a regular expression"))
  264. }
  265. searchStr := searchString.toString()
  266. l := int64(s.Length())
  267. var pos int64
  268. if posArg := call.Argument(1); posArg != _undefined {
  269. pos = posArg.ToInteger()
  270. } else {
  271. pos = l
  272. }
  273. end := toIntStrict(min(max(pos, 0), l))
  274. searchLength := searchStr.Length()
  275. start := end - searchLength
  276. if start < 0 {
  277. return valueFalse
  278. }
  279. for i := 0; i < searchLength; i++ {
  280. if s.CharAt(start+i) != searchStr.CharAt(i) {
  281. return valueFalse
  282. }
  283. }
  284. return valueTrue
  285. }
  286. func (r *Runtime) stringproto_includes(call FunctionCall) Value {
  287. r.checkObjectCoercible(call.This)
  288. s := call.This.toString()
  289. searchString := call.Argument(0)
  290. if isRegexp(searchString) {
  291. panic(r.NewTypeError("First argument to String.prototype.includes must not be a regular expression"))
  292. }
  293. searchStr := searchString.toString()
  294. var pos int64
  295. if posArg := call.Argument(1); posArg != _undefined {
  296. pos = posArg.ToInteger()
  297. } else {
  298. pos = 0
  299. }
  300. start := toIntStrict(min(max(pos, 0), int64(s.Length())))
  301. if s.index(searchStr, start) != -1 {
  302. return valueTrue
  303. }
  304. return valueFalse
  305. }
  306. func (r *Runtime) stringproto_indexOf(call FunctionCall) Value {
  307. r.checkObjectCoercible(call.This)
  308. value := call.This.toString()
  309. target := call.Argument(0).toString()
  310. pos := call.Argument(1).ToInteger()
  311. if pos < 0 {
  312. pos = 0
  313. } else {
  314. l := int64(value.Length())
  315. if pos > l {
  316. pos = l
  317. }
  318. }
  319. return intToValue(int64(value.index(target, toIntStrict(pos))))
  320. }
  321. func (r *Runtime) stringproto_lastIndexOf(call FunctionCall) Value {
  322. r.checkObjectCoercible(call.This)
  323. value := call.This.toString()
  324. target := call.Argument(0).toString()
  325. numPos := call.Argument(1).ToNumber()
  326. var pos int64
  327. if f, ok := numPos.(valueFloat); ok && math.IsNaN(float64(f)) {
  328. pos = int64(value.Length())
  329. } else {
  330. pos = numPos.ToInteger()
  331. if pos < 0 {
  332. pos = 0
  333. } else {
  334. l := int64(value.Length())
  335. if pos > l {
  336. pos = l
  337. }
  338. }
  339. }
  340. return intToValue(int64(value.lastIndex(target, toIntStrict(pos))))
  341. }
  342. func (r *Runtime) stringproto_localeCompare(call FunctionCall) Value {
  343. r.checkObjectCoercible(call.This)
  344. this := norm.NFD.String(call.This.toString().String())
  345. that := norm.NFD.String(call.Argument(0).toString().String())
  346. return intToValue(int64(r.collator().CompareString(this, that)))
  347. }
  348. func (r *Runtime) stringproto_match(call FunctionCall) Value {
  349. r.checkObjectCoercible(call.This)
  350. regexp := call.Argument(0)
  351. if regexp != _undefined && regexp != _null {
  352. if matcher := toMethod(r.getV(regexp, SymMatch)); matcher != nil {
  353. return matcher(FunctionCall{
  354. This: regexp,
  355. Arguments: []Value{call.This},
  356. })
  357. }
  358. }
  359. var rx *regexpObject
  360. if regexp, ok := regexp.(*Object); ok {
  361. rx, _ = regexp.self.(*regexpObject)
  362. }
  363. if rx == nil {
  364. rx = r.newRegExp(regexp, nil, r.getRegExpPrototype())
  365. }
  366. if matcher, ok := r.toObject(rx.getSym(SymMatch, nil)).self.assertCallable(); ok {
  367. return matcher(FunctionCall{
  368. This: rx.val,
  369. Arguments: []Value{call.This.toString()},
  370. })
  371. }
  372. panic(r.NewTypeError("RegExp matcher is not a function"))
  373. }
  374. func (r *Runtime) stringproto_matchAll(call FunctionCall) Value {
  375. r.checkObjectCoercible(call.This)
  376. regexp := call.Argument(0)
  377. if regexp != _undefined && regexp != _null {
  378. if isRegexp(regexp) {
  379. if o, ok := regexp.(*Object); ok {
  380. flags := nilSafe(o.self.getStr("flags", nil))
  381. r.checkObjectCoercible(flags)
  382. if !strings.Contains(flags.toString().String(), "g") {
  383. panic(r.NewTypeError("RegExp doesn't have global flag set"))
  384. }
  385. }
  386. }
  387. if matcher := toMethod(r.getV(regexp, SymMatchAll)); matcher != nil {
  388. return matcher(FunctionCall{
  389. This: regexp,
  390. Arguments: []Value{call.This},
  391. })
  392. }
  393. }
  394. rx := r.newRegExp(regexp, asciiString("g"), r.getRegExpPrototype())
  395. if matcher, ok := r.toObject(rx.getSym(SymMatchAll, nil)).self.assertCallable(); ok {
  396. return matcher(FunctionCall{
  397. This: rx.val,
  398. Arguments: []Value{call.This.toString()},
  399. })
  400. }
  401. panic(r.NewTypeError("RegExp matcher is not a function"))
  402. }
  403. func (r *Runtime) stringproto_normalize(call FunctionCall) Value {
  404. r.checkObjectCoercible(call.This)
  405. s := call.This.toString()
  406. var form string
  407. if formArg := call.Argument(0); formArg != _undefined {
  408. form = formArg.toString().toString().String()
  409. } else {
  410. form = "NFC"
  411. }
  412. var f norm.Form
  413. switch form {
  414. case "NFC":
  415. f = norm.NFC
  416. case "NFD":
  417. f = norm.NFD
  418. case "NFKC":
  419. f = norm.NFKC
  420. case "NFKD":
  421. f = norm.NFKD
  422. default:
  423. panic(r.newError(r.getRangeError(), "The normalization form should be one of NFC, NFD, NFKC, NFKD"))
  424. }
  425. switch s := s.(type) {
  426. case asciiString:
  427. return s
  428. case unicodeString:
  429. ss := s.String()
  430. return newStringValue(f.String(ss))
  431. case *importedString:
  432. if s.scanned && s.u == nil {
  433. return asciiString(s.s)
  434. }
  435. return newStringValue(f.String(s.s))
  436. default:
  437. panic(unknownStringTypeErr(s))
  438. }
  439. }
  440. func (r *Runtime) _stringPad(call FunctionCall, start bool) Value {
  441. r.checkObjectCoercible(call.This)
  442. s := call.This.toString()
  443. maxLength := toLength(call.Argument(0))
  444. stringLength := int64(s.Length())
  445. if maxLength <= stringLength {
  446. return s
  447. }
  448. strAscii, strUnicode := devirtualizeString(s)
  449. var filler String
  450. var fillerAscii asciiString
  451. var fillerUnicode unicodeString
  452. if fillString := call.Argument(1); fillString != _undefined {
  453. filler = fillString.toString()
  454. if filler.Length() == 0 {
  455. return s
  456. }
  457. fillerAscii, fillerUnicode = devirtualizeString(filler)
  458. } else {
  459. fillerAscii = " "
  460. filler = fillerAscii
  461. }
  462. remaining := toIntStrict(maxLength - stringLength)
  463. if fillerUnicode == nil && strUnicode == nil {
  464. fl := fillerAscii.Length()
  465. var sb strings.Builder
  466. sb.Grow(toIntStrict(maxLength))
  467. if !start {
  468. sb.WriteString(string(strAscii))
  469. }
  470. for remaining >= fl {
  471. sb.WriteString(string(fillerAscii))
  472. remaining -= fl
  473. }
  474. if remaining > 0 {
  475. sb.WriteString(string(fillerAscii[:remaining]))
  476. }
  477. if start {
  478. sb.WriteString(string(strAscii))
  479. }
  480. return asciiString(sb.String())
  481. }
  482. var sb unicodeStringBuilder
  483. sb.ensureStarted(toIntStrict(maxLength))
  484. if !start {
  485. sb.writeString(s)
  486. }
  487. fl := filler.Length()
  488. for remaining >= fl {
  489. sb.writeString(filler)
  490. remaining -= fl
  491. }
  492. if remaining > 0 {
  493. sb.writeString(filler.Substring(0, remaining))
  494. }
  495. if start {
  496. sb.writeString(s)
  497. }
  498. return sb.String()
  499. }
  500. func (r *Runtime) stringproto_padEnd(call FunctionCall) Value {
  501. return r._stringPad(call, false)
  502. }
  503. func (r *Runtime) stringproto_padStart(call FunctionCall) Value {
  504. return r._stringPad(call, true)
  505. }
  506. func (r *Runtime) stringproto_repeat(call FunctionCall) Value {
  507. r.checkObjectCoercible(call.This)
  508. s := call.This.toString()
  509. n := call.Argument(0).ToNumber()
  510. if n == _positiveInf {
  511. panic(r.newError(r.getRangeError(), "Invalid count value"))
  512. }
  513. numInt := n.ToInteger()
  514. if numInt < 0 {
  515. panic(r.newError(r.getRangeError(), "Invalid count value"))
  516. }
  517. if numInt == 0 || s.Length() == 0 {
  518. return stringEmpty
  519. }
  520. num := toIntStrict(numInt)
  521. a, u := devirtualizeString(s)
  522. if u == nil {
  523. var sb strings.Builder
  524. sb.Grow(len(a) * num)
  525. for i := 0; i < num; i++ {
  526. sb.WriteString(string(a))
  527. }
  528. return asciiString(sb.String())
  529. }
  530. var sb unicodeStringBuilder
  531. sb.Grow(u.Length() * num)
  532. for i := 0; i < num; i++ {
  533. sb.writeUnicodeString(u)
  534. }
  535. return sb.String()
  536. }
  537. func getReplaceValue(replaceValue Value) (str String, rcall func(FunctionCall) Value) {
  538. if replaceValue, ok := replaceValue.(*Object); ok {
  539. if c, ok := replaceValue.self.assertCallable(); ok {
  540. rcall = c
  541. return
  542. }
  543. }
  544. str = replaceValue.toString()
  545. return
  546. }
  547. func stringReplace(s String, found [][]int, newstring String, rcall func(FunctionCall) Value) Value {
  548. if len(found) == 0 {
  549. return s
  550. }
  551. a, u := devirtualizeString(s)
  552. var buf StringBuilder
  553. lastIndex := 0
  554. lengthS := s.Length()
  555. if rcall != nil {
  556. for _, item := range found {
  557. if item[0] != lastIndex {
  558. buf.WriteSubstring(s, lastIndex, item[0])
  559. }
  560. matchCount := len(item) / 2
  561. argumentList := make([]Value, matchCount+2)
  562. for index := 0; index < matchCount; index++ {
  563. offset := 2 * index
  564. if item[offset] != -1 {
  565. if u == nil {
  566. argumentList[index] = a[item[offset]:item[offset+1]]
  567. } else {
  568. argumentList[index] = u.Substring(item[offset], item[offset+1])
  569. }
  570. } else {
  571. argumentList[index] = _undefined
  572. }
  573. }
  574. argumentList[matchCount] = valueInt(item[0])
  575. argumentList[matchCount+1] = s
  576. replacement := rcall(FunctionCall{
  577. This: _undefined,
  578. Arguments: argumentList,
  579. }).toString()
  580. buf.WriteString(replacement)
  581. lastIndex = item[1]
  582. }
  583. } else {
  584. for _, item := range found {
  585. if item[0] != lastIndex {
  586. buf.WriteString(s.Substring(lastIndex, item[0]))
  587. }
  588. matchCount := len(item) / 2
  589. writeSubstitution(s, item[0], matchCount, func(idx int) String {
  590. if item[idx*2] != -1 {
  591. if u == nil {
  592. return a[item[idx*2]:item[idx*2+1]]
  593. }
  594. return u.Substring(item[idx*2], item[idx*2+1])
  595. }
  596. return stringEmpty
  597. }, newstring, &buf)
  598. lastIndex = item[1]
  599. }
  600. }
  601. if lastIndex != lengthS {
  602. buf.WriteString(s.Substring(lastIndex, lengthS))
  603. }
  604. return buf.String()
  605. }
  606. func (r *Runtime) stringproto_replace(call FunctionCall) Value {
  607. r.checkObjectCoercible(call.This)
  608. searchValue := call.Argument(0)
  609. replaceValue := call.Argument(1)
  610. if searchValue != _undefined && searchValue != _null {
  611. if replacer := toMethod(r.getV(searchValue, SymReplace)); replacer != nil {
  612. return replacer(FunctionCall{
  613. This: searchValue,
  614. Arguments: []Value{call.This, replaceValue},
  615. })
  616. }
  617. }
  618. s := call.This.toString()
  619. var found [][]int
  620. searchStr := searchValue.toString()
  621. pos := s.index(searchStr, 0)
  622. if pos != -1 {
  623. found = append(found, []int{pos, pos + searchStr.Length()})
  624. }
  625. str, rcall := getReplaceValue(replaceValue)
  626. return stringReplace(s, found, str, rcall)
  627. }
  628. func (r *Runtime) stringproto_replaceAll(call FunctionCall) Value {
  629. r.checkObjectCoercible(call.This)
  630. searchValue := call.Argument(0)
  631. replaceValue := call.Argument(1)
  632. if searchValue != _undefined && searchValue != _null {
  633. if isRegexp(searchValue) {
  634. if o, ok := searchValue.(*Object); ok {
  635. flags := nilSafe(o.self.getStr("flags", nil))
  636. r.checkObjectCoercible(flags)
  637. if !strings.Contains(flags.toString().String(), "g") {
  638. panic(r.NewTypeError("String.prototype.replaceAll called with a non-global RegExp argument"))
  639. }
  640. }
  641. }
  642. if replacer := toMethod(r.getV(searchValue, SymReplace)); replacer != nil {
  643. return replacer(FunctionCall{
  644. This: searchValue,
  645. Arguments: []Value{call.This, replaceValue},
  646. })
  647. }
  648. }
  649. s := call.This.toString()
  650. var found [][]int
  651. searchStr := searchValue.toString()
  652. searchLength := searchStr.Length()
  653. advanceBy := toIntStrict(max(1, int64(searchLength)))
  654. pos := s.index(searchStr, 0)
  655. for pos != -1 {
  656. found = append(found, []int{pos, pos + searchLength})
  657. pos = s.index(searchStr, pos+advanceBy)
  658. }
  659. str, rcall := getReplaceValue(replaceValue)
  660. return stringReplace(s, found, str, rcall)
  661. }
  662. func (r *Runtime) stringproto_search(call FunctionCall) Value {
  663. r.checkObjectCoercible(call.This)
  664. regexp := call.Argument(0)
  665. if regexp != _undefined && regexp != _null {
  666. if searcher := toMethod(r.getV(regexp, SymSearch)); searcher != nil {
  667. return searcher(FunctionCall{
  668. This: regexp,
  669. Arguments: []Value{call.This},
  670. })
  671. }
  672. }
  673. var rx *regexpObject
  674. if regexp, ok := regexp.(*Object); ok {
  675. rx, _ = regexp.self.(*regexpObject)
  676. }
  677. if rx == nil {
  678. rx = r.newRegExp(regexp, nil, r.getRegExpPrototype())
  679. }
  680. if searcher, ok := r.toObject(rx.getSym(SymSearch, nil)).self.assertCallable(); ok {
  681. return searcher(FunctionCall{
  682. This: rx.val,
  683. Arguments: []Value{call.This.toString()},
  684. })
  685. }
  686. panic(r.NewTypeError("RegExp searcher is not a function"))
  687. }
  688. func (r *Runtime) stringproto_slice(call FunctionCall) Value {
  689. r.checkObjectCoercible(call.This)
  690. s := call.This.toString()
  691. l := int64(s.Length())
  692. start := call.Argument(0).ToInteger()
  693. var end int64
  694. if arg1 := call.Argument(1); arg1 != _undefined {
  695. end = arg1.ToInteger()
  696. } else {
  697. end = l
  698. }
  699. if start < 0 {
  700. start += l
  701. if start < 0 {
  702. start = 0
  703. }
  704. } else {
  705. if start > l {
  706. start = l
  707. }
  708. }
  709. if end < 0 {
  710. end += l
  711. if end < 0 {
  712. end = 0
  713. }
  714. } else {
  715. if end > l {
  716. end = l
  717. }
  718. }
  719. if end > start {
  720. return s.Substring(int(start), int(end))
  721. }
  722. return stringEmpty
  723. }
  724. func (r *Runtime) stringproto_split(call FunctionCall) Value {
  725. r.checkObjectCoercible(call.This)
  726. separatorValue := call.Argument(0)
  727. limitValue := call.Argument(1)
  728. if separatorValue != _undefined && separatorValue != _null {
  729. if splitter := toMethod(r.getV(separatorValue, SymSplit)); splitter != nil {
  730. return splitter(FunctionCall{
  731. This: separatorValue,
  732. Arguments: []Value{call.This, limitValue},
  733. })
  734. }
  735. }
  736. s := call.This.toString()
  737. limit := -1
  738. if limitValue != _undefined {
  739. limit = int(toUint32(limitValue))
  740. }
  741. separatorValue = separatorValue.ToString()
  742. if limit == 0 {
  743. return r.newArrayValues(nil)
  744. }
  745. if separatorValue == _undefined {
  746. return r.newArrayValues([]Value{s})
  747. }
  748. separator := separatorValue.String()
  749. excess := false
  750. str := s.String()
  751. if limit > len(str) {
  752. limit = len(str)
  753. }
  754. splitLimit := limit
  755. if limit > 0 {
  756. splitLimit = limit + 1
  757. excess = true
  758. }
  759. // TODO handle invalid UTF-16
  760. split := strings.SplitN(str, separator, splitLimit)
  761. if excess && len(split) > limit {
  762. split = split[:limit]
  763. }
  764. valueArray := make([]Value, len(split))
  765. for index, value := range split {
  766. valueArray[index] = newStringValue(value)
  767. }
  768. return r.newArrayValues(valueArray)
  769. }
  770. func (r *Runtime) stringproto_startsWith(call FunctionCall) Value {
  771. r.checkObjectCoercible(call.This)
  772. s := call.This.toString()
  773. searchString := call.Argument(0)
  774. if isRegexp(searchString) {
  775. panic(r.NewTypeError("First argument to String.prototype.startsWith must not be a regular expression"))
  776. }
  777. searchStr := searchString.toString()
  778. l := int64(s.Length())
  779. var pos int64
  780. if posArg := call.Argument(1); posArg != _undefined {
  781. pos = posArg.ToInteger()
  782. }
  783. start := toIntStrict(min(max(pos, 0), l))
  784. searchLength := searchStr.Length()
  785. if int64(searchLength+start) > l {
  786. return valueFalse
  787. }
  788. for i := 0; i < searchLength; i++ {
  789. if s.CharAt(start+i) != searchStr.CharAt(i) {
  790. return valueFalse
  791. }
  792. }
  793. return valueTrue
  794. }
  795. func (r *Runtime) stringproto_substring(call FunctionCall) Value {
  796. r.checkObjectCoercible(call.This)
  797. s := call.This.toString()
  798. l := int64(s.Length())
  799. intStart := call.Argument(0).ToInteger()
  800. var intEnd int64
  801. if end := call.Argument(1); end != _undefined {
  802. intEnd = end.ToInteger()
  803. } else {
  804. intEnd = l
  805. }
  806. if intStart < 0 {
  807. intStart = 0
  808. } else if intStart > l {
  809. intStart = l
  810. }
  811. if intEnd < 0 {
  812. intEnd = 0
  813. } else if intEnd > l {
  814. intEnd = l
  815. }
  816. if intStart > intEnd {
  817. intStart, intEnd = intEnd, intStart
  818. }
  819. return s.Substring(int(intStart), int(intEnd))
  820. }
  821. func (r *Runtime) stringproto_toLowerCase(call FunctionCall) Value {
  822. r.checkObjectCoercible(call.This)
  823. s := call.This.toString()
  824. return s.toLower()
  825. }
  826. func (r *Runtime) stringproto_toUpperCase(call FunctionCall) Value {
  827. r.checkObjectCoercible(call.This)
  828. s := call.This.toString()
  829. return s.toUpper()
  830. }
  831. func (r *Runtime) stringproto_trim(call FunctionCall) Value {
  832. r.checkObjectCoercible(call.This)
  833. s := call.This.toString()
  834. // TODO handle invalid UTF-16
  835. return newStringValue(strings.Trim(s.String(), parser.WhitespaceChars))
  836. }
  837. func (r *Runtime) stringproto_trimEnd(call FunctionCall) Value {
  838. r.checkObjectCoercible(call.This)
  839. s := call.This.toString()
  840. // TODO handle invalid UTF-16
  841. return newStringValue(strings.TrimRight(s.String(), parser.WhitespaceChars))
  842. }
  843. func (r *Runtime) stringproto_trimStart(call FunctionCall) Value {
  844. r.checkObjectCoercible(call.This)
  845. s := call.This.toString()
  846. // TODO handle invalid UTF-16
  847. return newStringValue(strings.TrimLeft(s.String(), parser.WhitespaceChars))
  848. }
  849. func (r *Runtime) stringproto_substr(call FunctionCall) Value {
  850. r.checkObjectCoercible(call.This)
  851. s := call.This.toString()
  852. start := call.Argument(0).ToInteger()
  853. var length int64
  854. sl := int64(s.Length())
  855. if arg := call.Argument(1); arg != _undefined {
  856. length = arg.ToInteger()
  857. } else {
  858. length = sl
  859. }
  860. if start < 0 {
  861. start = max(sl+start, 0)
  862. }
  863. length = min(max(length, 0), sl-start)
  864. if length <= 0 {
  865. return stringEmpty
  866. }
  867. return s.Substring(int(start), int(start+length))
  868. }
  869. func (r *Runtime) stringIterProto_next(call FunctionCall) Value {
  870. thisObj := r.toObject(call.This)
  871. if iter, ok := thisObj.self.(*stringIterObject); ok {
  872. return iter.next()
  873. }
  874. panic(r.NewTypeError("Method String Iterator.prototype.next called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
  875. }
  876. func (r *Runtime) createStringIterProto(val *Object) objectImpl {
  877. o := newBaseObjectObj(val, r.getIteratorPrototype(), classObject)
  878. o._putProp("next", r.newNativeFunc(r.stringIterProto_next, "next", 0), true, false, true)
  879. o._putSym(SymToStringTag, valueProp(asciiString(classStringIterator), false, false, true))
  880. return o
  881. }
  882. func (r *Runtime) getStringIteratorPrototype() *Object {
  883. var o *Object
  884. if o = r.global.StringIteratorPrototype; o == nil {
  885. o = &Object{runtime: r}
  886. r.global.StringIteratorPrototype = o
  887. o.self = r.createStringIterProto(o)
  888. }
  889. return o
  890. }
  891. func createStringProtoTemplate() *objectTemplate {
  892. t := newObjectTemplate()
  893. t.protoFactory = func(r *Runtime) *Object {
  894. return r.global.ObjectPrototype
  895. }
  896. t.putStr("length", func(r *Runtime) Value { return valueProp(intToValue(0), false, false, false) })
  897. t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getString(), true, false, true) })
  898. t.putStr("at", func(r *Runtime) Value { return r.methodProp(r.stringproto_at, "at", 1) })
  899. t.putStr("charAt", func(r *Runtime) Value { return r.methodProp(r.stringproto_charAt, "charAt", 1) })
  900. t.putStr("charCodeAt", func(r *Runtime) Value { return r.methodProp(r.stringproto_charCodeAt, "charCodeAt", 1) })
  901. t.putStr("codePointAt", func(r *Runtime) Value { return r.methodProp(r.stringproto_codePointAt, "codePointAt", 1) })
  902. t.putStr("concat", func(r *Runtime) Value { return r.methodProp(r.stringproto_concat, "concat", 1) })
  903. t.putStr("endsWith", func(r *Runtime) Value { return r.methodProp(r.stringproto_endsWith, "endsWith", 1) })
  904. t.putStr("includes", func(r *Runtime) Value { return r.methodProp(r.stringproto_includes, "includes", 1) })
  905. t.putStr("indexOf", func(r *Runtime) Value { return r.methodProp(r.stringproto_indexOf, "indexOf", 1) })
  906. t.putStr("lastIndexOf", func(r *Runtime) Value { return r.methodProp(r.stringproto_lastIndexOf, "lastIndexOf", 1) })
  907. t.putStr("localeCompare", func(r *Runtime) Value { return r.methodProp(r.stringproto_localeCompare, "localeCompare", 1) })
  908. t.putStr("match", func(r *Runtime) Value { return r.methodProp(r.stringproto_match, "match", 1) })
  909. t.putStr("matchAll", func(r *Runtime) Value { return r.methodProp(r.stringproto_matchAll, "matchAll", 1) })
  910. t.putStr("normalize", func(r *Runtime) Value { return r.methodProp(r.stringproto_normalize, "normalize", 0) })
  911. t.putStr("padEnd", func(r *Runtime) Value { return r.methodProp(r.stringproto_padEnd, "padEnd", 1) })
  912. t.putStr("padStart", func(r *Runtime) Value { return r.methodProp(r.stringproto_padStart, "padStart", 1) })
  913. t.putStr("repeat", func(r *Runtime) Value { return r.methodProp(r.stringproto_repeat, "repeat", 1) })
  914. t.putStr("replace", func(r *Runtime) Value { return r.methodProp(r.stringproto_replace, "replace", 2) })
  915. t.putStr("replaceAll", func(r *Runtime) Value { return r.methodProp(r.stringproto_replaceAll, "replaceAll", 2) })
  916. t.putStr("search", func(r *Runtime) Value { return r.methodProp(r.stringproto_search, "search", 1) })
  917. t.putStr("slice", func(r *Runtime) Value { return r.methodProp(r.stringproto_slice, "slice", 2) })
  918. t.putStr("split", func(r *Runtime) Value { return r.methodProp(r.stringproto_split, "split", 2) })
  919. t.putStr("startsWith", func(r *Runtime) Value { return r.methodProp(r.stringproto_startsWith, "startsWith", 1) })
  920. t.putStr("substring", func(r *Runtime) Value { return r.methodProp(r.stringproto_substring, "substring", 2) })
  921. t.putStr("toLocaleLowerCase", func(r *Runtime) Value { return r.methodProp(r.stringproto_toLowerCase, "toLocaleLowerCase", 0) })
  922. t.putStr("toLocaleUpperCase", func(r *Runtime) Value { return r.methodProp(r.stringproto_toUpperCase, "toLocaleUpperCase", 0) })
  923. t.putStr("toLowerCase", func(r *Runtime) Value { return r.methodProp(r.stringproto_toLowerCase, "toLowerCase", 0) })
  924. t.putStr("toString", func(r *Runtime) Value { return r.methodProp(r.stringproto_toString, "toString", 0) })
  925. t.putStr("toUpperCase", func(r *Runtime) Value { return r.methodProp(r.stringproto_toUpperCase, "toUpperCase", 0) })
  926. t.putStr("trim", func(r *Runtime) Value { return r.methodProp(r.stringproto_trim, "trim", 0) })
  927. t.putStr("trimEnd", func(r *Runtime) Value { return valueProp(r.getStringproto_trimEnd(), true, false, true) })
  928. t.putStr("trimStart", func(r *Runtime) Value { return valueProp(r.getStringproto_trimStart(), true, false, true) })
  929. t.putStr("trimRight", func(r *Runtime) Value { return valueProp(r.getStringproto_trimEnd(), true, false, true) })
  930. t.putStr("trimLeft", func(r *Runtime) Value { return valueProp(r.getStringproto_trimStart(), true, false, true) })
  931. t.putStr("valueOf", func(r *Runtime) Value { return r.methodProp(r.stringproto_valueOf, "valueOf", 0) })
  932. // Annex B
  933. t.putStr("substr", func(r *Runtime) Value { return r.methodProp(r.stringproto_substr, "substr", 2) })
  934. t.putSym(SymIterator, func(r *Runtime) Value {
  935. return valueProp(r.newNativeFunc(r.stringproto_iterator, "[Symbol.iterator]", 0), true, false, true)
  936. })
  937. return t
  938. }
  939. func (r *Runtime) getStringproto_trimEnd() *Object {
  940. ret := r.global.stringproto_trimEnd
  941. if ret == nil {
  942. ret = r.newNativeFunc(r.stringproto_trimEnd, "trimEnd", 0)
  943. r.global.stringproto_trimEnd = ret
  944. }
  945. return ret
  946. }
  947. func (r *Runtime) getStringproto_trimStart() *Object {
  948. ret := r.global.stringproto_trimStart
  949. if ret == nil {
  950. ret = r.newNativeFunc(r.stringproto_trimStart, "trimStart", 0)
  951. r.global.stringproto_trimStart = ret
  952. }
  953. return ret
  954. }
  955. func (r *Runtime) getStringSingleton() *stringObject {
  956. ret := r.stringSingleton
  957. if ret == nil {
  958. ret = r.builtin_new(r.getString(), nil).self.(*stringObject)
  959. r.stringSingleton = ret
  960. }
  961. return ret
  962. }
  963. func (r *Runtime) getString() *Object {
  964. ret := r.global.String
  965. if ret == nil {
  966. ret = &Object{runtime: r}
  967. r.global.String = ret
  968. proto := r.getStringPrototype()
  969. o := r.newNativeFuncAndConstruct(ret, r.builtin_String, r.wrapNativeConstruct(r.builtin_newString, ret, proto), proto, "String", intToValue(1))
  970. ret.self = o
  971. o._putProp("fromCharCode", r.newNativeFunc(r.string_fromcharcode, "fromCharCode", 1), true, false, true)
  972. o._putProp("fromCodePoint", r.newNativeFunc(r.string_fromcodepoint, "fromCodePoint", 1), true, false, true)
  973. o._putProp("raw", r.newNativeFunc(r.string_raw, "raw", 1), true, false, true)
  974. }
  975. return ret
  976. }
  977. var stringProtoTemplate *objectTemplate
  978. var stringProtoTemplateOnce sync.Once
  979. func getStringProtoTemplate() *objectTemplate {
  980. stringProtoTemplateOnce.Do(func() {
  981. stringProtoTemplate = createStringProtoTemplate()
  982. })
  983. return stringProtoTemplate
  984. }
  985. func (r *Runtime) getStringPrototype() *Object {
  986. ret := r.global.StringPrototype
  987. if ret == nil {
  988. ret = &Object{runtime: r}
  989. r.global.StringPrototype = ret
  990. o := r.newTemplatedObject(getStringProtoTemplate(), ret)
  991. o.class = classString
  992. }
  993. return ret
  994. }