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  }