• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Pseudo-recursive call stops working

Status
Not open for further replies.
Level 15
Joined
Aug 7, 2013
Messages
1,337
Hi,

I have a recursive struct called Quad. Basically a Quad is a wrapper around a JASS rect. Each Quad also has a Quad array of max size 5 (so quads within quads, to resemble the recursive structure of rectangles).

I have this function that cleans up a quad and all of its subquads. It works if I have a single Quad and it has any number of quads in its array. But for some reason, if the the Quads within those quads in the array also have quads in their array, the function fails to clean THOSE ones up.

Here is the function
JASS:
    method flush takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i == BOLTS_PER_RECT
            call DestroyLightning(bolts[i])
            set bolts[i] = null
            set i = i + 1
        endloop
        set i = 0
        loop
            exitwhen subQuads[i] == 0
            call subQuads[i].flush()
            set i = i + 1
        endloop
        call RemoveRect(r)
        set r = null
        call this.destroy()
    endmethod

So here is where it works 100% of the time:
JASS:
//this works as expected
local Quad q = Quad.create(...)
set q.subQuads[0] = Quad.create(...)
...
set q.subQuads[4] = Quad.create(...)
call q.flush() //after calling flush, q is cleaned up
//and each Quad in its quad array is also cleaned up

Here is where it seems to fail 100% of the time and start building up leaks
JASS:
//this fails to work as expected
local Quad q = Quad.create(...)
set q.subQuads[0] = Quad.create(...)
...
set q.subQuads[4] = Quad.create(...)
set q.subQuads[0].subQuads[0] = Quad.create(...)
...
set q.subQuads[4].subQuads[4] = Quad.create(...)
call q.flush() //after calling flush, q is cleaned up
//and I believe its Quad array members are...
//BUT the array members of those Quad array members don't get cleaned up!!

So inherently there is a contradiction. We know that flush() works if we have this situation:
a single Quad and any number of quad array members

Now, since each quad array member is also a quad, and each quad can have an array of members, it must be the case that calling flush() also works as expected, since it's the same exact conditions.

But testing it out this is actually not the case!

So either I've got insane, there's some weird case in my code I'm failing to cover, or somehow my calls start to crash for a reason that I can't control (limit of warcraft 3 engine with pseudo-recursion?).
 

Cokemonkey11

Spell Reviewer
Level 30
Joined
May 9, 2006
Messages
3,537
JASS:
        loop
            exitwhen subQuads[i] == 0
            call subQuads[i].flush()
            set i = i + 1
        endloop

This is the issue I think. Since Quad contains a Quad array of size exactly 5, subQuads[5] comparator will cause undefined behavior (kill virtual thread I think)

Edit: Also, referencing a array instance not initialized will cause undefined behavior, eg:

JASS:
local integer array q[5]
set q[0] = 1
if q[1]==0 then
    //undefined behavior
endif
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
Edit: Also, referencing a array instance not initialized will cause undefined behavior, eg:

JASS:
local integer array q[5]
set q[0] = 1
if q[1]==0 then
    //undefined behavior
endif

nah, we're not in c-land. it will just return 0- value based on the type: 0, 0.0, false, null

see also
 

Cokemonkey11

Spell Reviewer
Level 30
Joined
May 9, 2006
Messages
3,537
nah, we're not in c-land. it will just return 0- value based on the type: 0, 0.0, false, null

see also

JASS:
scope test initializer i
	private function i takes nothing returns nothing
		local unit q
		call BJDebugMsg("hello world")
		if q==null then
			call BJDebugMsg("was null")
		endif
		if q!=null then
			call BJDebugMsg("was not null")
		endif
		call BJDebugMsg("test")
	endfunction
endscope
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
JASS:
scope test initializer i
	private function i takes nothing returns nothing
		local unit q
		call BJDebugMsg("hello world")
		if q==null then
			call BJDebugMsg("was null")
		endif
		if q!=null then
			call BJDebugMsg("was not null")
		endif
		call BJDebugMsg("test")
	endfunction
endscope

yeah, that's no array though.
reading an uninitialized variable kills the thread.
 
Status
Not open for further replies.
Top