diff --git a/src/thingdef/thingdef.cpp b/src/thingdef/thingdef.cpp index a1350086b0..d1c76943a0 100644 --- a/src/thingdef/thingdef.cpp +++ b/src/thingdef/thingdef.cpp @@ -289,17 +289,15 @@ static void FinishThingdef() FStateTempCall *tcall = StateTempCalls[i]; VMFunction *func; - assert(tcall->Call != NULL); - if (tcall->Call->GetArgCount() == 0) - { - // There are no arguments, so we can call this function directly - // without wrapping it in an anonymous function. - func = tcall->Call->GetVMFunction(); - } - else + assert(tcall->Code != NULL); + + // Can we call this function directly without wrapping it in an + // anonymous function? e.g. Are we passing any parameters to it? + func = tcall->Code->GetDirectFunction(); + if (func == NULL) { FCompileContext ctx(tcall->ActorClass); - tcall->Call->Resolve(ctx); + tcall->Code->Resolve(ctx); VMFunctionBuilder buildit; // Allocate registers used to pass parameters in. @@ -307,7 +305,7 @@ static void FinishThingdef() buildit.Registers[REGT_POINTER].Get(3); // Emit a tail call via FxVMFunctionCall - tcall->Call->Emit(&buildit, true); + tcall->Code->Emit(&buildit, true); VMScriptFunction *sfunc = buildit.MakeFunction(); sfunc->NumArgs = NAP; @@ -322,8 +320,8 @@ static void FinishThingdef() codesize += sfunc->CodeSize; } } - delete tcall->Call; - tcall->Call = NULL; + delete tcall->Code; + tcall->Code = NULL; for (int k = 0; k < tcall->NumStates; ++k) { tcall->ActorClass->OwnedStates[tcall->FirstState + k].SetAction(func); diff --git a/src/thingdef/thingdef.h b/src/thingdef/thingdef.h index 70da997e36..ecec26775a 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -119,10 +119,10 @@ public: struct FStateTempCall { - FStateTempCall() : ActorClass(NULL), Call(NULL), FirstState(0), NumStates(0) {} + FStateTempCall() : ActorClass(NULL), Code(NULL), FirstState(0), NumStates(0) {} PClassActor *ActorClass; - class FxVMFunctionCall *Call; + class FxTailable *Code; int FirstState; int NumStates; }; @@ -189,7 +189,8 @@ AFuncDesc *FindFunction(const char * string); void ParseStates(FScanner &sc, PClassActor *actor, AActor *defaults, Baggage &bag); void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray &out_params, PFunction *afd, FString statestring, FStateDefinitions *statedef); -bool ParseAction(FScanner &sc, FState state, FString statestring, FStateTempCall *tcall, Baggage &bag); +void ParseActions(FScanner &sc, FState state, FString statestring, FStateTempCall *tcall, Baggage &bag); +class FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, Baggage &bag); PFunction *FindGlobalActionFunction(const char *name); diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index 8608557ef8..47cda7c464 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -782,28 +782,6 @@ public: ExpEmit Emit(VMFunctionBuilder *build); }; -//========================================================================== -// -// FxVMFunctionCall -// -//========================================================================== - -class FxVMFunctionCall : public FxExpression -{ - PFunction *Function; - FArgumentList *ArgList; - PType *ReturnType; - -public: - FxVMFunctionCall(PFunction *func, FArgumentList *args, const FScriptPosition &pos); - ~FxVMFunctionCall(); - FxExpression *Resolve(FCompileContext&); - virtual ExpEmit Emit(VMFunctionBuilder *build); - ExpEmit Emit(VMFunctionBuilder *build, bool tailcall); - unsigned GetArgCount() { return ArgList == NULL ? 0 : ArgList->Size(); } - VMFunction *GetVMFunction() { return Function->Variants[0].Implementation; } -}; - //========================================================================== // // FxGlobalFunctionCall @@ -823,6 +801,62 @@ public: ExpEmit Emit(VMFunctionBuilder *build); }; +//========================================================================== +// +// FxTailable +// +// An expression that can produce a tail call +// +//========================================================================== + +class FxTailable : public FxExpression +{ +public: + FxTailable(const FScriptPosition &pos) : FxExpression(pos) {} + virtual ExpEmit Emit(VMFunctionBuilder *build, bool tailcall) = 0; + ExpEmit Emit(VMFunctionBuilder *build); + virtual VMFunction *GetDirectFunction(); +}; + +//========================================================================== +// +// FxVMFunctionCall +// +//========================================================================== + +class FxVMFunctionCall : public FxTailable +{ + PFunction *Function; + FArgumentList *ArgList; + PType *ReturnType; + +public: + FxVMFunctionCall(PFunction *func, FArgumentList *args, const FScriptPosition &pos); + ~FxVMFunctionCall(); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build, bool tailcall); + unsigned GetArgCount() { return ArgList == NULL ? 0 : ArgList->Size(); } + VMFunction *GetVMFunction() { return Function->Variants[0].Implementation; } + VMFunction *GetDirectFunction(); +}; + +//========================================================================== +// +// FxSequence +// +//========================================================================== + +class FxSequence : public FxTailable +{ + TDeletingArray Expressions; + +public: + FxSequence(const FScriptPosition &pos) : FxTailable(pos) {} + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build, bool tailcall); + void Add(FxTailable *expr) { Expressions.Push(expr); } + VMFunction *GetDirectFunction(); +}; //========================================================================== // diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 1f704a1a46..c219c92da9 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -3004,6 +3004,28 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build) return dest; } +//========================================================================== +// +// +// +//========================================================================== + +ExpEmit FxTailable::Emit(VMFunctionBuilder *build) +{ + return Emit(build, false); +} + +//========================================================================== +// +// +// +//========================================================================== + +VMFunction *FxTailable::GetDirectFunction() +{ + return NULL; +} + //========================================================================== // // FxVMFunctionCall @@ -3011,7 +3033,7 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build) //========================================================================== FxVMFunctionCall::FxVMFunctionCall(PFunction *func, FArgumentList *args, const FScriptPosition &pos) -: FxExpression(pos) +: FxTailable(pos) { Function = func; ArgList = args; @@ -3082,15 +3104,10 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) // //========================================================================== -ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) -{ - return Emit(build, false); -} - ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build, bool tailcall) { assert(build->Registers[REGT_POINTER].GetMostUsed() >= 3); - int count = ArgList->Size(); + int count = GetArgCount(); // Emit code to pass implied parameters if (Function->Flags & VARF_Method) @@ -3105,9 +3122,12 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build, bool tailcall) count += 2; } // Emit code to pass explicit parameters - for (unsigned i = 0; i < ArgList->Size(); ++i) + if (ArgList != NULL) { - (*ArgList)[i]->Emit(build); + for (unsigned i = 0; i < ArgList->Size(); ++i) + { + (*ArgList)[i]->Emit(build); + } } // Get a constant register for this function int funcaddr = build->GetConstantAddress(Function->Variants[0].Implementation, ATAG_OBJECT); @@ -3132,6 +3152,24 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build, bool tailcall) } } +//========================================================================== +// +// FxVMFunctionCall :: GetDirectFunction +// +// If the function is not passed any explicit arguments, returns the +// function. Otherwise returns NULL. +// +//========================================================================== + +VMFunction *FxVMFunctionCall::GetDirectFunction() +{ + if (GetArgCount() == 0) + { + return GetVMFunction(); + } + return NULL; +} + //========================================================================== // // @@ -3221,6 +3259,58 @@ ExpEmit FxGlobalFunctionCall::Emit(VMFunctionBuilder *build) return v; } +//========================================================================== +// +// FxSequence :: Resolve +// +//========================================================================== + +FxExpression *FxSequence::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + for (unsigned i = 0; i < Expressions.Size(); ++i) + { + if (NULL == (Expressions[i] = static_cast(Expressions[i]->Resolve(ctx)))) + { + delete this; + return NULL; + } + } + return this; +} + +//========================================================================== +// +// FxSequence :: Emit +// +//========================================================================== + +ExpEmit FxSequence::Emit(VMFunctionBuilder *build, bool tailcall) +{ + for (unsigned i = 0; i < Expressions.Size(); ++i) + { + ExpEmit v = Expressions[i]->Emit(build, tailcall ? i == Expressions.Size()-1 : false); + // Throw away any result. We don't care about it. + v.Free(build); + } + return ExpEmit(); +} + +//========================================================================== +// +// FxSequence :: GetDirectFunction +// +//========================================================================== + +VMFunction *FxSequence::GetDirectFunction() +{ + if (Expressions.Size() == 1) + { + return Expressions[0]->GetDirectFunction(); + } + return NULL; +} + //========================================================================== // //========================================================================== diff --git a/src/thingdef/thingdef_states.cpp b/src/thingdef/thingdef_states.cpp index aab66f4ed4..94f5de1bc6 100644 --- a/src/thingdef/thingdef_states.cpp +++ b/src/thingdef/thingdef_states.cpp @@ -314,10 +314,8 @@ do_stop: continue; } - if (ParseAction(sc, state, statestring, tcall, bag)) - { - goto endofstate; - } + ParseActions(sc, state, statestring, tcall, bag); + goto endofstate; } sc.UnGet(); endofstate: @@ -327,7 +325,7 @@ endofstate: sc.ScriptError ("Invalid frame character string '%s'", statestring.GetChars()); count = -count; } - if (tcall->Call != NULL) + if (tcall->Code != NULL) { tcall->ActorClass = actor; tcall->FirstState = bag.statedef.GetStateCount() - count; @@ -348,21 +346,51 @@ endofstate: sc.SetEscape(true); // re-enable escape sequences } +//========================================================================== +// +// ParseActions +// +//========================================================================== + +void ParseActions(FScanner &sc, FState state, FString statestring, FStateTempCall *tcall, Baggage &bag) +{ + // If it's not a '{', then it should be a single action. + // Otherwise, it's a sequence of actions. + if (!sc.Compare("{")) + { + tcall->Code = ParseAction(sc, state, statestring, bag); + return; + } + + FxSequence *seq = new FxSequence(sc); + sc.MustGetString(); + while (!sc.Compare("}")) + { + FxVMFunctionCall *call = ParseAction(sc, state, statestring, bag); + seq->Add(call); + sc.MustGetStringName(";"); + sc.MustGetString(); + } + tcall->Code = seq; +} + //========================================================================== // // ParseAction // //========================================================================== -bool ParseAction(FScanner &sc, FState state, FString statestring, FStateTempCall *tcall, Baggage &bag) +FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, Baggage &bag) { + FxVMFunctionCall *call; + // Make the action name lowercase strlwr (sc.String); - tcall->Call = DoActionSpecials(sc, state, bag); - if (tcall->Call != NULL) + call = DoActionSpecials(sc, state, bag); + if (call != NULL) { - return true; + return call; } PFunction *afd = dyn_cast(bag.Info->Symbols.FindSymbol(FName(sc.String, true), true)); @@ -370,15 +398,15 @@ bool ParseAction(FScanner &sc, FState state, FString statestring, FStateTempCall { FArgumentList *args = new FArgumentList; ParseFunctionParameters(sc, bag.Info, *args, afd, statestring, &bag.statedef); - tcall->Call = new FxVMFunctionCall(afd, args->Size() > 0 ? args : NULL, sc); + call = new FxVMFunctionCall(afd, args->Size() > 0 ? args : NULL, sc); if (args->Size() == 0) { delete args; } - return true; + return call; } sc.ScriptError("Invalid state parameter %s\n", sc.String); - return false; + return NULL; } //==========================================================================