Browse Source

Use a sequencer instead of pointer for object ids (because pointers are not guaranteed to remain the same).

Dmitry Panov 5 years ago
parent
commit
8d9f8c2670
7 changed files with 75 additions and 28 deletions
  1. 1 1
      builtin_map.go
  2. 1 1
      builtin_set.go
  3. 4 4
      builtin_weakmap.go
  4. 4 4
      builtin_weakset.go
  5. 24 9
      object.go
  6. 10 2
      runtime.go
  7. 31 7
      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.val.runtime.hash)
+	mo.m = newOrderedMap(mo.val.runtime.getHash())
 }
 }
 
 
 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.val.runtime.hash)
+	so.m = newOrderedMap(so.val.runtime.getHash())
 }
 }
 
 
 func (r *Runtime) setProto_add(call FunctionCall) Value {
 func (r *Runtime) setProto_add(call FunctionCall) Value {

+ 4 - 4
builtin_weakmap.go

@@ -6,7 +6,7 @@ type weakMap struct {
 	// need to synchronise access to the data map because it may be accessed
 	// need to synchronise access to the data map because it may be accessed
 	// from the finalizer goroutine
 	// from the finalizer goroutine
 	sync.Mutex
 	sync.Mutex
-	data map[uintptr]Value
+	data map[uint64]Value
 }
 }
 
 
 type weakMapObject struct {
 type weakMapObject struct {
@@ -16,7 +16,7 @@ type weakMapObject struct {
 
 
 func newWeakMap() *weakMap {
 func newWeakMap() *weakMap {
 	return &weakMap{
 	return &weakMap{
-		data: make(map[uintptr]Value),
+		data: make(map[uint64]Value),
 	}
 	}
 }
 }
 
 
@@ -25,9 +25,9 @@ func (wmo *weakMapObject) init() {
 	wmo.m = newWeakMap()
 	wmo.m = newWeakMap()
 }
 }
 
 
-func (wm *weakMap) removePtr(ptr uintptr) {
+func (wm *weakMap) removeId(id uint64) {
 	wm.Lock()
 	wm.Lock()
-	delete(wm.data, ptr)
+	delete(wm.data, id)
 	wm.Unlock()
 	wm.Unlock()
 }
 }
 
 

+ 4 - 4
builtin_weakset.go

@@ -6,7 +6,7 @@ type weakSet struct {
 	// need to synchronise access to the data map because it may be accessed
 	// need to synchronise access to the data map because it may be accessed
 	// from the finalizer goroutine
 	// from the finalizer goroutine
 	sync.Mutex
 	sync.Mutex
-	data map[uintptr]struct{}
+	data map[uint64]struct{}
 }
 }
 
 
 type weakSetObject struct {
 type weakSetObject struct {
@@ -16,7 +16,7 @@ type weakSetObject struct {
 
 
 func newWeakSet() *weakSet {
 func newWeakSet() *weakSet {
 	return &weakSet{
 	return &weakSet{
-		data: make(map[uintptr]struct{}),
+		data: make(map[uint64]struct{}),
 	}
 	}
 }
 }
 
 
@@ -25,9 +25,9 @@ func (ws *weakSetObject) init() {
 	ws.s = newWeakSet()
 	ws.s = newWeakSet()
 }
 }
 
 
-func (ws *weakSet) removePtr(ptr uintptr) {
+func (ws *weakSet) removeId(id uint64) {
 	ws.Lock()
 	ws.Lock()
-	delete(ws.data, ptr)
+	delete(ws.data, id)
 	ws.Unlock()
 	ws.Unlock()
 }
 }
 
 

+ 24 - 9
object.go

@@ -6,7 +6,6 @@ import (
 	"reflect"
 	"reflect"
 	"runtime"
 	"runtime"
 	"sort"
 	"sort"
-	"unsafe"
 
 
 	"github.com/dop251/goja/unistring"
 	"github.com/dop251/goja/unistring"
 )
 )
@@ -33,10 +32,11 @@ const (
 )
 )
 
 
 type weakCollection interface {
 type weakCollection interface {
-	removePtr(uintptr)
+	removeId(uint64)
 }
 }
 
 
 type weakCollections struct {
 type weakCollections struct {
+	objId uint64
 	colls []weakCollection
 	colls []weakCollection
 }
 }
 
 
@@ -49,8 +49,8 @@ func (r *weakCollections) add(c weakCollection) {
 	r.colls = append(r.colls, c)
 	r.colls = append(r.colls, c)
 }
 }
 
 
-func (r *weakCollections) id() uintptr {
-	return uintptr(unsafe.Pointer(r))
+func (r *weakCollections) id() uint64 {
+	return r.objId
 }
 }
 
 
 func (r *weakCollections) remove(c weakCollection) {
 func (r *weakCollections) remove(c weakCollection) {
@@ -79,12 +79,13 @@ func (r *weakCollections) remove(c weakCollection) {
 func finalizeObjectWeakRefs(r *weakCollections) {
 func finalizeObjectWeakRefs(r *weakCollections) {
 	id := r.id()
 	id := r.id()
 	for _, c := range r.colls {
 	for _, c := range r.colls {
-		c.removePtr(id)
+		c.removeId(id)
 	}
 	}
 	r.colls = nil
 	r.colls = nil
 }
 }
 
 
 type Object struct {
 type Object struct {
+	id      uint64
 	runtime *Runtime
 	runtime *Runtime
 	self    objectImpl
 	self    objectImpl
 
 
@@ -518,7 +519,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.val.runtime.hash)
+				o.symValues = newOrderedMap(nil)
 			}
 			}
 			o.symValues.set(name, val)
 			o.symValues.set(name, val)
 		}
 		}
@@ -760,7 +761,7 @@ func (o *baseObject) defineOwnPropertySym(s *valueSymbol, descr PropertyDescript
 	}
 	}
 	if v, ok := o._defineOwnProperty(s.desc.string(), existingVal, descr, throw); ok {
 	if v, ok := o._defineOwnProperty(s.desc.string(), existingVal, descr, throw); ok {
 		if o.symValues == nil {
 		if o.symValues == nil {
-			o.symValues = newOrderedMap(&o.val.runtime.hash)
+			o.symValues = newOrderedMap(nil)
 		}
 		}
 		o.symValues.set(s, v)
 		o.symValues.set(s, v)
 		return true
 		return true
@@ -796,7 +797,7 @@ func (o *baseObject) _putProp(name unistring.String, value Value, writable, enum
 
 
 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.val.runtime.hash)
+		o.symValues = newOrderedMap(nil)
 	}
 	}
 	o.symValues.set(s, prop)
 	o.symValues.set(s, prop)
 }
 }
