[Tutorial] Known Runtime/Compiler PAWN Bugs - Printable Version
+- SA-MP Forums Archive (
https://sampforum.blast.hk)
+-- Forum: SA-MP Scripting and Plugins (
https://sampforum.blast.hk/forumdisplay.php?fid=8)
+--- Forum: Scripting Help (
https://sampforum.blast.hk/forumdisplay.php?fid=12)
+---- Forum: Tutorials (
https://sampforum.blast.hk/forumdisplay.php?fid=70)
+---- Thread: [Tutorial] Known Runtime/Compiler PAWN Bugs (
/showthread.php?tid=570960)
Known Runtime/Compiler PAWN Bugs -
Ada32 - 14.04.2015
Introduction
Known (or suspected) compiler bugs. Feel free to add more if there are any.
1)
- Code
- Problem
Returning string literals directly crashes the compiler.
- Solution
Put the string you want to return in to an array. If you use "static const" to declare the array the resulting code is either identical to, or in some cases BETTER THAN, the original version returning a string literal.
pawn Code:
static const
szcHi[] = "hi";
return szcHi;
2)
- Code
pawn Code:
string = (a == 5) ? "is five" : "is not five";
- Problem
Strings can be concatenated by the compiler, for example this will compile as the single string "Hello World":
pawn Code:
string = "Hello" " " "World";
However, the compiler doesn't recognise that ":" in a ternary operator can end a string so thinks it is an invalid part of the string. The result is many odd errors such as:
pawn Code:
<path>\errors.pwn(11) : error 001: expected token: "-string end-", but found "-identifier-"
Pawn compiler 3.2.3664 Copyright (c) 1997-2006, ITB CompuPhase
1 Error.
I should note that this is MY fault, string concatenation is one of the modifications I made in the SA-MP version of the compiler.
- Solution
To fix this, just enclose the strings in brackets, which the commpiler DOES recognise as a valid string end. Note again that the does not affect the generated code in any way so there is no run-time cost associated with this fix.
pawn Code:
string = (a == 5) ? ("is five") : ("is not five");
3)
- Code
pawn Code:
new gGlobalVariable = SomeFunction();
- Problem
Calling a function to initialise a global variable outside of a function will crash the compiler.
- Solution
Call the function to initialise the variable in "OnGameModeInit", "OnFilterScriptInit", or "main".
4)
- Code
pawn Code:
#emit SYSREQ.C fblockread
- Problem
When using "SYSREQ.C" to call a native function, the compiler will not add that native function to the list of used native functions stored in the .AMX file. If the function is not already there the compiler will crash.
- Solution
The simplest way to fix this crash is to add a function something like the following BEFORE the function in which "SYSREQ.C" is found:
pawn Code:
forward SYSREQ_FIX();
public SYSREQ_FIX()
{
fblockread(File:0, "", 0);
}
It must come BEFORE the function begin written, not after, and not inside. This code will still crash the compiler if "fblockread" (or whatever other native function you are using) has not been previously used:
pawn Code:
MyFunc()
{
fblockread(File:0, "", 0);
#emit SYSREQ.C fblockread
}
5)
- Code
pawn Code:
stock Function1()
{
}
stock Function3()
{
#emit CONST.pri Function2
}
stock Function2()
{
}
- Problem
It seems that if a function uses "CONST.pri" to get the address of a function, and that function comes after other functions in the same file, and those functions are stock and not included, then "CONST.pri" will get the wrong value.
- Solution
I suspect what is happening here is that the compiler first compiles all the functions in a file, and from there gets the address of any functions used in "CONST.pri". THEN it removes any functions that are not used and shifts all later functions up in memory. This causes the address of the functions to change but any "CONST.pri" (or "CONST.alt") uses are not updated.
The solution is simple - because as far as I can tell the problem only occurs within a file, just move any functions used in "CONST.pri" to near the top of the file.
Note that I am not ENTRELY sure of the circumstances under which this happens yet (I only found it last night). When it happened the function whose address I was getting was AFTER the function in which the address was being got. That may have also contributed to the issue and thus simply moving the target function up should solve the issue.
6)
- Code
Unknown.
- Problem
The compiler has a line length limit of 512. Normaly if this limit is breached you will get a line length error, but it is possible when using macros to push over this limit without the compiler noticing and thus crash the compiler. I have no idea exactly what causes it though.
- Solution
Write shorter lines. Normally this is simpler than people think:
pawn Code:
format(str, sizeof (str),
"A long string",
other,
parameters);
That splits the function call over 4 lines, making each one of them shorter. However, that's only a solution to the normal too long problem that gives a compiler error. The bug is when the error doesn't happen and the compiler crashes instead.
7)
- Code
- Problem
You can't use the "CALL" assembly instruction with "#emit" - it seems to crash the compiler.
- Solution
If you can, just call the function normally:
If you can't and need to use assembly use this code to get the return address and jump to the start manually:
pawn Code:
#emit LCTRL 6
#emit ADD.C 28
#emit PUSH.pri
#emit CONST.pri main
#emit SCTRL 6
- 8 )
- Code
pawn Code:
#include <a_samp>
_:operator=(Taggg:a)
{
return _:a + 5;
}
main()
{
new
a = d();
printf("%d", a);
}
forward Taggg:d();
Taggg:d()
{
return Taggg:5;
}
- Problem
If you use a custom assignment operator (as "Float:" does) and forward a tag returning function AFTER it is first used instead of before, the compiler can think that the function is both not used and used, so generate code assuming that it exists but never include it and result in the wrong code being called (the example above will endlessly loop "main").
More information can be found here: https://sampforum.blast.hk/showthread.php?tid=425970
- Solution
Place the tag returning function's "forward" above where it is first used (here "main"). Alternatively remove the "forward" all together, that will generate the correct code but with a warning.
9)
- Code
pawn Code:
new gVar;
stock SomeFunction()
{
#if defined main
gVar = (gVar ? 0 : 1);
#endif
return 1;
}
main()
{
return SomeFunction() ? 0 : 1;
}
- Problem
The code above will generate the first ternary operator on the SECOND compiler pass but not the first (the first time it doesn't yet know that "main" is defined later on). As a result, for some unknown reason, any other ternary operators in your mode will have the wrong code generated and cause a crash.
More information can be found here: https://groups.******.com/forum/?fro...pt/1Cg2C8dn-0A
- Solution
Don't put ternary operators inside condition code generation that relies on "defined".
10)
- Code
pawn Code:
new gVar;
stock SomeFunction()
{
if (gSomeVar == SOMETHING)
{
print("In if.");
}
#emit LCTRL 6
#emit STOR.pri gOtherVar
return 1;
}
- Problem
When "#emit" assembly comes directly after an "if" statement, the compiler incorrectly assigns the assembly INSIDE the "if" statement instead of after it. The code above will compile as if you wrote this:
pawn Code:
new gVar;
stock SomeFunction()
{
if (gSomeVar == SOMETHING)
{
print("In if.");
#emit LCTRL 6
#emit STOR.pri gOtherVar
}
return 1;
}
It doesn't matter what the "if" check is, what is inside the braces, or what assembly you use.
- Solution
The simplest solution is to place extra empty braces between the end of the "if" statement and the assembly:
pawn Code:
new gSomeVar, gOtherVar;
stock SomeFunction()
{
if (gSomeVar == SOMETHING)
{
print("In if.");
}
{}
#emit LCTRL 6
#emit STOR.pri gOtherVar
return 1;
}
With decent compiler flags that will not produce any extra assembly.
11)
- Code
pawn Code:
stock VaradicFunction(static_, args, ...)
{
new
str[128];
// Other code here.
return str;
}
- Problem
Returning a string from a varadic function causes a run-time crash (not a compiler crash).
- Solution
This code curtesy of Slice:
pawn Code:
stock VaradicFunction(static_, args, ...)
{
new
str[128];
// Other code here.
#emit LOAD.S.pri 8
#emit ADD.C 12
#emit MOVE.alt
#emit LCTRL 5
#emit ADD
#emit LOAD.I
#emit STOR.S.pri 24 // 16 + (static_args * 4)
return str;
}
Note that the last assembly line must be adjusted for your function.
Credits to Y_Less..
Re: Known Runtime/Compiler PAWN Bugs -
Yashas - 17.06.2015
12)- Code
Code:
#if defined FILTERSCRIPT
public OnFilterScriptInit()
{
new idx = funcidx("OnPlayerCommandPerformed");
if(idx == -1)
{
#emit LOAD.S.alt idx
}
}
#else
public OnGameModeInit()
{
new idx = funcidx("OnPlayerCommandPerformed");
if(idx != -1)
{
#emit LOAD.S.alt idx
}
}
#endif
- Problem
You get this error "error 017: undefined symbol "idx" though there is no error.
If you are using a gamemode (it will give rise to an error in OnFilterscriptInit), then the compiler gives that error for the line "#emit LOAD.S.alt idx" which is inside OnFilterScriptInit.The compiler isn't supposed to check OnFilterScriptInit because #if defined FILTERSCRIPT will fail.The compiler ignores the "new idx" because the #if failed but it doesn't ignore "#emit LOAD.S.alt idx".
The generalized version of this bug is that #emit code when placed inside #if is always added irrespective of the falsity of the condition given to #if.
- Solution:
The solution here is to make idx a global variable and change your inline assembly so that it will work with global variable.
Re: Known Runtime/Compiler PAWN Bugs -
Yashas - 12.07.2015
13)- Code
Code:
goto abc; //No error
#emit LOAD.pri a
#emit LOAD.alt b
#emit JSLESS abc //Error, undefined symbol abc
abc:
your code
- Problem
The compiler allows you to use goto with lables that are defined later (somewhere down the file after your goto statement). But unfortunately the compiler does not recognize labels that are defined later when you use #emit.
- Solution:
No idea!