Skip to content

Commit

Permalink
Implement String.prototype.replaceAll
Browse files Browse the repository at this point in the history
  • Loading branch information
shiroyk committed Aug 11, 2023
1 parent c933cf9 commit 6df49a2
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 1 deletion.
39 changes: 39 additions & 0 deletions builtin_string.go
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,44 @@ func (r *Runtime) stringproto_replace(call FunctionCall) Value {
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)
Expand Down Expand Up @@ -976,6 +1014,7 @@ func (r *Runtime) initString() {
o._putProp("padStart", r.newNativeFunc(r.stringproto_padStart, nil, "padStart", nil, 1), true, false, true)
o._putProp("repeat", r.newNativeFunc(r.stringproto_repeat, nil, "repeat", nil, 1), true, false, true)
o._putProp("replace", r.newNativeFunc(r.stringproto_replace, nil, "replace", nil, 2), true, false, true)
o._putProp("replaceAll", r.newNativeFunc(r.stringproto_replaceAll, nil, "replaceAll", nil, 2), true, false, true)
o._putProp("search", r.newNativeFunc(r.stringproto_search, nil, "search", nil, 1), true, false, true)
o._putProp("slice", r.newNativeFunc(r.stringproto_slice, nil, "slice", nil, 2), true, false, true)
o._putProp("split", r.newNativeFunc(r.stringproto_split, nil, "split", nil, 2), true, false, true)
Expand Down
3 changes: 3 additions & 0 deletions string_ascii.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,9 @@ func (s asciiString) CompareTo(other String) int {
func (s asciiString) index(substr String, start int) int {
a, u := devirtualizeString(substr)
if u == nil {
if start > len(s) {
return -1
}
p := strings.Index(string(s[start:]), string(a))
if p >= 0 {
return p + start
Expand Down
4 changes: 3 additions & 1 deletion tc39_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,15 @@ var (
// Character \ missing from character class [\c]
"test/annexB/built-ins/RegExp/RegExp-invalid-control-escape-character-class.js": true,
"test/annexB/built-ins/RegExp/RegExp-control-escape-russian-letter.js": true,

// Skip due to regexp named groups
"test/built-ins/String/prototype/replaceAll/searchValue-replacer-RegExp-call.js": true,
}

featuresBlackList = []string{
"async-iteration",
"Symbol.asyncIterator",
"BigInt",
"String.prototype.replaceAll",
"resizable-arraybuffer",
"regexp-named-groups",
"regexp-dotall",
Expand Down

0 comments on commit 6df49a2

Please sign in to comment.