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  }