Get the call stack in UnrealScript with the following two functions:
functionfor*/function scripttrace (); function */function String getscripttrace ();
These two are static functions on object, which is equivalent to the global function.
The function in C + + will eventually be called:
/** * This would return the StackTrace of the current callstack from. UC Land **/inline fstring fframe::getstacktrace ()Const{fstring Retval; //Travel down the stack recording the framestarray<ConstFframe*>Framestack; Constfframe* Currframe = This; while(Currframe! =NULL) {Framestack.additem (currframe); Currframe= currframe->Previousframe; } //And then dump them to a stringRetval + = fstring (TEXT ("Script Call stack:\n") ); for(INT idx = Framestack.num ()-1; IDX >=0; idx-- ) { Constfframe& F = *framestack (IDX); Retval+ = fstring::P rintf (TEXT ("\t%s%d\n"), *f.node->getfullname (), f.node->Line ); } returnRetval;}
This f. Node->line is the line number, originally did not, I added to. But it doesn't really work.
Because of the call stack backtracking, it is important to know which line each layer of functions is currently executing on, and the line here is just the row where the first sentence of the function definition block is located.
If a calls B, and in the function body of a, there are multiple places to invoke B, then for a stacktrace that does not actually execute the line number, it is not known which sentence the current A is executing into B.
To achieve the above function, it is certainly not enough to record only one function definition first line, it is necessary to establish a mapping table of bytecode and source line number, this mapping table may be large, can be removed by switch when compiling release mode. (for example, LUA does that)
Why didn't the UnrealScript do something like that in the middle of their own development?
I think it might have something to do with UnrealScript's design philosophy because, according to the documentation, there is no mechanism for UnrealScript:
- UnrealScript doesn ' t support generics, annotations or exception handling. Things like nullpointerexception or indexoutofboundsexception is handled gracefully in UnrealScript By just returning null values after writing a warning message to the log file. Typecasting object references is safe and also works for empty references.
And:
The idea behind using return types in functions (or methods if you'll) is to make sure it cannot return any other thing, And if it does, cast an Exception ...
You can ' t return anything from a function with no return type, nor can return anything of the wrong type (except in CA Ses where there is an automatic cast)-the compiler would give an error. That's much better than throwing an exception.
And:
Have an exception handler would also add this game halts on non-trapped exceptions. You really wouldn ' t want your game halting and dying on any error. Besides, there's only a handful of possible error conditions the can happen at runtime, anyway.
In summary, it is unrealscript itself is a strong type of compiled script, many problems can be found in the compilation period, and once entered the runtime, encountered an error, the first goal is to be compatible with it, rather than throw an exception. For example, the typical "Access None", which in C + + is to access the null pointer such a serious error, but UnrealScript does not throw an exception, just simply hit a warn to log, and then silently execute the next sentence code.
The benefit is that most of the statements in the function are executed without interrupting or altering the process due to the error of the sentence.
Since this is the case, the stack trace does not really have much use, because it is the function of an anomaly, to understand the call chain when the error occurs.
Several additional memos:
1, about the function definition of the line number, actually did not match with the function declaration in the source file strict matching, which made me very confused, but by observing the debug script compilation process, know the reason: the function of the line number, is actually the first sentence in its body "non-declarative statement" the row. That is, the head of all "local classxxx varyyy" Such statements do not count, until the "x=y" to calculate the first line of code. Specific implementations in the UnSrcCom.cpp:
Ubool fscriptcompiler::compilestatement () {Ubool Needsemicolon=1; //Get a token and compile it.Ftoken Token; if( ! GetToken (Token,null,1) ) { //End of file. return 0; } Else if(! compiledeclaration (Token, Needsemicolon) ) { if(Pass = =pass_parse) { //Skip This and subsequent commands so we can hit the them on next pass. if(Nestlevel <3) {Scripterrorf (Scel_unknown/*scel_formatting*/, TEXT ("unexpected '%s '"), token.identifier); } ungettoken (Token); popnest (topnest ->nesttype, Nesttypename (topnest-> nesttype)); Skipstatements (1, Nesttypename (topnest->nesttype)); Needsemicolon=0;
...
When Compiledeclaration fails (that is, when it is no longer a declaration statement), it is transferred to Popnest, which is the process of terminating the previous AST node, where the function node's line property is assigned:
void Fscriptcompiler::P opnest (Enesttype nesttype, const tchar* Descr) {... // pass-specific handling. if (Pass == Pass_parse) { Remember code position. if (Nesttype==nest_state | | Nesttype==nest_function) {topnode ->textpos = Inputpos; Topnode ->line = inputline;
...
And before we get to this point, we've looked at a lot of tokens, that is, reading a lot of characters ahead, including wrapping, so here inputline is not the line where the function declaration is, but the line that the current AST is a non-declarative statement.
The above is the case when compiling a "full function definition", for a statement of a sentence, such as:
Native static final function scripttrace ();
And what about this? In this case, the end of the semicolon, can be attributed to a function declaration, so in the compiledeclaration->compilefunctiondeclaration also called to Popnest, and then to the Inputline assignment, At this point the inputline is the current row, so the line number of this function is exactly matched.
2, in the VC debugging, in order to facilitate the observation of specific situations, it is necessary to set conditional breakpoints, usually can be compared to the string name class to achieve the purpose. and Unreal3 that the string fstring and fname are very trick structure, can not directly compare them with literals (mainly the breakpoint condition expression does not support C + + overload of this advanced syntax), need to get the two structure of the real data address, In order to compare:
FSTRING:WCSCMP (L "Engine.gameviewportclient", Value.AllocatorInstance.Data) ==0, note that the UNREAL3 uses a Unicode string
FNAME:STRCMP ("Gameviewportclient", ((fnameentry**) inclass->name.names.allocatorinstance.data) [InClass-> Name.index]->ansiname) ==0
Unreal3 Script StackTrace Issues