Browse Source

Fixed concurrent use of hashmap.Hash

Dmitry Panov 5 years ago
parent
commit
47fce32384
9 changed files with 53 additions and 47 deletions
  1. 1 1
      builtin_map.go
  2. 1 1
      builtin_set.go
  3. 14 10
      map.go
  4. 10 8
      map_test.go
  5. 3 3
      object.go
  6. 3 1
      runtime.go
  7. 5 4
      string_ascii.go
  8. 5 4
      string_unicode.go
  9. 11 15
      value.go

+ 1 - 1
builtin_map.go

@@ -37,7 +37,7 @@ func (o *mapIterObject) next() Value {
 
 
 func (mo *mapObject) init() {
 func (mo *mapObject) init() {
 	mo.baseObject.init()
 	mo.baseObject.init()
-	mo.m = newOrderedMap()
+	mo.m = newOrderedMap(&mo.val.runtime.hash)
 }
 }
 
 
 func (r *Runtime) mapProto_clear(call FunctionCall) Value {
 func (r *Runtime) mapProto_clear(call FunctionCall) Value {

+ 1 - 1
builtin_set.go

@@ -35,7 +35,7 @@ func (o *setIterObject) next() Value {
 
 
 func (so *setObject) init() {
 func (so *setObject) init() {
 	so.baseObject.init()
 	so.baseObject.init()
-	so.m = newOrderedMap()
+	so.m = newOrderedMap(&so.val.runtime.hash)
 }
 }
 
 
 func (r *Runtime) setProto_add(call FunctionCall) Value {
 func (r *Runtime) setProto_add(call FunctionCall) Value {

+ 14 - 10
map.go

@@ -1,5 +1,7 @@
 package goja
 package goja
 
 
+import "hash"
+
 type mapEntry struct {
 type mapEntry struct {
 	key, value Value
 	key, value Value
 
 
@@ -8,7 +10,8 @@ type mapEntry struct {
 }
 }
 
 
 type orderedMap struct {
 type orderedMap struct {
-	hash                map[uint64]*mapEntry
+	hash                hash.Hash64
+	hashTable           map[uint64]*mapEntry
 	iterFirst, iterLast *mapEntry
 	iterFirst, iterLast *mapEntry
 	size                int
 	size                int
 }
 }
@@ -22,8 +25,8 @@ func (m *orderedMap) lookup(key Value) (h uint64, entry, hPrev *mapEntry) {
 	if key == _negativeZero {
 	if key == _negativeZero {
 		key = intToValue(0)
 		key = intToValue(0)
 	}
 	}
-	h = key.hash()
-	for entry = m.hash[h]; entry != nil && !entry.key.SameAs(key); hPrev, entry = entry, entry.hNext {
+	h = key.hash(m.hash)
+	for entry = m.hashTable[h]; entry != nil && !entry.key.SameAs(key); hPrev, entry = entry, entry.hNext {
 	}
 	}
 	return
 	return
 }
 }
@@ -38,7 +41,7 @@ func (m *orderedMap) set(key, value Value) {
 		}
 		}
 		entry = &mapEntry{key: key, value: value}
 		entry = &mapEntry{key: key, value: value}
 		if hPrev == nil {
 		if hPrev == nil {
-			m.hash[h] = entry
+			m.hashTable[h] = entry
 		} else {
 		} else {
 			hPrev.hNext = entry
 			hPrev.hNext = entry
 		}
 		}
@@ -80,12 +83,12 @@ func (m *orderedMap) remove(key Value) bool {
 			m.iterLast = entry.iterPrev
 			m.iterLast = entry.iterPrev
 		}
 		}
 
 
-		// remove from the hash
+		// remove from the hashTable
 		if hPrev == nil {
 		if hPrev == nil {
 			if entry.hNext == nil {
 			if entry.hNext == nil {
-				delete(m.hash, h)
+				delete(m.hashTable, h)
 			} else {
 			} else {
-				m.hash[h] = entry.hNext
+				m.hashTable[h] = entry.hNext
 			}
 			}
 		} else {
 		} else {
 			hPrev.hNext = entry.hNext
 			hPrev.hNext = entry.hNext
@@ -135,9 +138,10 @@ func (iter *orderedMapIter) close() {
 	iter.cur = nil
 	iter.cur = nil
 }
 }
 
 
-func newOrderedMap() *orderedMap {
+func newOrderedMap(h hash.Hash64) *orderedMap {
 	return &orderedMap{
 	return &orderedMap{
-		hash: make(map[uint64]*mapEntry),
+		hash:      h,
+		hashTable: make(map[uint64]*mapEntry),
 	}
 	}
 }
 }
 
 
@@ -158,6 +162,6 @@ func (m *orderedMap) clear() {
 	}
 	}
 	m.iterFirst = nil
 	m.iterFirst = nil
 	m.iterLast = nil
 	m.iterLast = nil
-	m.hash = make(map[uint64]*mapEntry)
+	m.hashTable = make(map[uint64]*mapEntry)
 	m.size = 0
 	m.size = 0
 }
 }

+ 10 - 8
map_test.go

@@ -1,13 +1,15 @@
 package goja
 package goja
 
 
 import (
 import (
+	"hash/maphash"
 	"math"
 	"math"
 	"strconv"
 	"strconv"
 	"testing"
 	"testing"
 )
 )
 
 
 func testMapHashVal(v1, v2 Value, expected bool, t *testing.T) {
 func testMapHashVal(v1, v2 Value, expected bool, t *testing.T) {
-	actual := v1.hash() == v2.hash()
+	var h maphash.Hash
+	actual := v1.hash(&h) == v2.hash(&h)
 	if actual != expected {
 	if actual != expected {
 		t.Fatalf("testMapHashVal failed for %v, %v", v1, v2)
 		t.Fatalf("testMapHashVal failed for %v, %v", v1, v2)
 	}
 	}
@@ -31,7 +33,7 @@ func TestMapHash(t *testing.T) {
 }
 }
 
 
 func TestOrderedMap(t *testing.T) {
 func TestOrderedMap(t *testing.T) {
-	m := newOrderedMap()
+	m := newOrderedMap(&maphash.Hash{})
 	for i := int64(0); i < 50; i++ {
 	for i := int64(0); i < 50; i++ {
 		m.set(intToValue(i), asciiString(strconv.FormatInt(i, 10)))
 		m.set(intToValue(i), asciiString(strconv.FormatInt(i, 10)))
 	}
 	}
@@ -77,14 +79,14 @@ func TestOrderedMap(t *testing.T) {
 }
 }
 
 
 func TestOrderedMapCollision(t *testing.T) {
 func TestOrderedMapCollision(t *testing.T) {
-	m := newOrderedMap()
+	m := newOrderedMap(&maphash.Hash{})
 	n1 := uint64(123456789)
 	n1 := uint64(123456789)
 	n2 := math.Float64frombits(n1)
 	n2 := math.Float64frombits(n1)
 	n1Key := intToValue(int64(n1))
 	n1Key := intToValue(int64(n1))
 	n2Key := floatToValue(n2)
 	n2Key := floatToValue(n2)
 	m.set(n1Key, asciiString("n1"))
 	m.set(n1Key, asciiString("n1"))
 	m.set(n2Key, asciiString("n2"))
 	m.set(n2Key, asciiString("n2"))
-	if m.size == len(m.hash) {
+	if m.size == len(m.hashTable) {
 		t.Fatal("Expected a collision but there wasn't one")
 		t.Fatal("Expected a collision but there wasn't one")
 	}
 	}
 	if n2Val := m.get(n2Key); !asciiString("n2").SameAs(n2Val) {
 	if n2Val := m.get(n2Key); !asciiString("n2").SameAs(n2Val) {
@@ -103,7 +105,7 @@ func TestOrderedMapCollision(t *testing.T) {
 }
 }
 
 
 func TestOrderedMapIter(t *testing.T) {
 func TestOrderedMapIter(t *testing.T) {
-	m := newOrderedMap()
+	m := newOrderedMap(&maphash.Hash{})
 	iter := m.newIter()
 	iter := m.newIter()
 	ent := iter.next()
 	ent := iter.next()
 	if ent != nil {
 	if ent != nil {
@@ -128,7 +130,7 @@ func TestOrderedMapIter(t *testing.T) {
 }
 }
 
 
 func TestOrderedMapIterVisitAfterReAdd(t *testing.T) {
 func TestOrderedMapIterVisitAfterReAdd(t *testing.T) {
-	m := newOrderedMap()
+	m := newOrderedMap(&maphash.Hash{})
 	one := intToValue(1)
 	one := intToValue(1)
 	two := intToValue(2)
 	two := intToValue(2)
 
 
@@ -157,7 +159,7 @@ func TestOrderedMapIterVisitAfterReAdd(t *testing.T) {
 }
 }
 
 
 func TestOrderedMapIterAddAfterClear(t *testing.T) {
 func TestOrderedMapIterAddAfterClear(t *testing.T) {
-	m := newOrderedMap()
+	m := newOrderedMap(&maphash.Hash{})
 	one := intToValue(1)
 	one := intToValue(1)
 	m.set(one, valueTrue)
 	m.set(one, valueTrue)
 	iter := m.newIter()
 	iter := m.newIter()
@@ -178,7 +180,7 @@ func TestOrderedMapIterAddAfterClear(t *testing.T) {
 }
 }
 
 
 func TestOrderedMapIterDeleteCurrent(t *testing.T) {
 func TestOrderedMapIterDeleteCurrent(t *testing.T) {
-	m := newOrderedMap()
+	m := newOrderedMap(&maphash.Hash{})
 	one := intToValue(1)
 	one := intToValue(1)
 	two := intToValue(2)
 	two := intToValue(2)
 	iter := m.newIter()
 	iter := m.newIter()

+ 3 - 3
object.go

@@ -527,7 +527,7 @@ func (o *baseObject) setOwnSym(name *valueSymbol, val Value, throw bool) bool {
 			return false
 			return false
 		} else {
 		} else {
 			if o.symValues == nil {
 			if o.symValues == nil {
-				o.symValues = newOrderedMap()
+				o.symValues = newOrderedMap(&o.val.runtime.hash)
 			}
 			}
 			o.symValues.set(name, val)
 			o.symValues.set(name, val)
 		}
 		}
@@ -769,7 +769,7 @@ func (o *baseObject) defineOwnPropertySym(s *valueSymbol, descr PropertyDescript
 	}
 	}
 	if v, ok := o._defineOwnProperty(s.String(), existingVal, descr, throw); ok {
 	if v, ok := o._defineOwnProperty(s.String(), existingVal, descr, throw); ok {
 		if o.symValues == nil {
 		if o.symValues == nil {
-			o.symValues = newOrderedMap()
+			o.symValues = newOrderedMap(&o.val.runtime.hash)
 		}
 		}
 		o.symValues.set(s, v)
 		o.symValues.set(s, v)
 		return true
 		return true
@@ -805,7 +805,7 @@ func (o *baseObject) _putProp(name string, value Value, writable, enumerable, co
 
 
 func (o *baseObject) _putSym(s *valueSymbol, prop Value) {
 func (o *baseObject) _putSym(s *valueSymbol, prop Value) {
 	if o.symValues == nil {
 	if o.symValues == nil {
-		o.symValues = newOrderedMap()
+		o.symValues = newOrderedMap(&o.val.runtime.hash)
 	}
 	}
 	o.symValues.set(s, prop)
 	o.symValues.set(s, prop)
 }
 }

+ 3 - 1
runtime.go

@@ -5,6 +5,7 @@ import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"go/ast"
 	"go/ast"
+	"hash/maphash"
 	"math"
 	"math"
 	"math/rand"
 	"math/rand"
 	"reflect"
 	"reflect"
@@ -146,7 +147,8 @@ type Runtime struct {
 	typeInfoCache   map[reflect.Type]*reflectTypeInfo
 	typeInfoCache   map[reflect.Type]*reflectTypeInfo
 	fieldNameMapper FieldNameMapper
 	fieldNameMapper FieldNameMapper
 
 
-	vm *vm
+	vm   *vm
+	hash maphash.Hash
 }
 }
 
 
 type StackFrame struct {
 type StackFrame struct {

+ 5 - 4
string_ascii.go

@@ -2,6 +2,7 @@ package goja
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"hash"
 	"io"
 	"io"
 	"math"
 	"math"
 	"reflect"
 	"reflect"
@@ -217,10 +218,10 @@ func (s asciiString) baseObject(r *Runtime) *Object {
 	return ss.val
 	return ss.val
 }
 }
 
 
-func (s asciiString) hash() uint64 {
-	_, _ = mapHasher.WriteString(string(s))
-	h := mapHasher.Sum64()
-	mapHasher.Reset()
+func (s asciiString) hash(hash hash.Hash64) uint64 {
+	_, _ = hash.Write([]byte(s))
+	h := hash.Sum64()
+	hash.Reset()
 	return h
 	return h
 }
 }
 
 

+ 5 - 4
string_unicode.go

@@ -6,6 +6,7 @@ import (
 	"github.com/dop251/goja/parser"
 	"github.com/dop251/goja/parser"
 	"golang.org/x/text/cases"
 	"golang.org/x/text/cases"
 	"golang.org/x/text/language"
 	"golang.org/x/text/language"
+	"hash"
 	"io"
 	"io"
 	"math"
 	"math"
 	"reflect"
 	"reflect"
@@ -299,9 +300,9 @@ func (s unicodeString) ExportType() reflect.Type {
 	return reflectTypeString
 	return reflectTypeString
 }
 }
 
 
-func (s unicodeString) hash() uint64 {
-	_, _ = mapHasher.Write(*(*[]byte)(unsafe.Pointer(&s)))
-	h := mapHasher.Sum64()
-	mapHasher.Reset()
+func (s unicodeString) hash(hash hash.Hash64) uint64 {
+	_, _ = hash.Write(*(*[]byte)(unsafe.Pointer(&s)))
+	h := hash.Sum64()
+	hash.Reset()
 	return h
 	return h
 }
 }

+ 11 - 15
value.go

@@ -2,7 +2,7 @@ package goja
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"hash/maphash"
+	"hash"
 	"math"
 	"math"
 	"reflect"
 	"reflect"
 	"regexp"
 	"regexp"
@@ -33,10 +33,6 @@ var (
 	reflectTypeString = reflect.TypeOf("")
 	reflectTypeString = reflect.TypeOf("")
 )
 )
 
 
-var (
-	mapHasher maphash.Hash
-)
-
 var intCache [256]Value
 var intCache [256]Value
 
 
 type Value interface {
 type Value interface {
@@ -56,7 +52,7 @@ type Value interface {
 
 
 	baseObject(r *Runtime) *Object
 	baseObject(r *Runtime) *Object
 
 
-	hash() uint64
+	hash(hash64 hash.Hash64) uint64
 }
 }
 
 
 type typeError string
 type typeError string
@@ -193,7 +189,7 @@ func (i valueInt) ExportType() reflect.Type {
 	return reflectTypeInt
 	return reflectTypeInt
 }
 }
 
 
-func (i valueInt) hash() uint64 {
+func (i valueInt) hash(hash.Hash64) uint64 {
 	return uint64(i)
 	return uint64(i)
 }
 }
 
 
@@ -283,7 +279,7 @@ func (o valueBool) ExportType() reflect.Type {
 	return reflectTypeBool
 	return reflectTypeBool
 }
 }
 
 
-func (b valueBool) hash() uint64 {
+func (b valueBool) hash(hash.Hash64) uint64 {
 	if b {
 	if b {
 		return uint64(uintptr(unsafe.Pointer(&valueTrue)))
 		return uint64(uintptr(unsafe.Pointer(&valueTrue)))
 	}
 	}
@@ -336,7 +332,7 @@ func (u valueUndefined) ToFloat() float64 {
 	return math.NaN()
 	return math.NaN()
 }
 }
 
 
-func (u valueUndefined) hash() uint64 {
+func (u valueUndefined) hash(hash.Hash64) uint64 {
 	return uint64(uintptr(unsafe.Pointer(&_undefined)))
 	return uint64(uintptr(unsafe.Pointer(&_undefined)))
 }
 }
 
 
@@ -388,7 +384,7 @@ func (n valueNull) ExportType() reflect.Type {
 	return reflectTypeNil
 	return reflectTypeNil
 }
 }
 
 
-func (n valueNull) hash() uint64 {
+func (n valueNull) hash(hash.Hash64) uint64 {
 	return uint64(uintptr(unsafe.Pointer(&_null)))
 	return uint64(uintptr(unsafe.Pointer(&_null)))
 }
 }
 
 
@@ -481,7 +477,7 @@ func (n *valueProperty) ExportType() reflect.Type {
 	panic("Cannot export valueProperty")
 	panic("Cannot export valueProperty")
 }
 }
 
 
-func (n *valueProperty) hash() uint64 {
+func (n *valueProperty) hash(hash.Hash64) uint64 {
 	panic("valueProperty should never be used in maps or sets")
 	panic("valueProperty should never be used in maps or sets")
 }
 }
 
 
@@ -606,7 +602,7 @@ func (f valueFloat) ExportType() reflect.Type {
 	return reflectTypeFloat
 	return reflectTypeFloat
 }
 }
 
 
-func (f valueFloat) hash() uint64 {
+func (f valueFloat) hash(hash.Hash64) uint64 {
 	if f == _negativeZero {
 	if f == _negativeZero {
 		return 0
 		return 0
 	}
 	}
@@ -686,7 +682,7 @@ func (o *Object) ExportType() reflect.Type {
 	return o.self.exportType()
 	return o.self.exportType()
 }
 }
 
 
-func (o *Object) hash() uint64 {
+func (o *Object) hash(hash.Hash64) uint64 {
 	return uint64(uintptr(unsafe.Pointer(o)))
 	return uint64(uintptr(unsafe.Pointer(o)))
 }
 }
 
 
@@ -832,7 +828,7 @@ func (o valueUnresolved) ExportType() reflect.Type {
 	return nil
 	return nil
 }
 }
 
 
-func (o valueUnresolved) hash() uint64 {
+func (o valueUnresolved) hash(hash.Hash64) uint64 {
 	o.throw()
 	o.throw()
 	return 0
 	return 0
 }
 }
@@ -896,7 +892,7 @@ func (s *valueSymbol) baseObject(r *Runtime) *Object {
 	return r.newPrimitiveObject(s, r.global.SymbolPrototype, "Symbol")
 	return r.newPrimitiveObject(s, r.global.SymbolPrototype, "Symbol")
 }
 }
 
 
-func (s *valueSymbol) hash() uint64 {
+func (s *valueSymbol) hash(hash.Hash64) uint64 {
 	return uint64(uintptr(unsafe.Pointer(s)))
 	return uint64(uintptr(unsafe.Pointer(s)))
 }
 }