瀏覽代碼

Fixed setting of lastIndex after String.replace() and String.match(). Closes #682

Dmitry Panov 1 月之前
父節點
當前提交
7516b814d4
共有 3 個文件被更改,包括 46 次插入33 次删除
  1. 8 9
      builtin_regexp.go
  2. 13 24
      regexp.go
  3. 25 0
      regexp_test.go

+ 8 - 9
builtin_regexp.go

@@ -2,11 +2,12 @@ package goja
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"github.com/dop251/goja/parser"
 	"regexp"
 	"regexp"
 	"strings"
 	"strings"
 	"unicode/utf16"
 	"unicode/utf16"
 	"unicode/utf8"
 	"unicode/utf8"
+
+	"github.com/dop251/goja/parser"
 )
 )
 
 
 func (r *Runtime) newRegexpObject(proto *Object) *regexpObject {
 func (r *Runtime) newRegexpObject(proto *Object) *regexpObject {
@@ -762,16 +763,15 @@ func (r *Runtime) regexpproto_stdMatcher(call FunctionCall) Value {
 		return r.regexpproto_stdMatcherGeneric(thisObj, s)
 		return r.regexpproto_stdMatcherGeneric(thisObj, s)
 	}
 	}
 	if rx.pattern.global {
 	if rx.pattern.global {
+		rx.setOwnStr("lastIndex", intToValue(0), true)
 		res := rx.pattern.findAllSubmatchIndex(s, 0, -1, rx.pattern.sticky)
 		res := rx.pattern.findAllSubmatchIndex(s, 0, -1, rx.pattern.sticky)
 		if len(res) == 0 {
 		if len(res) == 0 {
-			rx.setOwnStr("lastIndex", intToValue(0), true)
 			return _null
 			return _null
 		}
 		}
 		a := make([]Value, 0, len(res))
 		a := make([]Value, 0, len(res))
 		for _, result := range res {
 		for _, result := range res {
 			a = append(a, s.Substring(result[0], result[1]))
 			a = append(a, s.Substring(result[0], result[1]))
 		}
 		}
-		rx.setOwnStr("lastIndex", intToValue(int64(res[len(res)-1][1])), true)
 		return r.newArrayValues(a)
 		return r.newArrayValues(a)
 	} else {
 	} else {
 		return rx.exec(s)
 		return rx.exec(s)
@@ -1226,17 +1226,16 @@ func (r *Runtime) regexpproto_stdReplacer(call FunctionCall) Value {
 	find := 1
 	find := 1
 	if rx.pattern.global {
 	if rx.pattern.global {
 		find = -1
 		find = -1
-		rx.setOwnStr("lastIndex", intToValue(0), true)
 	} else {
 	} else {
 		index = rx.getLastIndex()
 		index = rx.getLastIndex()
 	}
 	}
 	found := rx.pattern.findAllSubmatchIndex(s, toIntStrict(index), find, rx.pattern.sticky)
 	found := rx.pattern.findAllSubmatchIndex(s, toIntStrict(index), find, rx.pattern.sticky)
-	if len(found) > 0 {
-		if !rx.updateLastIndex(index, found[0], found[len(found)-1]) {
-			found = nil
+	if rx.pattern.global || rx.pattern.sticky {
+		var newLastIndex int64
+		if !rx.pattern.global && len(found) > 0 {
+			newLastIndex = int64(found[len(found)-1][1])
 		}
 		}
-	} else {
-		rx.updateLastIndex(index, nil, nil)
+		rx.setOwnStr("lastIndex", intToValue(newLastIndex), true)
 	}
 	}
 
 
 	return stringReplace(s, found, replaceStr, rcall)
 	return stringReplace(s, found, replaceStr, rcall)

+ 13 - 24
regexp.go

@@ -2,13 +2,14 @@ package goja
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"github.com/dlclark/regexp2"
-	"github.com/dop251/goja/unistring"
 	"io"
 	"io"
 	"regexp"
 	"regexp"
 	"sort"
 	"sort"
 	"strings"
 	"strings"
 	"unicode/utf16"
 	"unicode/utf16"
+
+	"github.com/dlclark/regexp2"
+	"github.com/dop251/goja/unistring"
 )
 )
 
 
 type regexp2MatchCache struct {
 type regexp2MatchCache struct {
@@ -538,33 +539,21 @@ func (r *regexpObject) getLastIndex() int64 {
 	return lastIndex
 	return lastIndex
 }
 }
 
 
-func (r *regexpObject) updateLastIndex(index int64, firstResult, lastResult []int) bool {
-	if r.pattern.sticky {
-		if firstResult == nil || int64(firstResult[0]) != index {
-			r.setOwnStr("lastIndex", intToValue(0), true)
-			return false
-		}
-	} else {
-		if firstResult == nil {
-			if r.pattern.global {
-				r.setOwnStr("lastIndex", intToValue(0), true)
-			}
-			return false
-		}
-	}
-
-	if r.pattern.global || r.pattern.sticky {
-		r.setOwnStr("lastIndex", intToValue(int64(lastResult[1])), true)
-	}
-	return true
-}
-
 func (r *regexpObject) execRegexp(target String) (match bool, result []int) {
 func (r *regexpObject) execRegexp(target String) (match bool, result []int) {
 	index := r.getLastIndex()
 	index := r.getLastIndex()
 	if index >= 0 && index <= int64(target.Length()) {
 	if index >= 0 && index <= int64(target.Length()) {
 		result = r.pattern.findSubmatchIndex(target, int(index))
 		result = r.pattern.findSubmatchIndex(target, int(index))
 	}
 	}
-	match = r.updateLastIndex(index, result, result)
+	match = len(result) > 0 && (!r.pattern.sticky || int64(result[0]) == index)
+
+	if r.pattern.global || r.pattern.sticky {
+		var newLastIndex int64
+		if match {
+			newLastIndex = int64(result[1])
+		}
+		r.setOwnStr("lastIndex", intToValue(newLastIndex), true)
+	}
+
 	return
 	return
 }
 }
 
 

+ 25 - 0
regexp_test.go

@@ -754,6 +754,31 @@ func TestRegexpUnicodeEscape(t *testing.T) {
 	testScriptWithTestLib(SCRIPT, _undefined, t)
 	testScriptWithTestLib(SCRIPT, _undefined, t)
 }
 }
 
 
+func TestRegExpLastIndex(t *testing.T) {
+	const SCRIPT = `
+	function t(R, type) {
+		const re = new R("x+", "g");
+		re.exec("xxy");
+		assert.sameValue(re.lastIndex, 2, type+": exec");
+		"xxy".replace(re);
+		assert.sameValue(re.lastIndex, 0, type+": replace must reset lastIndex");
+		re.lastIndex = 1;
+		assert.sameValue("xxy".match(re)[0], "xx", type+": match");
+		assert.sameValue(re.lastIndex, 0, type+": match must reset lastIndex");
+	}
+
+	class CustomRegExp extends RegExp {
+		exec(s) {
+			return super.exec(s);
+		}
+	}
+
+	t(RegExp, "std");
+	t(CustomRegExp, "generic");
+	`
+	testScriptWithTestLib(SCRIPT, _undefined, t)
+}
+
 func BenchmarkRegexpSplitWithBackRef(b *testing.B) {
 func BenchmarkRegexpSplitWithBackRef(b *testing.B) {
 	const SCRIPT = `
 	const SCRIPT = `
 	"aaaaaaaaaaaaaaaaaaaaaaaaa++bbbbbbbbbbbbbbbbbbbbbb+-ccccccccccccccccccccccc".split(/([+-])\1/)
 	"aaaaaaaaaaaaaaaaaaaaaaaaa++bbbbbbbbbbbbbbbbbbbbbb+-ccccccccccccccccccccccc".split(/([+-])\1/)