Skip to content

Commit

Permalink
Fixed data race on regexpPattern. Fixes dop251#214.
Browse files Browse the repository at this point in the history
  • Loading branch information
dop251 authored and tsedgwick committed Apr 14, 2021
1 parent 6f01e6f commit 60aefdb
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 1 deletion.
31 changes: 31 additions & 0 deletions regexp.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type regexp2MatchCache struct {
posMap []int
}

// Not goroutine-safe. Use regexp2Wrapper.clone()
type regexp2Wrapper struct {
rx *regexp2.Regexp
cache *regexp2MatchCache
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
14 changes: 14 additions & 0 deletions regexp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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/)
Expand Down
2 changes: 1 addition & 1 deletion vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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++
}

Expand Down

0 comments on commit 60aefdb

Please sign in to comment.