diff --git a/starlarkstruct/struct.go b/starlarkstruct/struct.go index 2282d7fb..44612d28 100644 --- a/starlarkstruct/struct.go +++ b/starlarkstruct/struct.go @@ -99,6 +99,7 @@ func FromStringDict(constructor starlark.Value, d starlark.StringDict) *Struct { type Struct struct { constructor starlark.Value entries entries // sorted by name + frozen bool } // Default is the default constructor for structs. @@ -172,8 +173,11 @@ func (s *Struct) Hash() (uint32, error) { return x, nil } func (s *Struct) Freeze() { - for _, e := range s.entries { - e.value.Freeze() + if !s.frozen { + s.frozen = true + for _, e := range s.entries { + e.value.Freeze() + } } } diff --git a/starlarkstruct/testdata/struct.star b/starlarkstruct/testdata/struct.star index e54fe046..993a84fb 100644 --- a/starlarkstruct/testdata/struct.star +++ b/starlarkstruct/testdata/struct.star @@ -61,3 +61,12 @@ assert.eq(alice + bob, person(age = 50, city = "NYC", name = "bob")) # not comm assert.fails(lambda : alice + 1, "struct \\+ int") assert.eq(http + http, http) assert.fails(lambda : http + bob, "different constructors: hostport \\+ person") + +# Check that a struct with a circular reference doesn't crash when it gets frozen. +def struct_with_a_circular_reference(): + foo = lambda: print(self) + self = struct(foo = foo) + return self + +# A top-level assigment is what causes this value to be frozen at the end of the load. +toplevel_struct_with_a_circular_reference = struct_with_a_circular_reference()