lang: funcs: facts: Do not reuse fact pointers

In my carelessness, I was re-using pointers when a fact was used twice!
This could cause disastrous consequences like a double close panic on a
datetime.now() fact for example.

In other news, we should consider if it's possible to get more clever
about graph shape optimization so that we don't need more than once
instance of certain functions like datetime.now() in our graph.
This commit is contained in:
James Shubin
2025-04-22 05:17:46 -04:00
parent 37bb67dffd
commit 16d3e3063c

View File

@@ -51,18 +51,18 @@ const (
ErrCantSpeculate = funcs.ErrCantSpeculate ErrCantSpeculate = funcs.ErrCantSpeculate
) )
// RegisteredFacts is a global map of all possible facts which can be used. You // registeredFacts is a global map of all possible facts which can be used. You
// should never touch this map directly. Use methods like Register instead. // should never touch this map directly. Use methods like Register instead.
var RegisteredFacts = make(map[string]func() Fact) // must initialize var registeredFacts = make(map[string]struct{}) // must initialize
// Register takes a fact and its name and makes it available for use. It is // Register takes a fact and its name and makes it available for use. It is
// commonly called in the init() method of the fact at program startup. There is // commonly called in the init() method of the fact at program startup. There is
// no matching Unregister function. // no matching Unregister function.
func Register(name string, fn func() Fact) { func Register(name string, fn func() Fact) {
if _, ok := RegisteredFacts[name]; ok { if _, ok := registeredFacts[name]; ok {
panic(fmt.Sprintf("a fact named %s is already registered", name)) panic(fmt.Sprintf("a fact named %s is already registered", name))
} }
f := fn() f := fn() // don't wrap this more than once!
metadata, err := funcs.GetFunctionMetadata(f) metadata, err := funcs.GetFunctionMetadata(f)
if err != nil { if err != nil {
@@ -72,12 +72,12 @@ func Register(name string, fn func() Fact) {
//gob.Register(fn()) //gob.Register(fn())
funcs.Register(name, func() interfaces.Func { // implement in terms of func interface funcs.Register(name, func() interfaces.Func { // implement in terms of func interface
return &FactFunc{ return &FactFunc{
Fact: f, Fact: fn(), // this MUST be a fresh/unique pointer!
Metadata: metadata, Metadata: metadata,
} }
}) })
RegisteredFacts[name] = fn registeredFacts[name] = struct{}{}
} }
// ModuleRegister is exactly like Register, except that it registers within a // ModuleRegister is exactly like Register, except that it registers within a