@@ -1348,9 +1349,23 @@ func (o *Object) defineOwnProperty(n Value, desc PropertyDescriptor, throw bool)
 
 
 func (o *Object) getWeakCollRefs() *weakCollections {
 func (o *Object) getWeakCollRefs() *weakCollections {
 	if o.weakColls == nil {
 	if o.weakColls == nil {
-		o.weakColls = &weakCollections{}
+		o.weakColls = &weakCollections{
+			objId: o.getId(),
+		}
 		runtime.SetFinalizer(o.weakColls, finalizeObjectWeakRefs)
 		runtime.SetFinalizer(o.weakColls, finalizeObjectWeakRefs)
 	}
 	}
 
 
 	return o.weakColls
 	return o.weakColls
 }
 }
+
+func (o *Object) getId() uint64 {
+	for o.id == 0 {
+		if o.runtime.hash == nil {
+			h := o.runtime.getHash()
+			o.runtime.idSeq = h.Sum64()
+		}
+		o.id = o.runtime.idSeq
+		o.runtime.idSeq++
+	}
+	return o.id
+}

+ 10 - 2
runtime.go

@@ -164,8 +164,9 @@ type Runtime struct {
 	typeInfoCache   map[reflect.Type]*reflectTypeInfo
 	typeInfoCache   map[reflect.Type]*reflectTypeInfo
 	fieldNameMapper FieldNameMapper
 	fieldNameMapper FieldNameMapper
 
 
-	vm   *vm
-	hash maphash.Hash
+	vm    *vm
+	hash  *maphash.Hash
+	idSeq uint64
 }
 }
 
 
 type StackFrame struct {
 type StackFrame struct {
@@ -1921,6 +1922,13 @@ func (r *Runtime) newLazyObject(create func(*Object) objectImpl) *Object {
 	return val
 	return val
 }
 }
 
 
+func (r *Runtime) getHash() *maphash.Hash {
+	if r.hash == nil {
+		r.hash = &maphash.Hash{}
+	}
+	return r.hash
+}
+
 func nilSafe(v Value) Value {
 func nilSafe(v Value) Value {
 	if v != nil {
 	if v != nil {
 		return v
 		return v

+ 31 - 7
value.go

@@ -11,6 +11,22 @@ import (
 	"github.com/dop251/goja/unistring"
 	"github.com/dop251/goja/unistring"
 )
 )
 
 
+var (
+	// Not goroutine-safe, do not use for anything other than package level init
+	pkgHasher maphash.Hash
+
+	hashFalse = randomHash()
+	hashTrue  = randomHash()
+	hashNull  = randomHash()
+	hashUndef = randomHash()
+)
+
+// Not goroutine-safe, do not use for anything other than package level init
+func randomHash() uint64 {
+	pkgHasher.WriteByte(0)
+	return pkgHasher.Sum64()
+}
+
 var (
 var (
 	valueFalse    Value = valueBool(false)
 	valueFalse    Value = valueBool(false)
 	valueTrue     Value = valueBool(true)
 	valueTrue     Value = valueBool(true)
@@ -73,6 +89,7 @@ type valueUndefined struct {
 	valueNull
 	valueNull
 }
 }
 type valueSymbol struct {
 type valueSymbol struct {
+	h    uintptr
 	desc valueString
 	desc valueString
 }
 }
 
 
@@ -297,9 +314,10 @@ func (b valueBool) ExportType() reflect.Type {
 
 
 func (b valueBool) hash(*maphash.Hash) uint64 {
 func (b valueBool) hash(*maphash.Hash) uint64 {
 	if b {
 	if b {
-		return uint64(uintptr(unsafe.Pointer(&valueTrue)))
+		return hashTrue
 	}
 	}
-	return uint64(uintptr(unsafe.Pointer(&valueFalse)))
+
+	return hashFalse
 }
 }
 
 
 func (n valueNull) ToInteger() int64 {
 func (n valueNull) ToInteger() int64 {
@@ -357,7 +375,7 @@ func (u valueUndefined) ToFloat() float64 {
 }
 }
 
 
 func (u valueUndefined) hash(*maphash.Hash) uint64 {
 func (u valueUndefined) hash(*maphash.Hash) uint64 {
-	return uint64(uintptr(unsafe.Pointer(&_undefined)))
+	return hashUndef
 }
 }
 
 
 func (n valueNull) ToFloat() float64 {
 func (n valueNull) ToFloat() float64 {
@@ -409,7 +427,7 @@ func (n valueNull) ExportType() reflect.Type {
 }
 }
 
 
 func (n valueNull) hash(*maphash.Hash) uint64 {
 func (n valueNull) hash(*maphash.Hash) uint64 {
-	return uint64(uintptr(unsafe.Pointer(&_null)))
+	return hashNull
 }
 }
 
 
 func (p *valueProperty) ToInteger() int64 {
 func (p *valueProperty) ToInteger() int64 {
@@ -719,7 +737,7 @@ func (o *Object) ExportType() reflect.Type {
 }
 }
 
 
 func (o *Object) hash(*maphash.Hash) uint64 {
 func (o *Object) hash(*maphash.Hash) uint64 {
-	return uint64(uintptr(unsafe.Pointer(o)))
+	return o.getId()
 }
 }
 
 
 func (o *Object) Get(name string) Value {
 func (o *Object) Get(name string) Value {
@@ -938,13 +956,19 @@ func (s *valueSymbol) baseObject(r *Runtime) *Object {
 }
 }
 
 
 func (s *valueSymbol) hash(*maphash.Hash) uint64 {
 func (s *valueSymbol) hash(*maphash.Hash) uint64 {
-	return uint64(uintptr(unsafe.Pointer(s)))
+	return uint64(s.h)
 }
 }
 
 
 func newSymbol(s valueString) *valueSymbol {
 func newSymbol(s valueString) *valueSymbol {
-	return &valueSymbol{
+	r := &valueSymbol{
 		desc: asciiString("Symbol(").concat(s).concat(asciiString(")")),
 		desc: asciiString("Symbol(").concat(s).concat(asciiString(")")),
 	}
 	}
+	// This may need to be reconsidered in the future.
+	// Depending on changes in Go's allocation policy and/or introduction of a compacting GC
+	// this may no longer provide sufficient dispersion. The alternative, however, is a globally
+	// synchronised random generator/hasher/sequencer and I don't want to go down that route just yet.
+	r.h = uintptr(unsafe.Pointer(r))
+	return r
 }
 }
 
 
 func init() {
 func init() {