How Was the bindenv() Function Re-Written to Take a Strong Reference?

I have my own compiled version of Squirrel and I’m thinking of following suit with electric imps implementation and changing the sq_bindenv function to hold a strong reference to the environment it’s binding, but I’m unsure exactly what would be the proper way to do this.

Do you have any ideas on how to do this or is the imp implementation somewhere to find on the internet?

Sounds like one for squirrel-man … @peter ?

1 Like

Thank’s for your reply! Any help is greatly appreciated. Do you know if it would be as simple as replacing the GetWeakRef() in the sq_bindenv() function with a __ObjAddRef() macro and it would automagically be a strong ref. cleaned up properly by the GC when its time has come? I wouldn’t know how to deal with the environment setting code that comes after that though.

There’s a bit more to it than that if you want GC to work properly. Diff attached (might not apply cleanly to upstream Squirrel due to our other changes).
strongref.diff.zip (1.7 KB)

1 Like

Awesome @peter! I tried making the changes indicated in the diff, but it doesn’t seem to work.
If I successfully made bindenv() take a strong ref then the following code should work, right?:

function print_data()
{
print(data);
}

function test_bindenv_strong_ref(cb)
{
local local_data = {data = “I’m alive!”};
cb = print_data.bindenv(local_data);
}

local functor;
test_bindenv_strong_ref(functor);
functor();

But at this point I get a null exception at the functor() line. Other squirrel code runs without crashing during initial tests, but I’m not sure what’s actually happening underneath.

I was a bit confused by two things when reading the diff file:

  1. I couldn’t quite piece together how the ifs where pieced together in the bindenv function I saw in the diff file. Especially at the later part with the nativeclosure. Seeing the whole function would be helpful :slight_smile:

  2. It seems as if you’ve remove the _root member variable of the SQClosures as well. Is this related to getting a strong ref., is it something else, or am I misreading the diff file?

It looks to me like you’re passing null to test_bindenv_strong_ref(). The null is passed by value, so anything you do to cb won’t affect functor. You could try passing a table instead:

cb.f <- print_data.bindenv(local_data);

local functor={};
test_bindenv_strong_ref(functor);
functor.f();

1 Like

Interesting, @cvrdrvn. I thought everything in Squirrel got passed around by reference. Rewriting the code with that in mind seems to make the strong ref actually work! But there’s something really fishy going on for the unmodified Squirrel 3.1 build.

I’m currently running this code to test it:

local functor;
{
    local a = 
    {
        function print_data()
        {
            print(data);
        },
    
        function test_bindenv_strong_ref()
        {
            local local_data = {data = "I'm alive!"};
            return print_data.bindenv(local_data);
        }
    }
    functor = a.test_bindenv_strong_ref();
    print("Why does this print make the index 'data' not exist at the functor() call, when the local variable below just outside of the scope is present?");
}
local this_makes_data_exist_at_functor_for_some_reason = 1;

print(collectgarbage());
functor();

Does this code test whether bindenv() makes a strong reference to its environment, @cvrdrvn, or have I made any mistake here?

With vanilla 3.1 I’d expect functor() to always get null index error when trying to print(data), but as hinted in the code, declaring any local variable directly after the scope (be it table or integer) makes the “data” index still exist at the point when functor() is called. To make this more wierd: Adding a print() statement at the bottom of the scope where “a” was declared makes the index “data” disappear and the functor() call get a null index error.

Also the collectgarbage() function always returns 0 (both for vanilla 3.1 and my modified version). Shouldn’t that return the amount of items garbage collected? Or does that only clean up exotic stuff such as circular dependencies?

My 3.1 with the modifications @peter suggested does seem to keep the “data” object alive no matter what comes in between though.

@Epcilon,
I can’t comment to much on alternate Squirrel implementations, as I’m not familiar with them. Your code does bind print_data with local_data. In an implementation that uses weak refs, this would fail (I tried it on https://tio.run/#squirrel) but should work with Electric Imp’s version (if it supported print())

Ah of course, I slightly forgot that we actually were on the electric imp forum. But thanks for confirming that the code should fail in a weak ref. implementation! :slight_smile:

I’ll have to continue digging to see why my implementation behaves weird in that way. It is quite worrying though, that adding and removing seemingly unrelated statements would alter what survives a scope and what doesn’t.

Cheers @cvrdrvn!