Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VBScript seems to be GC'ing objects from nested scopes in the wrong order

I have the following code:

function p (str)
    Response.Write VBLF & str
end function

function nop: end function

class Test1
    private sub class_initialize
        p "Test1 Start"
    end sub

    private sub class_terminate
        p "Test1 End"
    end sub
end class

class Test2
    private sub class_initialize
        p "    Test2 Start"
    end sub

    private sub class_terminate
        p "    Test2 End"
    end sub
end class

When I run:

with new Test1
    with new Test2
    end with
end with

I expect the output:

Test1 Start
    Test2 Start
    Test2 End
Test1 End

But I get:

Test1 Start
    Test2 Start
Test1 End
    Test2 End

I do, however, get what I expected if I run either of the following:

with new Test1
    with new Test2
        nop
    end with
end with

with new Test1
    with new Test2
    end with
    nop
end with

But not the following:

with new Test1
    nop
    with new Test2
    end with
end with

VBScript has a fairly strong guarantee about GC'ing objects right away, and I'm using (abusing?) this guarantee for a variety of purposes in my applications. Without the nop, why is it collecting Test1 and Test2 in the "wrong" order?

like image 850
Thom Smith Avatar asked Dec 21 '22 23:12

Thom Smith


1 Answers

What a fascinating bug.

I do not have a debug build of the VBScript engine handy. (The ten year old hard disk that I was keeping it on has apparently gone bad some time in the two years since I last looked at it.) However, I can easily guess what is going on here. My suspicion is that I did not mark the instruction corresponding to the "end with" as a point at which a statement has ended and the collector must run.

Let's consider that hypothesis.

In your first example, when would the GC run? When the program ends. At that point, the terminators are run in the order that the objects were created. (This is an implementation detail that you should not rely upon.)

In your second and third example the end of the call to nop triggers a GC, and that happens right next to the end with. (I'm not quite sure why the second example triggers a GC when the object is eligible for collection; I don't recall the exact semantics of the codegen for popping the object reference out of the with block scope.)

In your fourth example, the GC is triggered before the inner with, and does nothing.

Since I wrote both the termination logic and a fair amount of the "with" processing code I undoubtedly caused this bug, so many apologies. Since apparently we've lived with it with no major problems for the last eleven or twelve years, and since it only affects "with" blocks that have no contents, I don't imagine it will ever be fixed.

Regardless, you should not rely on termination order. That's a bad programming practice.

For more on the fascinating subject of VBScript termination logic see:

What is the order of destruction of objects in VBScript?

like image 197
Eric Lippert Avatar answered Dec 29 '22 00:12

Eric Lippert