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