gitee.com/quant1x/pkg@v0.2.8/goja/builtin_string.go (about)

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