From d714670acc85712ddecf0da39e03c051184cde67 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 22 Oct 2016 16:35:48 +0200 Subject: [PATCH] - 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. --- src/d_dehacked.cpp | 2 ++ src/dobjtype.h | 6 ++++++ src/info.cpp | 4 ++-- src/p_actionfunctions.cpp | 2 +- src/scripting/codegeneration/codegen.cpp | 9 +++------ src/scripting/vm/vm.h | 4 ++-- src/scripting/vm/vmbuilder.cpp | 1 + src/scripting/zscript/zcc_compile.cpp | 8 +++++--- 8 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 81ee907f4..66bdc652c 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -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); } } diff --git a/src/dobjtype.h b/src/dobjtype.h index e6f2a491b..c5d96972b 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -719,6 +719,12 @@ public: PClass *OwningClass = nullptr; unsigned AddVariant(PPrototype *proto, TArray &argflags, TArray &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(); diff --git a/src/info.cpp b/src/info.cpp index 44f485e6c..5e267b39c 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -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; diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index c8e110a68..bbeeca374 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -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) diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index dce724a32..1de5c13c2 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -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)) diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index b9dafde0c..a37b7449b 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -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 diff --git a/src/scripting/vm/vmbuilder.cpp b/src/scripting/vm/vmbuilder.cpp index a5f15b262..b58de0ea4 100644 --- a/src/scripting/vm/vmbuilder.cpp +++ b/src/scripting/vm/vmbuilder.cpp @@ -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); diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index ba038a716..9b4212820 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -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(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);