package goja import ( "math" "strings" "sync" "unicode/utf16" "unicode/utf8" "github.com/dop251/goja/unistring" "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).ToNumber().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() str := s.String() splitLimit := limit if limit > 0 { splitLimit = limit + 1 } // TODO handle invalid UTF-16 split := strings.SplitN(str, separator, splitLimit) if limit > 0 && 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 }