Explorar o código

Implement String.prototype.replaceAll

shiroyk %!s(int64=2) %!d(string=hai) anos
pai
achega
6df49a26cb
Modificáronse 3 ficheiros con 45 adicións e 1 borrados
  1. 39 0
      builtin_string.go
  2. 3 0
      string_ascii.go
  3. 3 1
      tc39_test.go

+ 39 - 0
builtin_string.go

@@ -680,6 +680,44 @@ func (r *Runtime) stringproto_replace(call FunctionCall) Value {
 	return stringReplace(s, found, str, rcall)
 	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 {
 func (r *Runtime) stringproto_search(call FunctionCall) Value {
 	r.checkObjectCoercible(call.This)
 	r.checkObjectCoercible(call.This)
 	regexp := call.Argument(0)
 	regexp := call.Argument(0)
@@ -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("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("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("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("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("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)
 	o._putProp("split", r.newNativeFunc(r.stringproto_split, nil, "split", nil, 2), true, false, true)

+ 3 - 0
string_ascii.go

@@ -313,6 +313,9 @@ func (s asciiString) CompareTo(other String) int {
 func (s asciiString) index(substr String, start int) int {
 func (s asciiString) index(substr String, start int) int {
 	a, u := devirtualizeString(substr)
 	a, u := devirtualizeString(substr)
 	if u == nil {
 	if u == nil {
+		if start > len(s) {
+			return -1
+		}
 		p := strings.Index(string(s[start:]), string(a))
 		p := strings.Index(string(s[start:]), string(a))
 		if p >= 0 {
 		if p >= 0 {
 			return p + start
 			return p + start

+ 3 - 1
tc39_test.go

@@ -196,13 +196,15 @@ var (
 		// Character \ missing from character class [\c]
 		// 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-invalid-control-escape-character-class.js": true,
 		"test/annexB/built-ins/RegExp/RegExp-control-escape-russian-letter.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{
 	featuresBlackList = []string{
 		"async-iteration",
 		"async-iteration",
 		"Symbol.asyncIterator",
 		"Symbol.asyncIterator",
 		"BigInt",
 		"BigInt",
-		"String.prototype.replaceAll",
 		"resizable-arraybuffer",
 		"resizable-arraybuffer",
 		"regexp-named-groups",
 		"regexp-named-groups",
 		"regexp-dotall",
 		"regexp-dotall",