Skip to content

Commit

Permalink
Use a sequencer instead of pointer for object ids (because pointers a…
Browse files Browse the repository at this point in the history
…re not guaranteed to remain the same).
  • Loading branch information
dop251 committed Apr 11, 2020
1 parent 5df89c3 commit 8d9f8c2
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 28 deletions.
2 changes: 1 addition & 1 deletion builtin_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (o *mapIterObject) next() Value {

func (mo *mapObject) 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 {
Expand Down
2 changes: 1 addition & 1 deletion builtin_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func (o *setIterObject) next() Value {

func (so *setObject) 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 {
Expand Down
8 changes: 4 additions & 4 deletions builtin_weakmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type weakMap struct {
// need to synchronise access to the data map because it may be accessed
// from the finalizer goroutine
sync.Mutex
data map[uintptr]Value
data map[uint64]Value
}

type weakMapObject struct {
Expand All @@ -16,7 +16,7 @@ type weakMapObject struct {

func newWeakMap() *weakMap {
return &weakMap{
data: make(map[uintptr]Value),
data: make(map[uint64]Value),
}
}

Expand All @@ -25,9 +25,9 @@ func (wmo *weakMapObject) init() {
wmo.m = newWeakMap()
}

func (wm *weakMap) removePtr(ptr uintptr) {
func (wm *weakMap) removeId(id uint64) {
wm.Lock()
delete(wm.data, ptr)
delete(wm.data, id)
wm.Unlock()
}

Expand Down
8 changes: 4 additions & 4 deletions builtin_weakset.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type weakSet struct {
// need to synchronise access to the data map because it may be accessed
// from the finalizer goroutine
sync.Mutex
data map[uintptr]struct{}
data map[uint64]struct{}
}

type weakSetObject struct {
Expand All @@ -16,7 +16,7 @@ type weakSetObject struct {

func newWeakSet() *weakSet {
return &weakSet{
data: make(map[uintptr]struct{}),
data: make(map[uint64]struct{}),
}
}

Expand All @@ -25,9 +25,9 @@ func (ws *weakSetObject) init() {
ws.s = newWeakSet()
}

func (ws *weakSet) removePtr(ptr uintptr) {
func (ws *weakSet) removeId(id uint64) {
ws.Lock()
delete(ws.data, ptr)
delete(ws.data, id)
ws.Unlock()
}

Expand Down
33 changes: 24 additions & 9 deletions object.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"reflect"
"runtime"
"sort"
"unsafe"

"github.com/dop251/goja/unistring"
)
Expand All @@ -33,10 +32,11 @@ const (
)

type weakCollection interface {
removePtr(uintptr)
removeId(uint64)
}

type weakCollections struct {
objId uint64
colls []weakCollection
}

Expand All @@ -49,8 +49,8 @@ func (r *weakCollections) add(c weakCollection) {
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) {
Expand Down Expand Up @@ -79,12 +79,13 @@ func (r *weakCollections) remove(c weakCollection) {
func finalizeObjectWeakRefs(r *weakCollections) {
id := r.id()
for _, c := range r.colls {
c.removePtr(id)
c.removeId(id)
}
r.colls = nil
}

type Object struct {
id uint64
runtime *Runtime
self objectImpl

Expand Down Expand Up @@ -518,7 +519,7 @@ func (o *baseObject) setOwnSym(name *valueSymbol, val Value, throw bool) bool {
return false
} else {
if o.symValues == nil {
o.symValues = newOrderedMap(&o.val.runtime.hash)
o.symValues = newOrderedMap(nil)
}
o.symValues.set(name, val)
}
Expand Down Expand Up @@ -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 o.symValues == nil {
o.symValues = newOrderedMap(&o.val.runtime.hash)
o.symValues = newOrderedMap(nil)
}
o.symValues.set(s, v)
return true
Expand Down Expand Up @@ -796,7 +797,7 @@ func (o *baseObject) _putProp(name unistring.String, value Value, writable, enum

func (o *baseObject) _putSym(s *valueSymbol, prop Value) {
if o.symValues == nil {
o.symValues = newOrderedMap(&o.val.runtime.hash)
o.symValues = newOrderedMap(nil)
}
o.symValues.set(s, prop)
}
Expand Down Expand Up @@ -1348,9 +1349,23 @@ func (o *Object) defineOwnProperty(n Value, desc PropertyDescriptor, throw bool)

func (o *Object) getWeakCollRefs() *weakCollections {
if o.weakColls == nil {
o.weakColls = &weakCollections{}
o.weakColls = &weakCollections{
objId: o.getId(),
}
runtime.SetFinalizer(o.weakColls, finalizeObjectWeakRefs)
}

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
}
12 changes: 10 additions & 2 deletions runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,9 @@ type Runtime struct {
typeInfoCache map[reflect.Type]*reflectTypeInfo
fieldNameMapper FieldNameMapper

vm *vm
hash maphash.Hash
vm *vm
hash *maphash.Hash
idSeq uint64
}

type StackFrame struct {
Expand Down Expand Up @@ -1921,6 +1922,13 @@ func (r *Runtime) newLazyObject(create func(*Object) objectImpl) *Object {
return val
}

func (r *Runtime) getHash() *maphash.Hash {
if r.hash == nil {
r.hash = &maphash.Hash{}
}
return r.hash
}

func nilSafe(v Value) Value {
if v != nil {
return v
Expand Down
38 changes: 31 additions & 7 deletions value.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,22 @@ import (
"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 (
valueFalse Value = valueBool(false)
valueTrue Value = valueBool(true)
Expand Down Expand Up @@ -73,6 +89,7 @@ type valueUndefined struct {
valueNull
}
type valueSymbol struct {
h uintptr
desc valueString
}

Expand Down Expand Up @@ -297,9 +314,10 @@ func (b valueBool) ExportType() reflect.Type {

func (b valueBool) hash(*maphash.Hash) uint64 {
if b {
return uint64(uintptr(unsafe.Pointer(&valueTrue)))
return hashTrue
}
return uint64(uintptr(unsafe.Pointer(&valueFalse)))

return hashFalse
}

func (n valueNull) ToInteger() int64 {
Expand Down Expand Up @@ -357,7 +375,7 @@ func (u valueUndefined) ToFloat() float64 {
}

func (u valueUndefined) hash(*maphash.Hash) uint64 {
return uint64(uintptr(unsafe.Pointer(&_undefined)))
return hashUndef
}

func (n valueNull) ToFloat() float64 {
Expand Down Expand Up @@ -409,7 +427,7 @@ func (n valueNull) ExportType() reflect.Type {
}

func (n valueNull) hash(*maphash.Hash) uint64 {
return uint64(uintptr(unsafe.Pointer(&_null)))
return hashNull
}

func (p *valueProperty) ToInteger() int64 {
Expand Down Expand Up @@ -719,7 +737,7 @@ func (o *Object) ExportType() reflect.Type {
}

func (o *Object) hash(*maphash.Hash) uint64 {
return uint64(uintptr(unsafe.Pointer(o)))
return o.getId()
}

func (o *Object) Get(name string) Value {
Expand Down Expand Up @@ -938,13 +956,19 @@ func (s *valueSymbol) baseObject(r *Runtime) *Object {
}

func (s *valueSymbol) hash(*maphash.Hash) uint64 {
return uint64(uintptr(unsafe.Pointer(s)))
return uint64(s.h)
}

func newSymbol(s valueString) *valueSymbol {
return &valueSymbol{
r := &valueSymbol{
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() {
Expand Down

0 comments on commit 8d9f8c2

Please sign in to comment.