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

[Crash] Null/uninitialized natives passed to functions

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

I started to get a lot of weird behavior with functions--they would suddenly not respond at all, even though they worked just in the previous version.

I believe it was because I had been passing uninitialized native variables, e.g. "boolean" or "integer" to functions. But it doesn't crash the game oddly enough. Which is worse in my opinion, because then I have no clue what is wrong.

e.g.

JASS:
boolean bool
...
if bool then
...

Now does JASS have default values for natives--e.g. booleans by default are false and integers are 0?

I think I didn't catch these errors because I'm used to languages like Java where the compiler tells you you didn't initialize a passed value. If that's the issue it should definitely be a feature of the VJASS compiler.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Actually integers are by default 0, reals 0.000 or more deciamls, if you use R2SW and booleans false. Everything else should be null by default. Which can be easy tested.

Edit: Only works for arrays.
JASS:
struct test

integer i
boolean b
real c
    static method run takes nothing returns nothing
        local thistype this = test.create()
        call BJDebugMsg(I2S(i))
        call BJDebugMsg(R2S(c))
        if b then
            call BJDebugMsg("true")
        else
             call BJDebugMsg("false")
        endif
        call this.destroy()
    endmethod
    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterPlayerEventEndCinematic(t, Player(0))
        call TriggerAddAction(t, function thistype.run)
    endmethod
endstruct
 
Last edited:

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
Actually integers are by default 0, reals 0.000 or more deciamls, if you use R2SW and booleans false. Everything else should be null by default. Which can be easy tested
JASS:
struct test

integer i
boolean b
real c
    static method run takes nothing returns nothing
        local thistype this = test.create()
        call BJDebugMsg(I2S(i))
        call BJDebugMsg(R2S(c))
        if b then
            call BJDebugMsg("true")
        else
             call BJDebugMsg("false")
        endif
        call this.destroy()
    endmethod
    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterPlayerEventEndCinematic(t, Player(0))
        call TriggerAddAction(t, function thistype.run)
    endmethod
endstruct

because theyre arrays. normal variables crash the thread when accessed uninitialized.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
default value is null, "", 0, 0.00, false as observed by arrays or reading from empty hashtable or gamecache

However, just like in C/C++, uninitialized values do not receive default value, but garbage

and yes, if you try to use uninitialized local or global values, your "virtual thread"(come up with better name) will crash

Example:

JASS:
private struct s extends array //no initialization of struct needed
    static method a takes integer a returns nothing
        call BJDebugMsg("This wont print as well")
    endmethod
    static method onInit takes nothing returns nothing
        local integer i
        local integer b
        call BJDebugMsg("This still works")
        set b = 5
        call BJDebugMsg("Still works, no read requested, only write")
        call thistype.a(i)    //using uninitialized variable, crashes virtual thread immediately
        call BJDebugMsg("This will never Print")
    endmethod
endstruct
 
Level 15
Joined
Aug 7, 2013
Messages
1,337
Right so why doesn't the game tell me a thread crashed? Like I said I think it would have been better if the actual game crashed, as it took me ages sadly to figure out the issue.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
A good example for the advantage of putting some debug lines into "crtitcal" parts of a system, snippet, etc...
Based on the questions you asked over the last month, I think you are very well aware of that aswell :).

Did you use library (uses, needs, requires) and scope correctly. Just to clarify again, the order you have in the trigger editor is not the order the JassHelper compiles the code in the end.
Also the JassHelper uses a constant boolean DEBUG_MODE. If you check mark debug mode in the editor it is set to true. Now you can do the following:
JASS:
static if DEBUG_MODE then
//Will only compile in debug mode ... what a surprise ^^
debug call BJDebugMsg("error?")
endif
Personally I'm using the snippet ErrorMessage, sadly we don't have a link on THW at the moment. It can still be found in Nestharus signature though.
 
