• 🏆 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!

[JASS] Does this leak? (local group)

Status
Not open for further replies.
Level 3
Joined
Feb 1, 2010
Messages
16
I made this spell some days ago and I don't know if the local group in this code leaks. The main question is do local groups "destroy" themselves at the end of the function or I need to manually null them?
I would appreciate any help.

Here is the code:

JASS:
function HolyGround_SpellID takes nothing returns integer
    return 'A001'
endfunction 

function HolyGround_Main takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local unit c = LoadUnitHandle(udg_HolyGround_Hash, id, 1)
    local unit u
    local real x = LoadReal(udg_HolyGround_Hash, id, 2)
    local real y = LoadReal(udg_HolyGround_Hash, id, 3)
    local integer duration = LoadInteger(udg_HolyGround_Hash, id, 4)
    local integer level = GetUnitAbilityLevel(c, HolyGround_SpellID())
    local real heal = 20+(level*20)
    local real damage = 10+(level*10)
    local group g = CreateGroup()
//==================================================================
    call SaveInteger(udg_HolyGround_Hash, id, 4, duration+1)
    call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Other\\Andt\\Andt.mdl", x, y))
    call GroupEnumUnitsInRange(g, x, y, 600.00, null)
    loop
      set u = FirstOfGroup(g)
    exitwhen u == null
      if not IsUnitType(u, UNIT_TYPE_STRUCTURE) and not IsUnitType(u, UNIT_TYPE_DEAD) and IsUnitAlly(u, GetOwningPlayer(c)) and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) then
        call SetUnitState(u, UNIT_STATE_LIFE, GetUnitState(u, UNIT_STATE_LIFE)+heal)
      endif
      if not IsUnitType(u, UNIT_TYPE_STRUCTURE) and not IsUnitType(u, UNIT_TYPE_DEAD) and IsUnitEnemy(u, GetOwningPlayer(c)) and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) then
        call UnitDamageTarget(c, u, damage, false, false, null, DAMAGE_TYPE_MAGIC, null)
      endif
      call GroupRemoveUnit(g, u)
    endloop
    if duration == 5 then
      call DestroyTimer(t)
      set g = null
      call FlushChildHashtable(udg_HolyGround_Hash, id)
    endif
endfunction

function HolyGround_Start takes nothing returns boolean
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)
    local unit c = GetTriggerUnit()
    local integer duration = 1
    local real x = GetSpellTargetX()
    local real y = GetSpellTargetY()
//==================================================================
    if GetSpellAbilityId() == HolyGround_SpellID() then
      call SaveUnitHandle(udg_HolyGround_Hash, id, 1, c)
      call SaveReal(udg_HolyGround_Hash, id, 2, x)
      call SaveReal(udg_HolyGround_Hash, id, 3, y)
      call SaveInteger(udg_HolyGround_Hash, id, 4, duration)
      call TimerStart(t, 1.00, true, function HolyGround_Main)
      return true
    endif
    return false
endfunction

//===========================================================================
function InitTrig_Holy_Ground takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function HolyGround_Start))
    set udg_HolyGround_Hash = InitHashtable()
endfunction
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,202
WarCraft III has no garbage collector. You need to destroy objects before you lose reference to them. Also due to a bug in the JASS virtual machine/interpreter, you will need to null any handle (or derived types of handle) local variables before the function ends otherwise there is a reference counter leak and the handle index used to reference the object can never be recycled.

To make it clear, the reference counter leak only applies for locals declared via the "local" statement. Locals declared as function areguments do get their reference counters decremented automaticly, just "local" locals do not. Also be aware that not all types of handle are reference counted or can be destoyed. An example is the player type where a player object never can be destoyed completly and probably has its own reserved handle index that is not available for re-use..
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,202
It is clearly a bug as the language is not meant to be able to leak references (as no language should). Argument locals work fine and do not need nulling but "local" declared locals do.

The fact that many BJ functions use by GUI suffer from this further proves it is a bug. Not even Blizzard was aware it was happening.
 
Status
Not open for further replies.
Top