diff --git a/eval/eval.go b/eval/eval.go index 19262040..446995a6 100644 --- a/eval/eval.go +++ b/eval/eval.go @@ -577,6 +577,8 @@ func (s *State) applyFunction(name string, fn object.Object, args []object.Objec } if after != before { log.Debugf("Cache miss for %s %v, %d get misses", function.CacheKey, args, after-before) + // A miss here is a miss upstack + s.env.TriggerNoCache() return res } // Don't cache errors, as it could be due to binding for instance. diff --git a/eval/eval_test.go b/eval/eval_test.go index 93af24ea..95d3f283 100644 --- a/eval/eval_test.go +++ b/eval/eval_test.go @@ -1009,3 +1009,16 @@ func TestSelfRef(t *testing.T) { t.Errorf("wrong result, got %q", res.Inspect()) } } + +func TestAliasTwice(t *testing.T) { + inp := `a=1; b=2;()=>{a=b}();b=5;()=>{a=b}()` // should not crash + s := eval.NewState() + res, err := eval.EvalString(s, inp, false) + if err != nil { + t.Errorf("should not have errored: %v", err) + } + expected := "5" + if res.Inspect() != expected { + t.Errorf("wrong result, got %q", res.Inspect()) + } +} diff --git a/examples/random.gr b/examples/random.gr new file mode 100644 index 00000000..e872d84e --- /dev/null +++ b/examples/random.gr @@ -0,0 +1,14 @@ +// Really this checks we don't cache functions that call non cacheable functions. + +func deepRand() { + () => { + rand() + }() +} + +a1 = deepRand(); log("a1", a1) +a2 = deepRand(); log("a2", a2) + +if a1 == a2 { + error("a1 and a2 should be different") +} diff --git a/main_test.txtar b/main_test.txtar index 930fae60..f6d7347e 100644 --- a/main_test.txtar +++ b/main_test.txtar @@ -301,6 +301,10 @@ stdout '^42$' grol -quiet -c 'a=-3; b=5; ()=>{b=a;a=b;b++;a++}();[a,b]' stdout '^\[-2,-2\]$' +# 2nd aliasing crashes +grol -quiet -c 'a=1; b=2;()=>{a=b}();()=>{a=b;println(type(a),type(b))}()' +stdout '&a.\(INTEGER\) &b.\(INTEGER\)' + -- json_output -- { "63": 63, diff --git a/object/state.go b/object/state.go index bf620695..1f57dfd3 100644 --- a/object/state.go +++ b/object/state.go @@ -302,7 +302,7 @@ func (e *Environment) SetNoChecks(name string, val Object, create bool) Object { // New name... let's see if it's really new or making it a ref. if ref, ok := e.makeRef(name); ok { log.Debugf("SetNoChecks(%s) created ref %s in %d", name, ref.Name, ref.RefEnv.depth) - ref.RefEnv.store[ref.Name] = val + ref.RefEnv.store[ref.Name] = Value(val) // kinda neat to make aliases but it can create loops, so not for now. return val } log.Debugf("SetNoChecks(%s) brand new to %d and above", name, e.depth)