Browse Source

Fixed data race on regexpPattern. Fixes #214.

Dmitry Panov 4 years ago
parent
commit
27b0a7dc4c
3 changed files with 46 additions and 1 deletions
  1. 31 0
      regexp.go
  2. 14 0
      regexp_test.go
  3. 1 1
      vm.go

+ 31 - 0
regexp.go

@@ -17,6 +17,7 @@ type regexp2MatchCache struct {
 	posMap []int
 }
 
+// Not goroutine-safe. Use regexp2Wrapper.clone()
 type regexp2Wrapper struct {
 	rx    *regexp2.Regexp
 	cache *regexp2MatchCache
@@ -56,6 +57,7 @@ func (rd *arrayRuneReader) ReadRune() (r rune, size int, err error) {
 	return
 }
 
+// Not goroutine-safe. Use regexpPattern.clone()
 type regexpPattern struct {
 	src string
 
@@ -165,6 +167,25 @@ func (p *regexpPattern) findAllSubmatchIndex(s valueString, start int, limit int
 	return p.regexp2Wrapper.findAllSubmatchIndex(s, start, limit, sticky, p.unicode)
 }
 
+// clone creates a copy of the regexpPattern which can be used concurrently.
+func (p *regexpPattern) clone() *regexpPattern {
+	ret := &regexpPattern{
+		src:        p.src,
+		global:     p.global,
+		ignoreCase: p.ignoreCase,
+		multiline:  p.multiline,
+		sticky:     p.sticky,
+		unicode:    p.unicode,
+	}
+	if p.regexpWrapper != nil {
+		ret.regexpWrapper = p.regexpWrapper.clone()
+	}
+	if p.regexp2Wrapper != nil {
+		ret.regexp2Wrapper = p.regexp2Wrapper.clone()
+	}
+	return ret
+}
+
 type regexpObject struct {
 	baseObject
 	pattern *regexpPattern
@@ -428,6 +449,12 @@ func (r *regexp2Wrapper) findAllSubmatchIndex(s valueString, start, limit int, s
 	}
 }
 
+func (r *regexp2Wrapper) clone() *regexp2Wrapper {
+	return &regexp2Wrapper{
+		rx: r.rx,
+	}
+}
+
 func (r *regexpWrapper) findAllSubmatchIndex(s string, limit int, sticky bool) (results [][]int) {
 	wrapped := (*regexp.Regexp)(r)
 	results = wrapped.FindAllStringSubmatchIndex(s, limit)
@@ -476,6 +503,10 @@ func (r *regexpWrapper) findSubmatchIndexUnicode(s unicodeString, fullUnicode bo
 	return wrapped.FindReaderSubmatchIndex(s.utf16Reader(0))
 }
 
+func (r *regexpWrapper) clone() *regexpWrapper {
+	return r
+}
+
 func (r *regexpObject) execResultToArray(target valueString, result []int) Value {
 	captureCount := len(result) >> 1
 	valueArray := make([]Value, captureCount)

+ 14 - 0
regexp_test.go

@@ -528,6 +528,20 @@ func TestRegexpInvalidUTF8(t *testing.T) {
 	}
 }
 
+// this should not cause data races when run with -race
+func TestRegexpConcurrentLiterals(t *testing.T) {
+	prg := MustCompile("test.js", `var r = /(?<!-)\d+/; r.test("");`, false)
+	go func() {
+		vm := New()
+		_, err := vm.RunProgram(prg)
+		if err != nil {
+			panic(err)
+		}
+	}()
+	vm := New()
+	_, _ = vm.RunProgram(prg)
+}
+
 func BenchmarkRegexpSplitWithBackRef(b *testing.B) {
 	const SCRIPT = `
 	"aaaaaaaaaaaaaaaaaaaaaaaaa++bbbbbbbbbbbbbbbbbbbbbb+-ccccccccccccccccccccccc".split(/([+-])\1/)

+ 1 - 1
vm.go

@@ -1278,7 +1278,7 @@ type newRegexp struct {
 }
 
 func (n *newRegexp) exec(vm *vm) {
-	vm.push(vm.r.newRegExpp(n.pattern, n.src, vm.r.global.RegExpPrototype))
+	vm.push(vm.r.newRegExpp(n.pattern.clone(), n.src, vm.r.global.RegExpPrototype))
 	vm.pc++
 }