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