Last edited:
Level 23
Joined
Apr 16, 2012
Messages
4,041
you can also put debug keyword before anything, and if you have debug mode, the code will be there, if you dont, it wont

why it wont print messages? Imagine you had map where a lot of units would fight, and you would enum over them multiple times in single routine, you could accidentally keep crashing thread, which would cause the game to spit messages at you

I can only say, deal with it, after all its warcraft 3, not proffesional compiler, it cant even optimize, certainly not debug
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
Right so why doesn't the game tell me a thread crashed? Like I said I think it would have been better if the actual game crashed, as it took me ages sadly to figure out the issue.

fdexSEt.jpg


Nah, but if wc3 crashed without a proper error message like how so often, you can still search for long. It's better if it just halts the thread, then you may see what parts of it executed, print debug messages, you can repeat it and do some tests right away. Plus you often want to try out other features of the map as well and not to constantly restart.

In a bigger project, you do not have the direct reason->effect insight, the errors become somewhat relative.
 
Because why not to make code ugly as hell.
I lost 2 days fixing Shadows of the Past Custom Inventory back in a day because of this damn shit.
2 freaking days until I realized that only damn error could be using uninitialized variables.
I understand that let's say unit u may be null by default, but hey let's freaking ignore common sense and freak the things up.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,201
It is because JASS uses some kind of hash table system to resolve the variables. If something never is initialized it will not be in the hash table. Some times this may be caught but other times it causes a thread crash.

You should always initialize all variables to some value before access anyway. Not doing that is a sign of poor programing practice that most employers hate. So many vulnerabilities and security exploits have arisen because the programmer falsely assumes stuff has a default value. Bring in Super Mario World and its total control exploit! This is particularly relevant to this topic as it works by abusing memory garbage to create an invalid sprite with a update function pointer set to the address of the controller registers allowing arbitrary code to be executed based upon controller input. This is why you should always make sure that you only read values that have been written and not that have whatever garbage they have there to start with (do not assume memory always zeroed).
 
Level 15
Joined
Aug 7, 2013
Messages
1,337
Well JASS forces us to declare all our locals ahead of time, and if I'm doing a series of if statements, I might not even get to use some of those locals, so if I did initialize them before hand in each function call but did not end up actually using them, I would be less efficient would I not.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,201
Well JASS forces us to declare all our locals ahead of time, and if I'm doing a series of if statements, I might not even get to use some of those locals, so if I did initialize them before hand in each function call but did not end up actually using them, I would be less efficient would I not.
Which is why you initialize them in the conditional branch. Only if you use them you initialize them. For them to be initialized they just need to have a known value before they are used, even if you assign that value the line before they are used in a conditional branch.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
this is microoptimzation comparable to Nes' 0 == this vs this == 0

really, you should at least default initialize them, not doing that may take microseconds, maybe even less

Believe me, if your map starts lagging, its not because you initialized your variables to null too much
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,201
It has nothing to do with micro optimization. You only need to initialize stuff before you use it. Some times it is more readable to do it at the start of the function but other times if a local is optional there may not be a sensible initial value for it neither do you care what value it has until you actually need to use it. Depending on the number of such locals you may wish to consider making a new function for more readability (less local declaration clutter).

The rule is that before you read any variable you need to be sure that it has a sensible and known value. It does not mater if this value comes from the start of the function or the very line before you use it, as long as you are absolutely sure a variable has been given a sensible value.

There is one exception to this, which usually applies to RNGs in simple platforms such as the NES and SNES. There many RNGs used the arbitrary initial state of console memory to seed themselves meaning that few starts and consoles were ever identical, especially if power cycled only shortly. Although this is a great way to get randomness, it is not ideal as it is non deterministic so virtually nothing but very cheap devices such as kids toys use it.

This exception is that you want and can handle a non-deterministic value. Generic examples of this are RNGs, filters (if no sensible initial state value can be generated) and closed loop control systems (it will stabilize naturally over time anyway).
 
Status
Not open for further replies.
Top