- Joined
- Apr 27, 2008
- Messages
- 2,455
All experimented "jassers" know that jass is tricky in many ways, there are things which are not obvious, or even senseless.
This thread is an attempt to list all these special behaviors, this way new users (there are still one from time to time which was in his cave and suddenly go out and try jass for no real reason) won't be trapped.
Note that i personnaly can't test anymore now, ofc there is still the possibility that i reinstall wc3 but that's really unlikely.
So i will say just what i'm 100 % sure.
Ofc feel free to contribute and take any, all, or nothing of my words in this thread to create a more elaborated thread.
If you want contribute, please use numbers like i did, for now the numbers are just to list the things, it's totally random, it isn't sorted depends the matter.
There is also this interesting thread : http://www.hiveworkshop.com/forums/...n-j-natives-do-nothing-useless-broken-199627/
Here we go.
1) boolexprs
Boolexprs created with the functions
In other words :
Yes, you can use either
Which also means that most of times
Now, a boolexpr created with the functions
2) texttags
You just can't create more than 100 texttags simultaneously.
The id of a texttag follows a lame convention, the first texttag will have the id "99", and the last one "0", but obviously a null texttag will also have the id "0".
So unless you handle your own counter you can't use GetHandleId to know if you've reached this limit or not (well in fact you can but it's a bit more complicated).
If you create more than 100 texttags, the one with the id "0" will be erased, let's say the previous one.
But good news, despite many other handles which will cause a desync if you create/destroy them within a local block (
3) variables
- if you use a not defined variable (without any value), it will crash the thread (like if a return was used).
- arrays' size is dynamic, you don't have to handle that. And unlike not array variables you don't have to give a value before using the variable, it will have the default value of the type, such as "false" for boolean.
The index available starts to 0 and ends to 8191, however if you use the index 8191 of any variable you won't be able to load a saved game (yes it's a bug which was never fixed)
- "null" can be used for every type of variable.
For every variable which is not an handle, it will give the default value of the type (excepted string), boolean -> "false", integer and real -> "0".
And for string, null means null, no string and not "".
4) memory handle id leak
Generally, handles and types which are derived of handles have a reference counter.
There are exceptions though, such as texttags and images.
The handle id is recycled only when there is no more variable which points on it, or "immediatly" (maybe it's still delayed, i have not checked it) after the handle was destroyed if there is no reference counter.
For a local variable we would expect that this reference counter is smart enough to handle them correcty, it isn't.
If you don't null them, the handle id will never be recycled, you have to null the local variable.
So, as you can see, the GUI function
However, function arguments (function Test takes ...) are not concerned by this bug, the reference counter handle them correctly.
5) memory leaks
Jass lacks of destructors, for example there is no way to remove a trigger event, a trackable, ...
So sometimes you just can't avoid a memory leak.
We would also expect that destroy a trigger would also destroy everything of it (events, conditions, actions), but it doesn't, or at least it doesn't remove actions, you have to use
For some reasons you don't have to worry with trigger conditions, it's one of the reason why some people don't use trigger actions but only trigger conditions.
I don't know about events, some people will say that the memory is cleaned up when the trigger is destroyed but the fact is that i've never seen a proof of it, and have never tested myself, so it could just be bullshit as well.
6) limit op
There is a safeguard in jass, after a certain number of operations, the jass code is just ignored, the thread crash.
In addition of what PipeDream said every native function which takes a code or boolexpr, such as
7) Strings
Check there : http://www.hiveworkshop.com/forums/lab-715/documentation-string-type-240473/
8) StringHash
This is not case sensitive, StringHash("aBcD") == StringHash("abcd")
And it doesn't make a difference between "/" and "\\", StringHash("a/b") == StringHash(a\\b).
9) Gamecache and hashtable
- There is an hardcoded limit of 256, if you try to have more simultaneously, the init function will return null (InitHashtable, InitGameCache).
- The keys of a gamecache are not case sensitive.
- You can't store "null" inside them (well remember about what null means for boolean, integer and real, for these ones you can).
- Same for "ghost" handles, (the handle is destroyed but the variable is not nulled).
And then you can use the boolean of the native Save hashtable functions to know if an handle is valid (not null, neither "ghost").
.
This thread is an attempt to list all these special behaviors, this way new users (there are still one from time to time which was in his cave and suddenly go out and try jass for no real reason) won't be trapped.
Note that i personnaly can't test anymore now, ofc there is still the possibility that i reinstall wc3 but that's really unlikely.
So i will say just what i'm 100 % sure.
Ofc feel free to contribute and take any, all, or nothing of my words in this thread to create a more elaborated thread.
If you want contribute, please use numbers like i did, for now the numbers are just to list the things, it's totally random, it isn't sorted depends the matter.
There is also this interesting thread : http://www.hiveworkshop.com/forums/...n-j-natives-do-nothing-useless-broken-199627/
Here we go.
1) boolexprs
Boolexprs created with the functions
Condition
and Filter
are created only if it was not already created, if the boolexpr was already created then it points on this one.In other words :
Filter(function F1) == Condition(function F1)
Yes, you can use either
Filter
Or Condition
, that doesn't matter, i personnaly prefer Filter
, just because it's faster to write and this word is generic.Which also means that most of times
DestroyBoolExpr
is useless.Now, a boolexpr created with the functions
And
and Or
is always a new one.2) texttags
You just can't create more than 100 texttags simultaneously.
The id of a texttag follows a lame convention, the first texttag will have the id "99", and the last one "0", but obviously a null texttag will also have the id "0".
So unless you handle your own counter you can't use GetHandleId to know if you've reached this limit or not (well in fact you can but it's a bit more complicated).
If you create more than 100 texttags, the one with the id "0" will be erased, let's say the previous one.
But good news, despite many other handles which will cause a desync if you create/destroy them within a local block (
GetLocalPlayer
), you can safely create a texttag in a local block, this way you will reach later or even never this hardcoded limit.3) variables
- if you use a not defined variable (without any value), it will crash the thread (like if a return was used).
- arrays' size is dynamic, you don't have to handle that. And unlike not array variables you don't have to give a value before using the variable, it will have the default value of the type, such as "false" for boolean.
The index available starts to 0 and ends to 8191, however if you use the index 8191 of any variable you won't be able to load a saved game (yes it's a bug which was never fixed)
- "null" can be used for every type of variable.
For every variable which is not an handle, it will give the default value of the type (excepted string), boolean -> "false", integer and real -> "0".
And for string, null means null, no string and not "".
4) memory handle id leak
Generally, handles and types which are derived of handles have a reference counter.
There are exceptions though, such as texttags and images.
The handle id is recycled only when there is no more variable which points on it, or "immediatly" (maybe it's still delayed, i have not checked it) after the handle was destroyed if there is no reference counter.
For a local variable we would expect that this reference counter is smart enough to handle them correcty, it isn't.
If you don't null them, the handle id will never be recycled, you have to null the local variable.
So, as you can see, the GUI function
PolledWait
(as an example) leaks an handle id each time it's used, we could also talk about its inaccuracy because of TriggerSleepAction
usage, but that's a whole new story ...However, function arguments (function Test takes ...) are not concerned by this bug, the reference counter handle them correctly.
5) memory leaks
Jass lacks of destructors, for example there is no way to remove a trigger event, a trackable, ...
So sometimes you just can't avoid a memory leak.
We would also expect that destroy a trigger would also destroy everything of it (events, conditions, actions), but it doesn't, or at least it doesn't remove actions, you have to use
TriggerRemoveAction
before DestroyTrigger
.TriggerClearActions
only "deactivate" actions but doesn't clean up the memory at all.For some reasons you don't have to worry with trigger conditions, it's one of the reason why some people don't use trigger actions but only trigger conditions.
I don't know about events, some people will say that the memory is cleaned up when the trigger is destroyed but the fact is that i've never seen a proof of it, and have never tested myself, so it could just be bullshit as well.
6) limit op
There is a safeguard in jass, after a certain number of operations, the jass code is just ignored, the thread crash.
PipeDream said:When you load a map, warcraft reads the jass file from disk and compiles it to a bytecode format. Bytecode ops are things like allocate a variable, multiply two registers, or call a native. A typical line of jass is 1-6 ops. Each instance of the virtual machine (new one on event, TriggerExecute, TriggerEvaluate, ExecuteFunc, TriggerSleepAction) has a limit at 300000 ops of continuous execution, at which point the VM returns.
1.21 and earlier versions of war3err can trace bytecode
1.22 and later versions of war3err can dump a function's bytecode with
Sample chunk of executing bytecode:JASS:call Cheat("GetBytecode funcname")
The (full?) list of ops:Code:bench :: 0 4:integer 217 0xe:read 3796:sw bench :: 0 0 217 0x13:push 0 bench :: 0 0 0 0x15:callnative 2070:StopWatchMark bench :: 0 0 0 0x11:set 3798:t1 bench :: 0 4:integer 218 0xc:literal 1000 bench :: 0 0 218 0x17:typecast 0 bench :: 0 0 218 0x13:push 0 bench :: 0 5:real 219 0xe:read 3798:t1 bench :: 0 0 219 0x13:push 0 bench :: 0 5:real 220 0xe:read 3797:t0 bench :: 0 0 221 0x14:setreg 0 bench :: 220 221 221 0x21:subtract 0 bench :: 0 0 222 0x14:setreg 0 bench :: 221 222 222 0x22:multiply 0 bench :: 0 0 222 0x13:push 0 bench :: 0 0 0 0x15:callnative 596:R2S bench :: 0 0 0 0x13:push 0 bench :: 0 0 0 0x16:call 2547:BJDebugMsg BJDebugMsg :: 0 1 6:string 0x8:pop 2077:msg BJDebugMsg :: 0 0 4:integer 0x5:local 92:i BJDebugMsg :: 0 4:integer 186 0xc:literal 0 BJDebugMsg :: 0 0 186 0x11:set 92:i BJDebugMsg :: 0 0 0 0x28:jump target 1 BJDebugMsg :: 0 4:integer 187 0xe:read 92:i
The binary bytecode format:Code:enum OPCODES { OP_ENDPROGRAM=0x1, OP_FUNCTION=0x3, // _ _ rettype funcname OP_ENDFUNCTION=0x4, OP_LOCAL=0x5, // _ _ type name OP_GLOBAL=0x6,OP_CONSTANT=0x7, OP_POPFUNCARG=0x8, // _ srcargi type destvar OP_CLEANSTACK=0xB, // _ _ nargs _ OP_LITERAL=0xC, // _ type destreg srcvalue OP_SETRET=0xD, // _ srcreg _ _ OP_GETVAR=0xE, // _ type destreg srcvar OP_CODE=0xF, OP_GETARRAY=0x10, OP_SETVAR=0x11, // _ _ srcreg destvar OP_SETARRAY=0x12, OP_PUSH=0x13, // _ _ srcreg _ OP_SETRIGHT=0x14, OP_NATIVE=0x15, // _ _ _ fn OP_JASSCALL=0x16, // _ _ _ fn OP_I2R=0x17, OP_AND = 0x18, OP_OR = 0x19, OP_EQUAL=0x1A, OP_NOTEQUAL=0x1B, // check OP_LESSEREQUAL=0x1C,OP_GREATEREQUAL=0x1D, OP_LESSER=0x1E,OP_GREATER=0x1F, OP_ADD=0x20,OP_SUB,OP_MUL,OP_DIV, OP_MODULO = 0x24, // unused OP_NEGATE=0x25, OP_NOT = 0x26, OP_RETURN=0x27, // _ _ _ _ OP_JUMPTARGET=0x28, OP_JUMPIFTRUE=0x29,OP_JUMPIFFALSE=0x2A, OP_JUMP=0x2B }
Code:typedef struct opcode { unsigned char r1,r2,r3; // register arguments and types unsigned char optype; // one of OPCODES int arg; // values, targets, names } opcode;
In addition of what PipeDream said every native function which takes a code or boolexpr, such as
ForForce
, or ForceEnumPlayers
reset the limit op, same for trigger conditions and so one.7) Strings
Check there : http://www.hiveworkshop.com/forums/lab-715/documentation-string-type-240473/
8) StringHash
This is not case sensitive, StringHash("aBcD") == StringHash("abcd")
And it doesn't make a difference between "/" and "\\", StringHash("a/b") == StringHash(a\\b).
9) Gamecache and hashtable
- There is an hardcoded limit of 256, if you try to have more simultaneously, the init function will return null (InitHashtable, InitGameCache).
- The keys of a gamecache are not case sensitive.
- You can't store "null" inside them (well remember about what null means for boolean, integer and real, for these ones you can).
- Same for "ghost" handles, (the handle is destroyed but the variable is not nulled).
And then you can use the boolean of the native Save hashtable functions to know if an handle is valid (not null, neither "ghost").
.
Last edited: