- try to preserve a bit more information about incorrect use of user variables to print more meaningful error messages. This is not complete yet and will need integration with the previous commit.

This commit is contained in:
Christoph Oelckers 2016-11-13 12:02:41 +01:00
parent ac0413838c
commit f238f0ba5c
11 changed files with 94 additions and 5 deletions

View file

@ -8073,7 +8073,7 @@ FxExpression *FxClassTypeCast::Resolve(FCompileContext &ctx)
delete this;
return nullptr;
}
ScriptPosition.Message(MSG_DEBUG, "resolving '%s' as class name", clsname.GetChars());
ScriptPosition.Message(MSG_DEBUGLOG, "resolving '%s' as class name", clsname.GetChars());
}
}
FxExpression *x = new FxConstant(cls, to, ScriptPosition);

View file

@ -79,6 +79,7 @@ struct FCompileContext
int StateIndex; // index in actor's state table for anonymous functions, otherwise -1 (not used by DECORATE which pre-resolves state indices)
int StateCount; // amount of states an anoymous function is being used on (must be 1 for state indices to be allowed.)
int Lump;
bool Unsafe = false;
TDeletingArray<FxLocalVariableDeclaration *> FunctionArgs;
FCompileContext(PFunction *func, PPrototype *ret, bool fromdecorate, int stateindex, int statecount, int lump);

View file

@ -909,6 +909,7 @@ PClassActor *CreateNewActor(const FScriptPosition &sc, FName typeName, FName par
}
}
ti = DecoDerivedClass(sc, parent, typeName);
ti->bDecorateClass = true; // we only set this for 'modern' DECORATE. The original stuff is so limited that it cannot do anything that may require flagging.
ti->Replacee = ti->Replacement = NULL;
ti->DoomEdNum = -1;

View file

@ -206,6 +206,55 @@ void CreateDamageFunction(PClassActor *info, AActor *defaults, FxExpression *id,
}
}
//==========================================================================
//
// CheckForUnsafeStates
//
// Performs a quick analysis to find potentially bad states.
// This is not perfect because it cannot track jumps by function.
// For such cases a runtime check in the relevant places is also present.
//
//==========================================================================
static int CheckForUnsafeStates(PClassActor *obj)
{
static ENamedName weaponstates[] = { NAME_Ready, NAME_Deselect, NAME_Select, NAME_Fire, NAME_AltFire, NAME_Hold, NAME_AltHold, NAME_Flash, NAME_AltFlash, NAME_None };
static ENamedName pickupstates[] = { NAME_Pickup, NAME_Drop, NAME_Use, NAME_None };
TMap<FState *, bool> checked;
ENamedName *test;
int errors = 0;
if (obj->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
{
if (obj->Size == RUNTIME_CLASS(AWeapon)->Size) return 0; // This class cannot have user variables.
test = weaponstates;
}
else if (obj->IsDescendantOf(RUNTIME_CLASS(ACustomInventory)))
{
if (obj->Size == RUNTIME_CLASS(ACustomInventory)->Size) return 0; // This class cannot have user variables.
test = pickupstates;
}
else return 0; // something else derived from AStateProvider. We do not know what this may be.
for (; *test != NAME_None; test++)
{
FState *state = obj->FindState(*test);
while (state != nullptr && checked.CheckKey(state) == nullptr) // have we checked this state already. If yes, we can stop checking the current chain.
{
checked[state] = true;
if (state->ActionFunc && state->ActionFunc->Unsafe)
{
// If an unsafe function (i.e. one that accesses user variables) is being detected, print a warning once and remove the bogus function. We may not call it because that would inevitably crash.
auto owner = FState::StaticFindStateOwner(state);
Printf(TEXTCOLOR_RED "Unsafe state call in state %s.%d to %s, reached by %s.%s which accesses user variables.\n",
owner->TypeName.GetChars(), state - owner->OwnedStates, static_cast<VMScriptFunction *>(state->ActionFunc)->PrintableName.GetChars(), obj->TypeName.GetChars(), FName(*test).GetChars());
errors++;
}
state = state->NextState;
}
}
return errors;
}
//==========================================================================
//
// LoadActors
@ -253,6 +302,15 @@ void LoadActors ()
errorcount++;
continue;
}
if (ti->bDecorateClass && ti->IsDescendantOf(RUNTIME_CLASS(AStateProvider)))
{
// either a DECORATE based weapon or CustomInventory.
// These are subject to relaxed rules for user variables in states.
// Although there is a runtime check for bogus states, let's do a quick analysis if any of the known entry points
// hits an unsafe state. If we can find something here it can be handled wuth a compile error rather than a runtime error.
errorcount += CheckForUnsafeStates(ti);
}
}
if (errorcount > 0)
{

View file

@ -656,6 +656,7 @@ class VMFunction : public DObject
public:
bool Native;
bool Final = false; // cannot be overridden
bool Unsafe = false; // Contains references to class fields that are unsafe for psp and item state calls.
BYTE ImplicitArgs = 0; // either 0 for static, 1 for method or 3 for action
int VirtualIndex = -1;
FName Name;