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