Add runtime type assertions for the basic compound types

If we have a List, Map, Set or Ref with a non Value element type we
now check the type of the params to functions that "mutate" these.
This commit is contained in:
Erik Arvidsson
2015-10-30 10:43:07 -04:00
parent 62fad2d615
commit 89734aae9e
9 changed files with 114 additions and 34 deletions

31
types/assert.go Normal file
View File

@@ -0,0 +1,31 @@
package types
import "github.com/attic-labs/noms/d"
func assertType(t TypeRef, v ...Value) {
if t.Kind() != ValueKind {
for _, v := range v {
d.Chk.True(t.Equals(v.TypeRef()))
}
}
}
func assertSetsSameType(s Set, v ...Set) {
if s.elemType().Kind() != ValueKind {
t := s.TypeRef()
for _, v := range v {
d.Chk.True(t.Equals(v.TypeRef()))
}
}
}
func assertMapElemTypes(m Map, v ...Value) {
elemTypes := m.elemTypes()
keyType := elemTypes[0]
valueType := elemTypes[0]
if keyType.Kind() != ValueKind || valueType.Kind() != ValueKind {
for i, v := range v {
d.Chk.True(elemTypes[i%2].Equals(v.TypeRef()))
}
}
}

View File

@@ -125,6 +125,7 @@ func (l listLeaf) Slice(start uint64, end uint64) List {
}
func (l listLeaf) Set(idx uint64, v Value) List {
assertType(l.elemType(), v)
values := make([]Value, len(l.values))
copy(values, l.values)
values[idx] = v
@@ -132,11 +133,13 @@ func (l listLeaf) Set(idx uint64, v Value) List {
}
func (l listLeaf) Append(v ...Value) List {
assertType(l.elemType(), v...)
values := append(l.values, v...)
return newListLeafNoCopy(values, l.t)
}
func (l listLeaf) Insert(idx uint64, v ...Value) List {
assertType(l.elemType(), v...)
values := make([]Value, len(l.values)+len(v))
copy(values, l.values[:idx])
copy(values[idx:], v)
@@ -178,3 +181,7 @@ func (l listLeaf) Chunks() (chunks []ref.Ref) {
func (l listLeaf) TypeRef() TypeRef {
return l.t
}
func (l listLeaf) elemType() TypeRef {
return l.t.Desc.(CompoundDesc).ElemTypes[0]
}

View File

@@ -341,19 +341,24 @@ func TestListTypeRef(t *testing.T) {
tr := MakeCompoundTypeRef(ListKind, MakePrimitiveTypeRef(UInt8Kind))
l2 := newListLeafNoCopy([]Value{UInt8(0), UInt8(1)}, tr)
assert.Equal(tr, l2.TypeRef())
l2 = l2.Slice(0, 1)
assert.Equal(tr, l2.TypeRef())
l2 = l2.Set(0, UInt8(11))
assert.Equal(tr, l2.TypeRef())
l2 = l2.Append(UInt8(2))
assert.Equal(tr, l2.TypeRef())
l2 = l2.Insert(0, UInt8(3))
assert.Equal(tr, l2.TypeRef())
l2 = l2.Remove(0, 1)
assert.Equal(tr, l2.TypeRef())
l2 = l2.RemoveAt(0)
assert.Equal(tr, l2.TypeRef())
l3 := l2.Slice(0, 1)
assert.True(tr.Equals(l3.TypeRef()))
l3 = l2.Remove(0, 1)
assert.True(tr.Equals(l3.TypeRef()))
l3 = l2.RemoveAt(0)
assert.True(tr.Equals(l3.TypeRef()))
l3 = l2.Set(0, UInt8(11))
assert.True(tr.Equals(l3.TypeRef()))
l3 = l2.Append(UInt8(2))
assert.True(tr.Equals(l3.TypeRef()))
l3 = l2.Insert(0, UInt8(3))
assert.True(tr.Equals(l3.TypeRef()))
assert.Panics(func() { l2.Set(0, NewString("")) })
assert.Panics(func() { l2.Append(NewString("")) })
assert.Panics(func() { l2.Insert(0, NewString("")) })
}
func TestListChunks(t *testing.T) {

View File

@@ -60,10 +60,14 @@ func (m Map) MaybeGet(key Value) (v Value, ok bool) {
}
func (m Map) Set(key Value, val Value) Map {
elemTypes := m.t.Desc.(CompoundDesc).ElemTypes
assertType(elemTypes[0], key)
assertType(elemTypes[1], val)
return newMapFromData(buildMapData(m.data, []Value{key, val}), m.t)
}
func (m Map) SetM(kv ...Value) Map {
assertMapElemTypes(m, kv...)
return newMapFromData(buildMapData(m.data, kv), m.t)
}
@@ -132,6 +136,10 @@ func (m Map) TypeRef() TypeRef {
return m.t
}
func (m Map) elemTypes() []TypeRef {
return m.t.Desc.(CompoundDesc).ElemTypes
}
func init() {
RegisterFromValFunction(mapTypeRef, func(v Value) Value {
return v.(Map)

View File

@@ -220,23 +220,28 @@ func TestMapTypeRef(t *testing.T) {
m := NewMap()
assert.True(m.TypeRef().Equals(MakeCompoundTypeRef(MapKind, MakePrimitiveTypeRef(ValueKind), MakePrimitiveTypeRef(ValueKind))))
tr := MakeCompoundTypeRef(MapKind, MakePrimitiveTypeRef(StringKind), MakePrimitiveTypeRef(Int64Kind))
tr := MakeCompoundTypeRef(MapKind, MakePrimitiveTypeRef(StringKind), MakePrimitiveTypeRef(UInt64Kind))
m = newMapFromData(mapData{}, tr)
assert.Equal(tr, m.TypeRef())
m = m.Set(NewString("A"), UInt64(1))
assert.Equal(tr, m.TypeRef())
m = m.SetM(NewString("B"), UInt64(2), NewString("C"), UInt64(2))
assert.Equal(tr, m.TypeRef())
m = m.Remove(NewString("B"))
assert.Equal(tr, m.TypeRef())
m2 := m.Remove(NewString("B"))
assert.True(tr.Equals(m2.TypeRef()))
m = m.Filter(func(k, v Value) bool {
return true
})
assert.Equal(tr, m.TypeRef())
assert.True(tr.Equals(m2.TypeRef()))
m2 = m.Set(NewString("A"), UInt64(1))
assert.True(tr.Equals(m2.TypeRef()))
m2 = m.SetM(NewString("B"), UInt64(2), NewString("C"), UInt64(2))
assert.True(tr.Equals(m2.TypeRef()))
assert.Panics(func() { m.Set(NewString("A"), UInt8(1)) })
assert.Panics(func() { m.Set(Bool(true), UInt64(1)) })
assert.Panics(func() { m.SetM(NewString("B"), UInt64(2), NewString("A"), UInt8(1)) })
assert.Panics(func() { m.SetM(NewString("B"), UInt64(2), Bool(true), UInt64(1)) })
}
func TestMapChunks(t *testing.T) {

View File

@@ -52,5 +52,6 @@ func (r Ref) TargetValue(cs chunks.ChunkSource) Value {
}
func (r Ref) SetTargetValue(val Value, cs chunks.ChunkSink) Ref {
assertType(r.t.Desc.(CompoundDesc).ElemTypes[0], val)
return newRef(WriteValue(val, cs), r.t)
}

View File

@@ -51,4 +51,13 @@ func TestRefTypeRef(t *testing.T) {
m := NewMap()
r2 := r.SetTargetValue(m, cs)
assert.True(r2.TypeRef().Equals(tr))
b := Bool(true)
r2 = r.SetTargetValue(b, cs)
r2.t = MakeCompoundTypeRef(RefKind, b.TypeRef())
r3 := r2.SetTargetValue(Bool(false), cs)
assert.True(r2.TypeRef().Equals(r3.TypeRef()))
assert.Panics(func() { r2.SetTargetValue(Int16(1), cs) })
}

View File

@@ -32,6 +32,7 @@ func (s Set) Has(v Value) bool {
}
func (s Set) Insert(values ...Value) Set {
assertType(s.elemType(), values...)
return newSetFromData(buildSetData(s.data, values), s.t)
}
@@ -49,6 +50,7 @@ func (s Set) Remove(values ...Value) Set {
}
func (s Set) Union(others ...Set) Set {
assertSetsSameType(s, others...)
result := s
for _, other := range others {
other.Iter(func(v Value) (stop bool) {
@@ -129,6 +131,10 @@ func (s Set) TypeRef() TypeRef {
return s.t
}
func (s Set) elemType() TypeRef {
return s.t.Desc.(CompoundDesc).ElemTypes[0]
}
func init() {
RegisterFromValFunction(setTypeRef, func(v Value) Value {
return v.(Set)

View File

@@ -193,22 +193,30 @@ func TestSetTypeRef(t *testing.T) {
s = newSetFromData(setData{}, tr)
assert.Equal(tr, s.TypeRef())
s = s.Insert(UInt64(0), UInt64(1))
assert.Equal(tr, s.TypeRef())
s2 := s.Remove(UInt64(1))
assert.True(tr.Equals(s2.TypeRef()))
s = s.Remove(UInt64(1))
assert.Equal(tr, s.TypeRef())
s2 = s.Subtract(s)
assert.True(tr.Equals(s2.TypeRef()))
s = s.Union(s)
assert.Equal(tr, s.TypeRef())
s = s.Subtract(s)
assert.Equal(tr, s.TypeRef())
s = s.Filter(func(v Value) bool {
s2 = s.Filter(func(v Value) bool {
return true
})
assert.Equal(tr, s.TypeRef())
assert.True(tr.Equals(s2.TypeRef()))
s2 = s.Insert(UInt64(0), UInt64(1))
assert.True(tr.Equals(s2.TypeRef()))
s3 := NewSet(UInt64(2))
s3.t = s2.t
s2 = s.Union(s3)
assert.True(tr.Equals(s2.TypeRef()))
assert.Panics(func() { s.Insert(Bool(true)) })
assert.Panics(func() { s.Insert(UInt64(3), Bool(true)) })
assert.Panics(func() { s.Union(NewSet(UInt64(2))) })
assert.Panics(func() { s.Union(NewSet(Bool(true))) })
assert.Panics(func() { s.Union(s, NewSet(Bool(true))) })
}
func TestSetChunks(t *testing.T) {