123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116 |
- package goja
- import (
- "github.com/dop251/goja/unistring"
- "math"
- "strings"
- "sync"
- "unicode/utf16"
- "unicode/utf8"
- "github.com/dop251/goja/parser"
- "golang.org/x/text/collate"
- "golang.org/x/text/language"
- "golang.org/x/text/unicode/norm"
- )
- func (r *Runtime) collator() *collate.Collator {
- collator := r._collator
- if collator == nil {
- collator = collate.New(language.Und)
- r._collator = collator
- }
- return collator
- }
- func toString(arg Value) String {
- if s, ok := arg.(String); ok {
- return s
- }
- if s, ok := arg.(*Symbol); ok {
- return s.descriptiveString()
- }
- return arg.toString()
- }
- func (r *Runtime) builtin_String(call FunctionCall) Value {
- if len(call.Arguments) > 0 {
- return toString(call.Arguments[0])
- } else {
- return stringEmpty
- }
- }
- func (r *Runtime) _newString(s String, proto *Object) *Object {
- v := &Object{runtime: r}
- o := &stringObject{}
- o.class = classString
- o.val = v
- o.extensible = true
- v.self = o
- o.prototype = proto
- if s != nil {
- o.value = s
- }
- o.init()
- return v
- }
- func (r *Runtime) builtin_newString(args []Value, proto *Object) *Object {
- var s String
- if len(args) > 0 {
- s = args[0].toString()
- } else {
- s = stringEmpty
- }
- return r._newString(s, proto)
- }
- func (r *Runtime) stringproto_toStringValueOf(this Value, funcName string) Value {
- if str, ok := this.(String); ok {
- return str
- }
- if obj, ok := this.(*Object); ok {
- if strObj, ok := obj.self.(*stringObject); ok {
- return strObj.value
- }
- if reflectObj, ok := obj.self.(*objectGoReflect); ok && reflectObj.class == classString {
- if toString := reflectObj.toString; toString != nil {
- return toString()
- }
- if valueOf := reflectObj.valueOf; valueOf != nil {
- return valueOf()
- }
- }
- if obj == r.global.StringPrototype {
- return stringEmpty
- }
- }
- r.typeErrorResult(true, "String.prototype.%s is called on incompatible receiver", funcName)
- return nil
- }
- func (r *Runtime) stringproto_toString(call FunctionCall) Value {
- return r.stringproto_toStringValueOf(call.This, "toString")
- }
- func (r *Runtime) stringproto_valueOf(call FunctionCall) Value {
- return r.stringproto_toStringValueOf(call.This, "valueOf")
- }
- func (r *Runtime) stringproto_iterator(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- return r.createStringIterator(call.This.toString())
- }
- func (r *Runtime) string_fromcharcode(call FunctionCall) Value {
- b := make([]byte, len(call.Arguments))
- for i, arg := range call.Arguments {
- chr := toUint16(arg)
- if chr >= utf8.RuneSelf {
- bb := make([]uint16, len(call.Arguments)+1)
- bb[0] = unistring.BOM
- bb1 := bb[1:]
- for j := 0; j < i; j++ {
- bb1[j] = uint16(b[j])
- }
- bb1[i] = chr
- i++
- for j, arg := range call.Arguments[i:] {
- bb1[i+j] = toUint16(arg)
- }
- return unicodeString(bb)
- }
- b[i] = byte(chr)
- }
- return asciiString(b)
- }
- func (r *Runtime) string_fromcodepoint(call FunctionCall) Value {
- var sb StringBuilder
- for _, arg := range call.Arguments {
- num := arg.ToNumber()
- var c rune
- if numInt, ok := num.(valueInt); ok {
- if numInt < 0 || numInt > utf8.MaxRune {
- panic(r.newError(r.getRangeError(), "Invalid code point %d", numInt))
- }
- c = rune(numInt)
- } else {
- panic(r.newError(r.getRangeError(), "Invalid code point %s", num))
- }
- sb.WriteRune(c)
- }
- return sb.String()
- }
- func (r *Runtime) string_raw(call FunctionCall) Value {
- cooked := call.Argument(0).ToObject(r)
- raw := nilSafe(cooked.self.getStr("raw", nil)).ToObject(r)
- literalSegments := toLength(raw.self.getStr("length", nil))
- if literalSegments <= 0 {
- return stringEmpty
- }
- var stringElements StringBuilder
- nextIndex := int64(0)
- numberOfSubstitutions := int64(len(call.Arguments) - 1)
- for {
- nextSeg := nilSafe(raw.self.getIdx(valueInt(nextIndex), nil)).toString()
- stringElements.WriteString(nextSeg)
- if nextIndex+1 == literalSegments {
- return stringElements.String()
- }
- if nextIndex < numberOfSubstitutions {
- stringElements.WriteString(nilSafe(call.Arguments[nextIndex+1]).toString())
- }
- nextIndex++
- }
- }
- func (r *Runtime) stringproto_at(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- s := call.This.toString()
- pos := call.Argument(0).ToInteger()
- length := int64(s.Length())
- if pos < 0 {
- pos = length + pos
- }
- if pos >= length || pos < 0 {
- return _undefined
- }
- return s.Substring(int(pos), int(pos+1))
- }
- func (r *Runtime) stringproto_charAt(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- s := call.This.toString()
- pos := call.Argument(0).ToInteger()
- if pos < 0 || pos >= int64(s.Length()) {
- return stringEmpty
- }
- return s.Substring(int(pos), int(pos+1))
- }
- func (r *Runtime) stringproto_charCodeAt(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- s := call.This.toString()
- pos := call.Argument(0).ToInteger()
- if pos < 0 || pos >= int64(s.Length()) {
- return _NaN
- }
- return intToValue(int64(s.CharAt(toIntStrict(pos)) & 0xFFFF))
- }
- func (r *Runtime) stringproto_codePointAt(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- s := call.This.toString()
- p := call.Argument(0).ToInteger()
- size := s.Length()
- if p < 0 || p >= int64(size) {
- return _undefined
- }
- pos := toIntStrict(p)
- first := s.CharAt(pos)
- if isUTF16FirstSurrogate(first) {
- pos++
- if pos < size {
- second := s.CharAt(pos)
- if isUTF16SecondSurrogate(second) {
- return intToValue(int64(utf16.DecodeRune(rune(first), rune(second))))
- }
- }
- }
- return intToValue(int64(first & 0xFFFF))
- }
- func (r *Runtime) stringproto_concat(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- strs := make([]String, len(call.Arguments)+1)
- a, u := devirtualizeString(call.This.toString())
- allAscii := true
- totalLen := 0
- if u == nil {
- strs[0] = a
- totalLen = len(a)
- } else {
- strs[0] = u
- totalLen = u.Length()
- allAscii = false
- }
- for i, arg := range call.Arguments {
- a, u := devirtualizeString(arg.toString())
- if u != nil {
- allAscii = false
- totalLen += u.Length()
- strs[i+1] = u
- } else {
- totalLen += a.Length()
- strs[i+1] = a
- }
- }
- if allAscii {
- var buf strings.Builder
- buf.Grow(totalLen)
- for _, s := range strs {
- buf.WriteString(s.String())
- }
- return asciiString(buf.String())
- } else {
- buf := make([]uint16, totalLen+1)
- buf[0] = unistring.BOM
- pos := 1
- for _, s := range strs {
- switch s := s.(type) {
- case asciiString:
- for i := 0; i < len(s); i++ {
- buf[pos] = uint16(s[i])
- pos++
- }
- case unicodeString:
- copy(buf[pos:], s[1:])
- pos += s.Length()
- }
- }
- return unicodeString(buf)
- }
- }
- func (r *Runtime) stringproto_endsWith(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- s := call.This.toString()
- searchString := call.Argument(0)
- if isRegexp(searchString) {
- panic(r.NewTypeError("First argument to String.prototype.endsWith must not be a regular expression"))
- }
- searchStr := searchString.toString()
- l := int64(s.Length())
- var pos int64
- if posArg := call.Argument(1); posArg != _undefined {
- pos = posArg.ToInteger()
- } else {
- pos = l
- }
- end := toIntStrict(min(max(pos, 0), l))
- searchLength := searchStr.Length()
- start := end - searchLength
- if start < 0 {
- return valueFalse
- }
- for i := 0; i < searchLength; i++ {
- if s.CharAt(start+i) != searchStr.CharAt(i) {
- return valueFalse
- }
- }
- return valueTrue
- }
- func (r *Runtime) stringproto_includes(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- s := call.This.toString()
- searchString := call.Argument(0)
- if isRegexp(searchString) {
- panic(r.NewTypeError("First argument to String.prototype.includes must not be a regular expression"))
- }
- searchStr := searchString.toString()
- var pos int64
- if posArg := call.Argument(1); posArg != _undefined {
- pos = posArg.ToInteger()
- } else {
- pos = 0
- }
- start := toIntStrict(min(max(pos, 0), int64(s.Length())))
- if s.index(searchStr, start) != -1 {
- return valueTrue
- }
- return valueFalse
- }
- func (r *Runtime) stringproto_indexOf(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- value := call.This.toString()
- target := call.Argument(0).toString()
- pos := call.Argument(1).ToInteger()
- if pos < 0 {
- pos = 0
- } else {
- l := int64(value.Length())
- if pos > l {
- pos = l
- }
- }
- return intToValue(int64(value.index(target, toIntStrict(pos))))
- }
- func (r *Runtime) stringproto_lastIndexOf(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- value := call.This.toString()
- target := call.Argument(0).toString()
- numPos := call.Argument(1).ToNumber()
- var pos int64
- if f, ok := numPos.(valueFloat); ok && math.IsNaN(float64(f)) {
- pos = int64(value.Length())
- } else {
- pos = numPos.ToInteger()
- if pos < 0 {
- pos = 0
- } else {
- l := int64(value.Length())
- if pos > l {
- pos = l
- }
- }
- }
- return intToValue(int64(value.lastIndex(target, toIntStrict(pos))))
- }
- func (r *Runtime) stringproto_localeCompare(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- this := norm.NFD.String(call.This.toString().String())
- that := norm.NFD.String(call.Argument(0).toString().String())
- return intToValue(int64(r.collator().CompareString(this, that)))
- }
- func (r *Runtime) stringproto_match(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- regexp := call.Argument(0)
- if regexp != _undefined && regexp != _null {
- if matcher := toMethod(r.getV(regexp, SymMatch)); matcher != nil {
- return matcher(FunctionCall{
- This: regexp,
- Arguments: []Value{call.This},
- })
- }
- }
- var rx *regexpObject
- if regexp, ok := regexp.(*Object); ok {
- rx, _ = regexp.self.(*regexpObject)
- }
- if rx == nil {
- rx = r.newRegExp(regexp, nil, r.getRegExpPrototype())
- }
- if matcher, ok := r.toObject(rx.getSym(SymMatch, nil)).self.assertCallable(); ok {
- return matcher(FunctionCall{
- This: rx.val,
- Arguments: []Value{call.This.toString()},
- })
- }
- panic(r.NewTypeError("RegExp matcher is not a function"))
- }
- func (r *Runtime) stringproto_matchAll(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- regexp := call.Argument(0)
- if regexp != _undefined && regexp != _null {
- if isRegexp(regexp) {
- if o, ok := regexp.(*Object); ok {
- flags := nilSafe(o.self.getStr("flags", nil))
- r.checkObjectCoercible(flags)
- if !strings.Contains(flags.toString().String(), "g") {
- panic(r.NewTypeError("RegExp doesn't have global flag set"))
- }
- }
- }
- if matcher := toMethod(r.getV(regexp, SymMatchAll)); matcher != nil {
- return matcher(FunctionCall{
- This: regexp,
- Arguments: []Value{call.This},
- })
- }
- }
- rx := r.newRegExp(regexp, asciiString("g"), r.getRegExpPrototype())
- if matcher, ok := r.toObject(rx.getSym(SymMatchAll, nil)).self.assertCallable(); ok {
- return matcher(FunctionCall{
- This: rx.val,
- Arguments: []Value{call.This.toString()},
- })
- }
- panic(r.NewTypeError("RegExp matcher is not a function"))
- }
- func (r *Runtime) stringproto_normalize(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- s := call.This.toString()
- var form string
- if formArg := call.Argument(0); formArg != _undefined {
- form = formArg.toString().toString().String()
- } else {
- form = "NFC"
- }
- var f norm.Form
- switch form {
- case "NFC":
- f = norm.NFC
- case "NFD":
- f = norm.NFD
- case "NFKC":
- f = norm.NFKC
- case "NFKD":
- f = norm.NFKD
- default:
- panic(r.newError(r.getRangeError(), "The normalization form should be one of NFC, NFD, NFKC, NFKD"))
- }
- switch s := s.(type) {
- case asciiString:
- return s
- case unicodeString:
- ss := s.String()
- return newStringValue(f.String(ss))
- case *importedString:
- if s.scanned && s.u == nil {
- return asciiString(s.s)
- }
- return newStringValue(f.String(s.s))
- default:
- panic(unknownStringTypeErr(s))
- }
- }
- func (r *Runtime) _stringPad(call FunctionCall, start bool) Value {
- r.checkObjectCoercible(call.This)
- s := call.This.toString()
- maxLength := toLength(call.Argument(0))
- stringLength := int64(s.Length())
- if maxLength <= stringLength {
- return s
- }
- strAscii, strUnicode := devirtualizeString(s)
- var filler String
- var fillerAscii asciiString
- var fillerUnicode unicodeString
- if fillString := call.Argument(1); fillString != _undefined {
- filler = fillString.toString()
- if filler.Length() == 0 {
- return s
- }
- fillerAscii, fillerUnicode = devirtualizeString(filler)
- } else {
- fillerAscii = " "
- filler = fillerAscii
- }
- remaining := toIntStrict(maxLength - stringLength)
- if fillerUnicode == nil && strUnicode == nil {
- fl := fillerAscii.Length()
- var sb strings.Builder
- sb.Grow(toIntStrict(maxLength))
- if !start {
- sb.WriteString(string(strAscii))
- }
- for remaining >= fl {
- sb.WriteString(string(fillerAscii))
- remaining -= fl
- }
- if remaining > 0 {
- sb.WriteString(string(fillerAscii[:remaining]))
- }
- if start {
- sb.WriteString(string(strAscii))
- }
- return asciiString(sb.String())
- }
- var sb unicodeStringBuilder
- sb.ensureStarted(toIntStrict(maxLength))
- if !start {
- sb.writeString(s)
- }
- fl := filler.Length()
- for remaining >= fl {
- sb.writeString(filler)
- remaining -= fl
- }
- if remaining > 0 {
- sb.writeString(filler.Substring(0, remaining))
- }
- if start {
- sb.writeString(s)
- }
- return sb.String()
- }
- func (r *Runtime) stringproto_padEnd(call FunctionCall) Value {
- return r._stringPad(call, false)
- }
- func (r *Runtime) stringproto_padStart(call FunctionCall) Value {
- return r._stringPad(call, true)
- }
- func (r *Runtime) stringproto_repeat(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- s := call.This.toString()
- n := call.Argument(0).ToNumber()
- if n == _positiveInf {
- panic(r.newError(r.getRangeError(), "Invalid count value"))
- }
- numInt := n.ToInteger()
- if numInt < 0 {
- panic(r.newError(r.getRangeError(), "Invalid count value"))
- }
- if numInt == 0 || s.Length() == 0 {
- return stringEmpty
- }
- num := toIntStrict(numInt)
- a, u := devirtualizeString(s)
- if u == nil {
- var sb strings.Builder
- sb.Grow(len(a) * num)
- for i := 0; i < num; i++ {
- sb.WriteString(string(a))
- }
- return asciiString(sb.String())
- }
- var sb unicodeStringBuilder
- sb.Grow(u.Length() * num)
- for i := 0; i < num; i++ {
- sb.writeUnicodeString(u)
- }
- return sb.String()
- }
- func getReplaceValue(replaceValue Value) (str String, rcall func(FunctionCall) Value) {
- if replaceValue, ok := replaceValue.(*Object); ok {
- if c, ok := replaceValue.self.assertCallable(); ok {
- rcall = c
- return
- }
- }
- str = replaceValue.toString()
- return
- }
- func stringReplace(s String, found [][]int, newstring String, rcall func(FunctionCall) Value) Value {
- if len(found) == 0 {
- return s
- }
- a, u := devirtualizeString(s)
- var buf StringBuilder
- lastIndex := 0
- lengthS := s.Length()
- if rcall != nil {
- for _, item := range found {
- if item[0] != lastIndex {
- buf.WriteSubstring(s, lastIndex, item[0])
- }
- matchCount := len(item) / 2
- argumentList := make([]Value, matchCount+2)
- for index := 0; index < matchCount; index++ {
- offset := 2 * index
- if item[offset] != -1 {
- if u == nil {
- argumentList[index] = a[item[offset]:item[offset+1]]
- } else {
- argumentList[index] = u.Substring(item[offset], item[offset+1])
- }
- } else {
- argumentList[index] = _undefined
- }
- }
- argumentList[matchCount] = valueInt(item[0])
- argumentList[matchCount+1] = s
- replacement := rcall(FunctionCall{
- This: _undefined,
- Arguments: argumentList,
- }).toString()
- buf.WriteString(replacement)
- lastIndex = item[1]
- }
- } else {
- for _, item := range found {
- if item[0] != lastIndex {
- buf.WriteString(s.Substring(lastIndex, item[0]))
- }
- matchCount := len(item) / 2
- writeSubstitution(s, item[0], matchCount, func(idx int) String {
- if item[idx*2] != -1 {
- if u == nil {
- return a[item[idx*2]:item[idx*2+1]]
- }
- return u.Substring(item[idx*2], item[idx*2+1])
- }
- return stringEmpty
- }, newstring, &buf)
- lastIndex = item[1]
- }
- }
- if lastIndex != lengthS {
- buf.WriteString(s.Substring(lastIndex, lengthS))
- }
- return buf.String()
- }
- func (r *Runtime) stringproto_replace(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- searchValue := call.Argument(0)
- replaceValue := call.Argument(1)
- if searchValue != _undefined && searchValue != _null {
- if replacer := toMethod(r.getV(searchValue, SymReplace)); replacer != nil {
- return replacer(FunctionCall{
- This: searchValue,
- Arguments: []Value{call.This, replaceValue},
- })
- }
- }
- s := call.This.toString()
- var found [][]int
- searchStr := searchValue.toString()
- pos := s.index(searchStr, 0)
- if pos != -1 {
- found = append(found, []int{pos, pos + searchStr.Length()})
- }
- str, rcall := getReplaceValue(replaceValue)
- return stringReplace(s, found, str, rcall)
- }
- func (r *Runtime) stringproto_replaceAll(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- searchValue := call.Argument(0)
- replaceValue := call.Argument(1)
- if searchValue != _undefined && searchValue != _null {
- if isRegexp(searchValue) {
- if o, ok := searchValue.(*Object); ok {
- flags := nilSafe(o.self.getStr("flags", nil))
- r.checkObjectCoercible(flags)
- if !strings.Contains(flags.toString().String(), "g") {
- panic(r.NewTypeError("String.prototype.replaceAll called with a non-global RegExp argument"))
- }
- }
- }
- if replacer := toMethod(r.getV(searchValue, SymReplace)); replacer != nil {
- return replacer(FunctionCall{
- This: searchValue,
- Arguments: []Value{call.This, replaceValue},
- })
- }
- }
- s := call.This.toString()
- var found [][]int
- searchStr := searchValue.toString()
- searchLength := searchStr.Length()
- advanceBy := toIntStrict(max(1, int64(searchLength)))
- pos := s.index(searchStr, 0)
- for pos != -1 {
- found = append(found, []int{pos, pos + searchLength})
- pos = s.index(searchStr, pos+advanceBy)
- }
- str, rcall := getReplaceValue(replaceValue)
- return stringReplace(s, found, str, rcall)
- }
- func (r *Runtime) stringproto_search(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- regexp := call.Argument(0)
- if regexp != _undefined && regexp != _null {
- if searcher := toMethod(r.getV(regexp, SymSearch)); searcher != nil {
- return searcher(FunctionCall{
- This: regexp,
- Arguments: []Value{call.This},
- })
- }
- }
- var rx *regexpObject
- if regexp, ok := regexp.(*Object); ok {
- rx, _ = regexp.self.(*regexpObject)
- }
- if rx == nil {
- rx = r.newRegExp(regexp, nil, r.getRegExpPrototype())
- }
- if searcher, ok := r.toObject(rx.getSym(SymSearch, nil)).self.assertCallable(); ok {
- return searcher(FunctionCall{
- This: rx.val,
- Arguments: []Value{call.This.toString()},
- })
- }
- panic(r.NewTypeError("RegExp searcher is not a function"))
- }
- func (r *Runtime) stringproto_slice(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- s := call.This.toString()
- l := int64(s.Length())
- start := call.Argument(0).ToInteger()
- var end int64
- if arg1 := call.Argument(1); arg1 != _undefined {
- end = arg1.ToInteger()
- } else {
- end = l
- }
- if start < 0 {
- start += l
- if start < 0 {
- start = 0
- }
- } else {
- if start > l {
- start = l
- }
- }
- if end < 0 {
- end += l
- if end < 0 {
- end = 0
- }
- } else {
- if end > l {
- end = l
- }
- }
- if end > start {
- return s.Substring(int(start), int(end))
- }
- return stringEmpty
- }
- func (r *Runtime) stringproto_split(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- separatorValue := call.Argument(0)
- limitValue := call.Argument(1)
- if separatorValue != _undefined && separatorValue != _null {
- if splitter := toMethod(r.getV(separatorValue, SymSplit)); splitter != nil {
- return splitter(FunctionCall{
- This: separatorValue,
- Arguments: []Value{call.This, limitValue},
- })
- }
- }
- s := call.This.toString()
- limit := -1
- if limitValue != _undefined {
- limit = int(toUint32(limitValue))
- }
- separatorValue = separatorValue.ToString()
- if limit == 0 {
- return r.newArrayValues(nil)
- }
- if separatorValue == _undefined {
- return r.newArrayValues([]Value{s})
- }
- separator := separatorValue.String()
- excess := false
- str := s.String()
- if limit > len(str) {
- limit = len(str)
- }
- splitLimit := limit
- if limit > 0 {
- splitLimit = limit + 1
- excess = true
- }
- // TODO handle invalid UTF-16
- split := strings.SplitN(str, separator, splitLimit)
- if excess && len(split) > limit {
- split = split[:limit]
- }
- valueArray := make([]Value, len(split))
- for index, value := range split {
- valueArray[index] = newStringValue(value)
- }
- return r.newArrayValues(valueArray)
- }
- func (r *Runtime) stringproto_startsWith(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- s := call.This.toString()
- searchString := call.Argument(0)
- if isRegexp(searchString) {
- panic(r.NewTypeError("First argument to String.prototype.startsWith must not be a regular expression"))
- }
- searchStr := searchString.toString()
- l := int64(s.Length())
- var pos int64
- if posArg := call.Argument(1); posArg != _undefined {
- pos = posArg.ToInteger()
- }
- start := toIntStrict(min(max(pos, 0), l))
- searchLength := searchStr.Length()
- if int64(searchLength+start) > l {
- return valueFalse
- }
- for i := 0; i < searchLength; i++ {
- if s.CharAt(start+i) != searchStr.CharAt(i) {
- return valueFalse
- }
- }
- return valueTrue
- }
- func (r *Runtime) stringproto_substring(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- s := call.This.toString()
- l := int64(s.Length())
- intStart := call.Argument(0).ToInteger()
- var intEnd int64
- if end := call.Argument(1); end != _undefined {
- intEnd = end.ToInteger()
- } else {
- intEnd = l
- }
- if intStart < 0 {
- intStart = 0
- } else if intStart > l {
- intStart = l
- }
- if intEnd < 0 {
- intEnd = 0
- } else if intEnd > l {
- intEnd = l
- }
- if intStart > intEnd {
- intStart, intEnd = intEnd, intStart
- }
- return s.Substring(int(intStart), int(intEnd))
- }
- func (r *Runtime) stringproto_toLowerCase(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- s := call.This.toString()
- return s.toLower()
- }
- func (r *Runtime) stringproto_toUpperCase(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- s := call.This.toString()
- return s.toUpper()
- }
- func (r *Runtime) stringproto_trim(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- s := call.This.toString()
- // TODO handle invalid UTF-16
- return newStringValue(strings.Trim(s.String(), parser.WhitespaceChars))
- }
- func (r *Runtime) stringproto_trimEnd(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- s := call.This.toString()
- // TODO handle invalid UTF-16
- return newStringValue(strings.TrimRight(s.String(), parser.WhitespaceChars))
- }
- func (r *Runtime) stringproto_trimStart(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- s := call.This.toString()
- // TODO handle invalid UTF-16
- return newStringValue(strings.TrimLeft(s.String(), parser.WhitespaceChars))
- }
- func (r *Runtime) stringproto_substr(call FunctionCall) Value {
- r.checkObjectCoercible(call.This)
- s := call.This.toString()
- start := call.Argument(0).ToInteger()
- var length int64
- sl := int64(s.Length())
- if arg := call.Argument(1); arg != _undefined {
- length = arg.ToInteger()
- } else {
- length = sl
- }
- if start < 0 {
- start = max(sl+start, 0)
- }
- length = min(max(length, 0), sl-start)
- if length <= 0 {
- return stringEmpty
- }
- return s.Substring(int(start), int(start+length))
- }
- func (r *Runtime) stringIterProto_next(call FunctionCall) Value {
- thisObj := r.toObject(call.This)
- if iter, ok := thisObj.self.(*stringIterObject); ok {
- return iter.next()
- }
- panic(r.NewTypeError("Method String Iterator.prototype.next called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
- }
- func (r *Runtime) createStringIterProto(val *Object) objectImpl {
- o := newBaseObjectObj(val, r.getIteratorPrototype(), classObject)
- o._putProp("next", r.newNativeFunc(r.stringIterProto_next, "next", 0), true, false, true)
- o._putSym(SymToStringTag, valueProp(asciiString(classStringIterator), false, false, true))
- return o
- }
- func (r *Runtime) getStringIteratorPrototype() *Object {
- var o *Object
- if o = r.global.StringIteratorPrototype; o == nil {
- o = &Object{runtime: r}
- r.global.StringIteratorPrototype = o
- o.self = r.createStringIterProto(o)
- }
- return o
- }
- func createStringProtoTemplate() *objectTemplate {
- t := newObjectTemplate()
- t.protoFactory = func(r *Runtime) *Object {
- return r.global.ObjectPrototype
- }
- t.putStr("length", func(r *Runtime) Value { return valueProp(intToValue(0), false, false, false) })
- t.putStr("constructor", func(r *Runtime) Value { return valueProp(r.getString(), true, false, true) })
- t.putStr("at", func(r *Runtime) Value { return r.methodProp(r.stringproto_at, "at", 1) })
- t.putStr("charAt", func(r *Runtime) Value { return r.methodProp(r.stringproto_charAt, "charAt", 1) })
- t.putStr("charCodeAt", func(r *Runtime) Value { return r.methodProp(r.stringproto_charCodeAt, "charCodeAt", 1) })
- t.putStr("codePointAt", func(r *Runtime) Value { return r.methodProp(r.stringproto_codePointAt, "codePointAt", 1) })
- t.putStr("concat", func(r *Runtime) Value { return r.methodProp(r.stringproto_concat, "concat", 1) })
- t.putStr("endsWith", func(r *Runtime) Value { return r.methodProp(r.stringproto_endsWith, "endsWith", 1) })
- t.putStr("includes", func(r *Runtime) Value { return r.methodProp(r.stringproto_includes, "includes", 1) })
- t.putStr("indexOf", func(r *Runtime) Value { return r.methodProp(r.stringproto_indexOf, "indexOf", 1) })
- t.putStr("lastIndexOf", func(r *Runtime) Value { return r.methodProp(r.stringproto_lastIndexOf, "lastIndexOf", 1) })
- t.putStr("localeCompare", func(r *Runtime) Value { return r.methodProp(r.stringproto_localeCompare, "localeCompare", 1) })
- t.putStr("match", func(r *Runtime) Value { return r.methodProp(r.stringproto_match, "match", 1) })
- t.putStr("matchAll", func(r *Runtime) Value { return r.methodProp(r.stringproto_matchAll, "matchAll", 1) })
- t.putStr("normalize", func(r *Runtime) Value { return r.methodProp(r.stringproto_normalize, "normalize", 0) })
- t.putStr("padEnd", func(r *Runtime) Value { return r.methodProp(r.stringproto_padEnd, "padEnd", 1) })
- t.putStr("padStart", func(r *Runtime) Value { return r.methodProp(r.stringproto_padStart, "padStart", 1) })
- t.putStr("repeat", func(r *Runtime) Value { return r.methodProp(r.stringproto_repeat, "repeat", 1) })
- t.putStr("replace", func(r *Runtime) Value { return r.methodProp(r.stringproto_replace, "replace", 2) })
- t.putStr("replaceAll", func(r *Runtime) Value { return r.methodProp(r.stringproto_replaceAll, "replaceAll", 2) })
- t.putStr("search", func(r *Runtime) Value { return r.methodProp(r.stringproto_search, "search", 1) })
- t.putStr("slice", func(r *Runtime) Value { return r.methodProp(r.stringproto_slice, "slice", 2) })
- t.putStr("split", func(r *Runtime) Value { return r.methodProp(r.stringproto_split, "split", 2) })
- t.putStr("startsWith", func(r *Runtime) Value { return r.methodProp(r.stringproto_startsWith, "startsWith", 1) })
- t.putStr("substring", func(r *Runtime) Value { return r.methodProp(r.stringproto_substring, "substring", 2) })
- t.putStr("toLocaleLowerCase", func(r *Runtime) Value { return r.methodProp(r.stringproto_toLowerCase, "toLocaleLowerCase", 0) })
- t.putStr("toLocaleUpperCase", func(r *Runtime) Value { return r.methodProp(r.stringproto_toUpperCase, "toLocaleUpperCase", 0) })
- t.putStr("toLowerCase", func(r *Runtime) Value { return r.methodProp(r.stringproto_toLowerCase, "toLowerCase", 0) })
- t.putStr("toString", func(r *Runtime) Value { return r.methodProp(r.stringproto_toString, "toString", 0) })
- t.putStr("toUpperCase", func(r *Runtime) Value { return r.methodProp(r.stringproto_toUpperCase, "toUpperCase", 0) })
- t.putStr("trim", func(r *Runtime) Value { return r.methodProp(r.stringproto_trim, "trim", 0) })
- t.putStr("trimEnd", func(r *Runtime) Value { return valueProp(r.getStringproto_trimEnd(), true, false, true) })
- t.putStr("trimStart", func(r *Runtime) Value { return valueProp(r.getStringproto_trimStart(), true, false, true) })
- t.putStr("trimRight", func(r *Runtime) Value { return valueProp(r.getStringproto_trimEnd(), true, false, true) })
- t.putStr("trimLeft", func(r *Runtime) Value { return valueProp(r.getStringproto_trimStart(), true, false, true) })
- t.putStr("valueOf", func(r *Runtime) Value { return r.methodProp(r.stringproto_valueOf, "valueOf", 0) })
- // Annex B
- t.putStr("substr", func(r *Runtime) Value { return r.methodProp(r.stringproto_substr, "substr", 2) })
- t.putSym(SymIterator, func(r *Runtime) Value {
- return valueProp(r.newNativeFunc(r.stringproto_iterator, "[Symbol.iterator]", 0), true, false, true)
- })
- return t
- }
- func (r *Runtime) getStringproto_trimEnd() *Object {
- ret := r.global.stringproto_trimEnd
- if ret == nil {
- ret = r.newNativeFunc(r.stringproto_trimEnd, "trimEnd", 0)
- r.global.stringproto_trimEnd = ret
- }
- return ret
- }
- func (r *Runtime) getStringproto_trimStart() *Object {
- ret := r.global.stringproto_trimStart
- if ret == nil {
- ret = r.newNativeFunc(r.stringproto_trimStart, "trimStart", 0)
- r.global.stringproto_trimStart = ret
- }
- return ret
- }
- func (r *Runtime) getStringSingleton() *stringObject {
- ret := r.stringSingleton
- if ret == nil {
- ret = r.builtin_new(r.getString(), nil).self.(*stringObject)
- r.stringSingleton = ret
- }
- return ret
- }
- func (r *Runtime) getString() *Object {
- ret := r.global.String
- if ret == nil {
- ret = &Object{runtime: r}
- r.global.String = ret
- proto := r.getStringPrototype()
- o := r.newNativeFuncAndConstruct(ret, r.builtin_String, r.wrapNativeConstruct(r.builtin_newString, ret, proto), proto, "String", intToValue(1))
- ret.self = o
- o._putProp("fromCharCode", r.newNativeFunc(r.string_fromcharcode, "fromCharCode", 1), true, false, true)
- o._putProp("fromCodePoint", r.newNativeFunc(r.string_fromcodepoint, "fromCodePoint", 1), true, false, true)
- o._putProp("raw", r.newNativeFunc(r.string_raw, "raw", 1), true, false, true)
- }
- return ret
- }
- var stringProtoTemplate *objectTemplate
- var stringProtoTemplateOnce sync.Once
- func getStringProtoTemplate() *objectTemplate {
- stringProtoTemplateOnce.Do(func() {
- stringProtoTemplate = createStringProtoTemplate()
- })
- return stringProtoTemplate
- }
- func (r *Runtime) getStringPrototype() *Object {
- ret := r.global.StringPrototype
- if ret == nil {
- ret = &Object{runtime: r}
- r.global.StringPrototype = ret
- o := r.newTemplatedObject(getStringProtoTemplate(), ret)
- o.class = classString
- }
- return ret
- }
|