Undefined Behavior in TorqueScript
by Glenn S · 04/23/2014 (1:03 pm) · 4 comments
So I was scripting something today, and noticed some strange undefined behavior.
I'm modding a Torque game, and had recently written a tooltip extension. Strangely, after writing that script I noticed that a button, "JS_refresh", was deleting itself. Looking through, I made no direct calls to JS_refresh, apart from setActive() in two places. I found that it would be deleted when I pressed the "Direct Connect" button, calling JoinServerGui::directConnect():
Adding if (isObject()) fixed the code, but the fact that this happens still interests me. Generally calling a function on an undefined object just returns "" (null), but for some odd reason it started returning parseable values.
So I changed around the code slightly to see what would happen if I defined getCount() as a variable:
I tried writing this in a more simple manner:
So why was this happening? JS_refresh was being updated in a repeated schedule every 100ms, so I cancelled that and tried to see if I could get this to work with any other objects. I tried calling functions with objects as parameters, but so far cannot get anything but JS_refresh.
I'll probably keep looking into this later, but I though sharing it with you all might help me figure out why this strange behavior is happening.
- Glenn
I'm modding a Torque game, and had recently written a tooltip extension. Strangely, after writing that script I noticed that a button, "JS_refresh", was deleting itself. Looking through, I made no direct calls to JS_refresh, apart from setActive() in two places. I found that it would be deleted when I pressed the "Direct Connect" button, calling JoinServerGui::directConnect():
function JoinServerGui::directConnect(%this) {
//Clear FooBarGroup
while (FooBarGroup.getCount()) {
%object = FooBarGroup.getObject(0);
//Stuff
%object.delete();
}
//Etc
}At this point, FooBarGroup did not exist, and getCount() was returning "" (null) and outputting a warning. What ended up happening is that, when the tooltip was updated, getCount() would return a value that passed as true in the while loop, still outputting the warning. The call to getObject(0) returned an unknown value, which just happened to be JS_refresh's id, and it outputted a warning as well.Adding if (isObject()) fixed the code, but the fact that this happens still interests me. Generally calling a function on an undefined object just returns "" (null), but for some odd reason it started returning parseable values.
So I changed around the code slightly to see what would happen if I defined getCount() as a variable:
function JoinServerGui::directConnect(%this) {
%count = FooBarGroup.getCount()
//Clear FooBarGroup
while (%count) {
%object = FooBarGroup.getObject(0);
//Stuff
%object.delete();
%count = FooBarGroup.getCount()
}
//Etc
}This time, %count was defined with the id of JS_refresh, and %object was blank.I tried writing this in a more simple manner:
function undefined() {
%unknown = NonExistingObject.getCount();
echo(%unknown);
}I tried calling this function from the console, but got nothing but a warning. Thinking it might be from the button, I assigned undefined() as the command of a button, and voila, it worked.So why was this happening? JS_refresh was being updated in a repeated schedule every 100ms, so I cancelled that and tried to see if I could get this to work with any other objects. I tried calling functions with objects as parameters, but so far cannot get anything but JS_refresh.
I'll probably keep looking into this later, but I though sharing it with you all might help me figure out why this strange behavior is happening.
- Glenn
#2
04/23/2014 (1:24 pm)
James, he's modding a game, he can't add it :P
#4
Second if you really need to loop through the group/set then you need to remove the object from the group/set before you delete it to make sure you don't run into your current problem:
The reason why a deleted object may still show up in a group/set is because it isn't removed from a group and/or sets until the object is actually being removed from the console/TorqueScript subsystem which occurs later on, anywhere from one to several ticks later depending on if it's ghosted or not.
04/25/2014 (7:52 am)
There are two workarounds. First one is if you intent to just delete all the objects in a group or set then simply do %group.deleteAllObjects(); and be done with it.Second if you really need to loop through the group/set then you need to remove the object from the group/set before you delete it to make sure you don't run into your current problem:
function JoinServerGui::directConnect(%this) {
//Clear FooBarGroup
while (FooBarGroup.getCount()) {
%object = FooBarGroup.getObject(0);
//Stuff
FooBarGroup.remove(%object);
%object.delete();
}
//Etc
}The reason why a deleted object may still show up in a group/set is because it isn't removed from a group and/or sets until the object is actually being removed from the console/TorqueScript subsystem which occurs later on, anywhere from one to several ticks later depending on if it's ghosted or not.

Associate James Urquhart
Since all object references in TorqueScript are strings, if there is a number or a name on the stack you can easily end up with it finding the wrong object.
In the CodeBlock::exec loop I suggest adding a call to setStringValue to resolve this...
if(!gEvalState.thisObject) { // Go back to the previous saved object. gEvalState.thisObject = saveObject; Con::warnf(ConsoleLogEntry::General,"%s: Unable to find object: '%s' attempting to call function '%s'", getFileLine(ip-4), callArgv[1], fnName); STR.popFrame(); STR.setStringValue(""); // <<< break; }