- allow calling non-action functions directly from a state.

Ironically this only requires a very minor change in the calling code and an added member for the VMFunction to tell that code how many parameters to pass.
This change will allow to turn the vast majority of action functions into regular members, the only ones that still need to be an action function are the few that actually use the pointers.
This commit is contained in:
Christoph Oelckers 2016-10-22 16:35:48 +02:00
parent d6047ae651
commit d714670acc
8 changed files with 22 additions and 14 deletions

View file

@ -823,7 +823,9 @@ void SetDehParams(FState *state, int codepointer)
VMScriptFunction *sfunc = new VMScriptFunction;
buildit.MakeFunction(sfunc);
sfunc->NumArgs = NAP;
sfunc->ImplicitArgs = NAP;
state->SetAction(sfunc);
sfunc->PrintableName.Format("Dehacked.%s.%d.%d", MBFCodePointers[codepointer].name.GetChars(), value1, value2);
}
}

View file

@ -719,6 +719,12 @@ public:
PClass *OwningClass = nullptr;
unsigned AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArray<FName> &argnames, VMFunction *impl, int flags);
int GetImplicitArgs()
{
if (Variants[0].Flags & VARF_Action) return 3;
else if (Variants[0].Flags & VARF_Method) return 1;
return 0;
}
size_t PropagateMark();

View file

@ -94,13 +94,13 @@ bool FState::CallAction(AActor *self, AActor *stateowner, FStateParamInfo *info,
}
if (stateret == NULL)
{
stack.Call(ActionFunc, params, countof(params), NULL, 0, NULL);
stack.Call(ActionFunc, params, ActionFunc->ImplicitArgs, NULL, 0, NULL);
}
else
{
VMReturn ret;
ret.PointerAt((void **)stateret);
stack.Call(ActionFunc, params, countof(params), &ret, 1, NULL);
stack.Call(ActionFunc, params, ActionFunc->ImplicitArgs, &ret, 1, NULL);
}
ActionCycles.Unclock();
return true;

View file

@ -168,7 +168,7 @@ bool ACustomInventory::CallStateChain (AActor *actor, FState *state)
numret = 2;
}
}
stack.Call(state->ActionFunc, params, countof(params), wantret, numret);
stack.Call(state->ActionFunc, params, state->ActionFunc->ImplicitArgs, wantret, numret);
// As long as even one state succeeds, the whole chain succeeds unless aborted below.
// A state that wants to jump does not count as "succeeded".
if (nextstate == NULL)

View file

@ -5196,11 +5196,11 @@ PPrototype *FxVMFunctionCall::ReturnProto()
VMFunction *FxVMFunctionCall::GetDirectFunction()
{
// If this return statement calls a function with no arguments,
// If this return statement calls a non-virtual function with no arguments,
// then it can be a "direct" function. That is, the DECORATE
// definition can call that function directly without wrapping
// it inside VM code.
if ((ArgList ? ArgList->Size() : 0) == 0 && (Function->Variants[0].Flags & VARF_Action))
if ((ArgList ? ArgList->Size() : 0) == 0 && !(Function->Variants[0].Flags & VARF_Virtual))
{
return Function->Variants[0].Implementation;
}
@ -5222,10 +5222,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
auto proto = Function->Variants[0].Proto;
auto argtypes = proto->ArgumentTypes;
int implicit;
if (Function->Variants[0].Flags & VARF_Action) implicit = 3;
else if (Function->Variants[0].Flags & VARF_Method) implicit = 1;
else implicit = 0;
int implicit = Function->GetImplicitArgs();
// This should never happen.
if (Self == nullptr && !(Function->Variants[0].Flags & VARF_Static))

View file

@ -179,12 +179,12 @@ class VMFunction : public DObject
HAS_OBJECT_POINTERS;
public:
bool Native;
BYTE ImplicitArgs = 0; // either 0 for static, 1 for method or 3 for action
FName Name;
class PPrototype *Proto;
VMFunction() : Native(false), Name(NAME_None), Proto(NULL) {}
VMFunction(FName name) : Native(false), Name(name), Proto(NULL) {}
VMFunction(FName name = NAME_None) : Native(false), ImplicitArgs(0), Name(name), Proto(NULL) {}
};
enum EVMOpMode

View file

@ -665,6 +665,7 @@ VMFunction *FFunctionBuildList::AddFunction(PFunction *functype, FxExpression *c
it.Code = code;
it.PrintableName = name;
it.Function = new VMScriptFunction;
it.Function->ImplicitArgs = functype->GetImplicitArgs();
it.Proto = nullptr;
it.FromDecorate = fromdecorate;
mItems.Push(it);

View file

@ -1951,14 +1951,15 @@ void ZCCCompiler::InitFunctions()
f->Flags &= notallowed;
}
uint32_t varflags = VARF_Method;
int implicitargs = 1;
AFuncDesc *afd = nullptr;
// map to implementation flags.
if (f->Flags & ZCC_Private) varflags |= VARF_Private;
if (f->Flags & ZCC_Protected) varflags |= VARF_Protected;
if (f->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated;
if (f->Flags & ZCC_Action) varflags |= VARF_Action|VARF_Final; // Action implies Final.
if (f->Flags & ZCC_Static) varflags = (varflags & ~VARF_Method) | VARF_Final; // Static implies Final.
if (f->Flags & ZCC_Action) varflags |= VARF_Action|VARF_Final, implicitargs = 3; // Action implies Final.
if (f->Flags & ZCC_Static) varflags = (varflags & ~VARF_Method) | VARF_Final, implicitargs = 0; // Static implies Final.
if ((f->Flags & (ZCC_Action | ZCC_Static)) == (ZCC_Action | ZCC_Static))
{
Error(f, "%s: Action and Static on the same function is not allowed.", FName(f->Name).GetChars());
@ -1973,6 +1974,7 @@ void ZCCCompiler::InitFunctions()
{
Error(f, "The function '%s' has not been exported from the executable.", FName(f->Name).GetChars());
}
(*afd->VMPointer)->ImplicitArgs = BYTE(implicitargs);
}
SetImplicitArgs(&args, &argflags, &argnames, c->Type(), varflags);
auto p = f->Params;
@ -2063,7 +2065,7 @@ FxExpression *ZCCCompiler::SetupActionFunction(PClassActor *cls, ZCC_TreeNode *a
PFunction *afd = dyn_cast<PFunction>(cls->Symbols.FindSymbol(id->Identifier, true));
if (afd != nullptr)
{
if (fc->Parameters == nullptr && (afd->Variants[0].Flags & VARF_Action))
if (fc->Parameters == nullptr && !(afd->Variants[0].Flags & VARF_Virtual))
{
// We can use this function directly without wrapping it in a caller.
return new FxVMFunctionCall(new FxSelf(*af), afd, nullptr, *af, false);