- more preparations to compile functions:

* Allow PFunction to work without a VMFunction being attached.
 * The Variant for a function must store the prototype itself instead of relying on the VMFunction it points to. Otherwise it would not be possible to reference a prototype during compilation of the function because it does not exist yet.
 * Give the variant a list of the function's argument's names, because these are also needed to compile the function.
 * create an anonymous function symbol when the function gets registered to the builder. At this point we have all the needed information to set it up correctly, but later this is no longer the case. This is the most convenient info to have here because it contains everything that's needed to compile the function in the proper context, so it has to be present when starting compilation.
 * added some preparations to implement special handling for weapons and custom inventory items, which can run action functions in another actor's context. This part is not active yet but the basics are present in SetImplicitArgs.
This commit is contained in:
Christoph Oelckers 2016-10-15 14:36:08 +02:00
parent 2cb5839c11
commit 32a3f57a54
13 changed files with 91 additions and 69 deletions

View file

@ -2490,14 +2490,14 @@ size_t PFunction::PropagateMark()
// //
//========================================================================== //==========================================================================
unsigned PFunction::AddVariant(PPrototype *proto, TArray<DWORD> &argflags, VMFunction *impl) unsigned PFunction::AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArray<FName> &argnames, VMFunction *impl)
{ {
Variant variant; Variant variant;
//variant.Proto = proto; variant.Proto = proto;
variant.ArgFlags = argflags; variant.ArgFlags = argflags;
variant.Implementation = impl; variant.Implementation = impl;
impl->Proto = proto; if (impl != nullptr) impl->Proto = proto;
return Variants.Push(variant); return Variants.Push(variant);
} }

View file

@ -692,14 +692,15 @@ class PFunction : public PSymbol
public: public:
struct Variant struct Variant
{ {
//PPrototype *Proto; PPrototype *Proto;
VMFunction *Implementation; VMFunction *Implementation;
TArray<DWORD> ArgFlags; // Should be the same length as Proto->ArgumentTypes TArray<DWORD> ArgFlags; // Should be the same length as Proto->ArgumentTypes
TArray<FName> ArgNames; // we need the names to access them later when the function gets compiled.
}; };
TArray<Variant> Variants; TArray<Variant> Variants;
DWORD Flags; DWORD Flags;
unsigned AddVariant(PPrototype *proto, TArray<DWORD> &argflags, VMFunction *impl); unsigned AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArray<FName> &argnames, VMFunction *impl);
size_t PropagateMark(); size_t PropagateMark();

View file

@ -717,7 +717,7 @@ xx(Max_10_Exp)
// implicit function parameters // implicit function parameters
xx(self) xx(self)
xx(caller) xx(invoker)
xx(stateinfo) xx(stateinfo)
xx(__decorate_internal_int__) xx(__decorate_internal_int__)

View file

@ -3883,7 +3883,7 @@ FxVMFunctionCall::~FxVMFunctionCall()
PPrototype *FxVMFunctionCall::ReturnProto() PPrototype *FxVMFunctionCall::ReturnProto()
{ {
EmitTail = true; EmitTail = true;
return Function->Variants[0].Implementation->Proto; return Function->Variants[0].Proto;
} }
//========================================================================== //==========================================================================
@ -3916,7 +3916,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
{ {
CHECKRESOLVED(); CHECKRESOLVED();
bool failed = false; bool failed = false;
auto proto = Function->Variants[0].Implementation->Proto; auto proto = Function->Variants[0].Proto;
auto argtypes = proto->ArgumentTypes; auto argtypes = proto->ArgumentTypes;
int implicit; int implicit;

View file

@ -458,6 +458,7 @@ void ParseFunctionDef(FScanner &sc, PClassActor *cls, FName funcname,
const AFuncDesc *afd; const AFuncDesc *afd;
TArray<PType *> args; TArray<PType *> args;
TArray<DWORD> argflags; TArray<DWORD> argflags;
TArray<FName> argnames;
afd = FindFunction(funcname); afd = FindFunction(funcname);
if (afd == NULL) if (afd == NULL)
@ -467,13 +468,14 @@ void ParseFunctionDef(FScanner &sc, PClassActor *cls, FName funcname,
} }
sc.MustGetToken('('); sc.MustGetToken('(');
SetImplicitArgs(&args, &argflags, cls, funcflags); SetImplicitArgs(&args, &argflags, &argnames, cls, funcflags);
// This function will be removed once all internal classes have been ported so don't bother filling in the function's argument names, because for anything going through here they'll never be used.
ParseArgListDef(sc, cls, args, argflags); ParseArgListDef(sc, cls, args, argflags);
if (afd != NULL) if (afd != NULL)
{ {
PFunction *sym = new PFunction(funcname); PFunction *sym = new PFunction(funcname);
sym->AddVariant(NewPrototype(rets, args), argflags, *(afd->VMPointer)); sym->AddVariant(NewPrototype(rets, args), argflags, argnames, *(afd->VMPointer));
sym->Flags = funcflags; sym->Flags = funcflags;
if (cls->Symbols.AddSymbol(sym) == NULL) if (cls->Symbols.AddSymbol(sym) == NULL)
{ {

View file

@ -322,7 +322,8 @@ do_stop:
endofstate: endofstate:
if (ScriptCode != nullptr) if (ScriptCode != nullptr)
{ {
state.ActionFunc = FunctionBuildList.AddFunction(actor, ScriptCode, FStringf("%s.StateFunction.%d", actor->TypeName.GetChars(), bag.statedef.GetStateCount()), true); auto funcsym = CreateAnonymousFunction(actor, nullptr, VARF_Method | VARF_Action);
state.ActionFunc = FunctionBuildList.AddFunction(funcsym, ScriptCode, FStringf("%s.StateFunction.%d", actor->TypeName.GetChars(), bag.statedef.GetStateCount()), true);
} }
int count = bag.statedef.AddStates(&state, statestring); int count = bag.statedef.AddStates(&state, statestring);
if (count < 0) if (count < 0)
@ -595,7 +596,7 @@ FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, B
void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression *> &out_params, void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression *> &out_params,
PFunction *afd, FString statestring, FStateDefinitions *statedef) PFunction *afd, FString statestring, FStateDefinitions *statedef)
{ {
const TArray<PType *> &params = afd->Variants[0].Implementation->Proto->ArgumentTypes; const TArray<PType *> &params = afd->Variants[0].Proto->ArgumentTypes;
const TArray<DWORD> &paramflags = afd->Variants[0].ArgFlags; const TArray<DWORD> &paramflags = afd->Variants[0].ArgFlags;
int numparams = (int)params.Size(); int numparams = (int)params.Size();
int pnum = 0; int pnum = 0;

View file

@ -80,34 +80,74 @@ PClassActor *QuestItemClasses[31];
// //
//========================================================================== //==========================================================================
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, PClass *cls, DWORD funcflags) void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FName> *argnames, PClass *cls, DWORD funcflags)
{ {
// Must be called before adding any other arguments. // Must be called before adding any other arguments.
assert(args == NULL || args->Size() == 0); assert(args == nullptr || args->Size() == 0);
assert(argflags == NULL || argflags->Size() == 0); assert(argflags == nullptr || argflags->Size() == 0);
if (funcflags & VARF_Method) if (funcflags & VARF_Method)
{ {
// implied self pointer // implied self pointer
if (args != NULL) args->Push(NewClassPointer(cls)); if (args != nullptr) args->Push(cls);
if (argflags != NULL) argflags->Push(VARF_Implicit); if (argflags != nullptr) argflags->Push(VARF_Implicit);
if (argnames != nullptr) argnames->Push(NAME_self);
} }
if (funcflags & VARF_Action) if (funcflags & VARF_Action)
{ {
// implied caller and callingstate pointers // implied caller and callingstate pointers
if (args != NULL) if (args != nullptr)
{ {
args->Insert(0, NewClassPointer(RUNTIME_CLASS(AActor))); // the caller must go before self due to an old design mistake. // Special treatment for weapons and CustomInventorys: 'self' is not the defining class but the actual user of the item, so this pointer must be of type 'Actor'
args->Push(TypeState); /* if (cls->IsDescendantOf(RUNTIME_CLASS(AStateProvider)))
{
args->Insert(0, RUNTIME_CLASS(AActor)); // this must go in before the real pointer to the containing class.
}
else*/
{
args->Push(cls);
}
args->Push(TypeState/*Info*/); // fixme: TypeState is not the correct type here!!!
} }
if (argflags != NULL) if (argflags != nullptr)
{ {
argflags->Push(VARF_Implicit); argflags->Push(VARF_Implicit);
argflags->Push(VARF_Implicit); argflags->Push(VARF_Implicit);
} }
if (argnames != nullptr)
{
argnames->Push(NAME_invoker);
argnames->Push(NAME_stateinfo);
}
} }
} }
//==========================================================================
//
// CreateAnonymousFunction
//
// Creates a function symbol for an anonymous function
// This contains actual info about the implied variables which is needed
// during code generation.
//
//==========================================================================
PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, int flags)
{
TArray<PType *> rets(1);
TArray<PType *> args;
TArray<uint32_t> argflags;
TArray<FName> argnames;
rets[0] = returntype != nullptr? returntype : TypeError; // Use TypeError as placeholder if we do not know the return type yet.
SetImplicitArgs(&args, &argflags, &argnames, containingclass, flags);
PFunction *sym = new PFunction(NAME_None); // anonymous functions do not have names.
sym->AddVariant(NewPrototype(rets, args), argflags, argnames, nullptr);
sym->Flags = VARF_Action;
return sym;
}
//========================================================================== //==========================================================================
// //
// LoadActors // LoadActors

View file

@ -150,7 +150,8 @@ void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression
FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &endswithret); FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &endswithret);
class FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, Baggage &bag); class FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, Baggage &bag);
FName CheckCastKludges(FName in); FName CheckCastKludges(FName in);
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, PClass *cls, DWORD funcflags); void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FName> *argnames, PClass *cls, DWORD funcflags);
PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, int flags);
//========================================================================== //==========================================================================
// //

View file

@ -657,7 +657,8 @@ DEFINE_PROPERTY(damage, X, Actor)
} }
else else
{ {
defaults->DamageFunc = FunctionBuildList.AddFunction(bag.Info, id, FStringf("%s.DamageFunction", bag.Info->TypeName.GetChars()), false); auto funcsym = CreateAnonymousFunction(bag.Info, TypeSInt32, VARF_Method);
defaults->DamageFunc = FunctionBuildList.AddFunction(funcsym, id, FStringf("%s.DamageFunction", bag.Info->TypeName.GetChars()), false);
} }
} }

View file

@ -649,7 +649,7 @@ void VMFunctionBuilder::BackpatchToHere(size_t loc)
//========================================================================== //==========================================================================
FFunctionBuildList FunctionBuildList; FFunctionBuildList FunctionBuildList;
VMFunction *FFunctionBuildList::AddFunction(PClass *cls, FxExpression *code, const FString &name, bool statecall) VMFunction *FFunctionBuildList::AddFunction(PFunction *functype, FxExpression *code, const FString &name, bool statecall)
{ {
auto func = code->GetDirectFunction(); auto func = code->GetDirectFunction();
if (func != nullptr) if (func != nullptr)
@ -661,7 +661,7 @@ VMFunction *FFunctionBuildList::AddFunction(PClass *cls, FxExpression *code, con
//Printf("Adding %s\n", name.GetChars()); //Printf("Adding %s\n", name.GetChars());
Item it; Item it;
it.Class = cls; it.Func = functype;
it.Code = code; it.Code = code;
it.DumpName = name; it.DumpName = name;
it.Function = new VMScriptFunction; it.Function = new VMScriptFunction;
@ -684,8 +684,11 @@ void FFunctionBuildList::Build()
{ {
assert(item.Code != NULL); assert(item.Code != NULL);
// This needs to be fixed, so that the compile context receives the entire function symbol, including the containing class, the prototype and argument names, which will be needed to run the code generator
// As a first step this just needs to get working so fetch the class type from the prototype's argument list.
auto cls = static_cast<PClass*>(item.Func->Variants[0].Proto->ArgumentTypes[!!(item.Func->Flags & VARF_Action)]);
// We don't know the return type in advance for anonymous functions. // We don't know the return type in advance for anonymous functions.
FCompileContext ctx(item.Class, nullptr); FCompileContext ctx(cls, nullptr);
item.Code = item.Code->Resolve(ctx); item.Code = item.Code->Resolve(ctx);
item.Proto = ctx.ReturnProto; item.Proto = ctx.ReturnProto;
@ -695,32 +698,15 @@ void FFunctionBuildList::Build()
VMFunctionBuilder buildit(true); VMFunctionBuilder buildit(true);
assert(item.Proto != nullptr); assert(item.Proto != nullptr);
auto numargs = item.Func->Variants[0].Proto->ArgumentTypes.Size();
int numargs;
int flags;
// Kludge alert. This needs to be done in a more universal fashion.
// Right now there's only action and damage functions, so for the time being
// this will do to get the whole thing started first.
if (item.type == 1) // anonymous action function
{
numargs = NAP;
flags = VARF_Method | VARF_Action;
}
else
{
numargs = 1;
flags = VARF_Method;
}
// Generate prototype for this anonymous function // Generate prototype for this anonymous function
// Fixme: This later needs to do proper allocation for the function's entire argument list, once non-anonymous functions can be done.
buildit.Registers[REGT_POINTER].Get(numargs); buildit.Registers[REGT_POINTER].Get(numargs);
TArray<PType *> args(numargs);
SetImplicitArgs(&args, nullptr, item.Class, flags);
VMScriptFunction *sfunc = item.Function; VMScriptFunction *sfunc = item.Function;
item.Function->Proto = NewPrototype(item.Proto->ReturnTypes, args); // create a new prototype from the now known return type and the argument list of the function's template prototype.
item.Function->Proto = NewPrototype(item.Proto->ReturnTypes, item.Func->Variants[0].Proto->ArgumentTypes);
// Emit code // Emit code
item.Code->Emit(&buildit); item.Code->Emit(&buildit);
@ -731,7 +717,7 @@ void FFunctionBuildList::Build()
{ {
char label[64]; char label[64];
int labellen = mysnprintf(label, countof(label), item.DumpName, int labellen = mysnprintf(label, countof(label), item.DumpName,
item.Class->TypeName.GetChars()); cls->TypeName.GetChars());
DumpFunction(dump, sfunc, label, labellen); DumpFunction(dump, sfunc, label, labellen);
codesize += sfunc->CodeSize; codesize += sfunc->CodeSize;
} }

View file

@ -101,7 +101,7 @@ class FFunctionBuildList
{ {
struct Item struct Item
{ {
PClass *Class = nullptr; PFunction *Func = nullptr;
FxExpression *Code = nullptr; FxExpression *Code = nullptr;
PPrototype *Proto = nullptr; PPrototype *Proto = nullptr;
VMScriptFunction *Function = nullptr; VMScriptFunction *Function = nullptr;
@ -112,7 +112,7 @@ class FFunctionBuildList
TArray<Item> mItems; TArray<Item> mItems;
public: public:
VMFunction *AddFunction(PClass *cls, FxExpression *code, const FString &name, bool statecall = false); VMFunction *AddFunction(PFunction *func, FxExpression *code, const FString &name, bool statecall = false);
void Build(); void Build();
}; };

View file

@ -1870,7 +1870,7 @@ void ZCCCompiler::InitFunctions()
TArray<PType *> rets(1); TArray<PType *> rets(1);
TArray<PType *> args; TArray<PType *> args;
TArray<uint32_t> argflags; TArray<uint32_t> argflags;
TArray<ENamedName> argnames; TArray<FName> argnames;
for (auto c : Classes) for (auto c : Classes)
{ {
@ -1925,20 +1925,7 @@ void ZCCCompiler::InitFunctions()
Error(f, "The function '%s' has not been exported from the executable.", FName(f->Name).GetChars()); Error(f, "The function '%s' has not been exported from the executable.", FName(f->Name).GetChars());
} }
} }
SetImplicitArgs(&args, &argflags, c->Type(), varflags); SetImplicitArgs(&args, &argflags, &argnames, c->Type(), varflags);
// Give names to the implicit parameters.
// Note that 'self' is the second argument on action functions, because this is the one referring to the owning class.
if (varflags & VARF_Action)
{
argnames.Push(NAME_caller);
argnames.Push(NAME_self);
argnames.Push(NAME_stateinfo);
}
else if (varflags & VARF_Method)
{
argnames.Push(NAME_self);
}
auto p = f->Params; auto p = f->Params;
if (p != nullptr) if (p != nullptr)
{ {
@ -1974,7 +1961,7 @@ void ZCCCompiler::InitFunctions()
} }
PFunction *sym = new PFunction(f->Name); PFunction *sym = new PFunction(f->Name);
sym->AddVariant(NewPrototype(rets, args), argflags, afd == nullptr? nullptr : *(afd->VMPointer)); sym->AddVariant(NewPrototype(rets, args), argflags, argnames, afd == nullptr? nullptr : *(afd->VMPointer));
sym->Flags = varflags; sym->Flags = varflags;
c->Type()->Symbols.ReplaceSymbol(sym); c->Type()->Symbols.ReplaceSymbol(sym);
@ -2025,7 +2012,7 @@ FxExpression *ZCCCompiler::SetupActionFunction(PClassActor *cls, ZCC_TreeNode *a
{ {
if (fc->Parameters == nullptr && (afd->Flags & VARF_Action)) if (fc->Parameters == nullptr && (afd->Flags & VARF_Action))
{ {
// This is the simple case which doesn't require work on the tree. // We can use this function directly without wrapping it in a caller.
return new FxVMFunctionCall(afd, nullptr, *af, true); return new FxVMFunctionCall(afd, nullptr, *af, true);
} }
} }
@ -2040,6 +2027,7 @@ FxExpression *ZCCCompiler::SetupActionFunction(PClassActor *cls, ZCC_TreeNode *a
// Action specials fall through to the code generator. // Action specials fall through to the code generator.
} }
} }
ConvertClass = cls;
return ConvertAST(af); return ConvertAST(af);
//Error(af, "Complex action functions not supported yet."); //Error(af, "Complex action functions not supported yet.");
@ -2173,7 +2161,8 @@ void ZCCCompiler::CompileStates()
auto code = SetupActionFunction(static_cast<PClassActor *>(c->Type()), sl->Action); auto code = SetupActionFunction(static_cast<PClassActor *>(c->Type()), sl->Action);
if (code != nullptr) if (code != nullptr)
{ {
state.ActionFunc = FunctionBuildList.AddFunction(c->Type(), code, FStringf("%s.StateFunction.%d", c->Type()->TypeName.GetChars(), statedef.GetStateCount()), true); auto funcsym = CreateAnonymousFunction(c->Type(), nullptr, VARF_Method | VARF_Action);
state.ActionFunc = FunctionBuildList.AddFunction(funcsym, code, FStringf("%s.StateFunction.%d", c->Type()->TypeName.GetChars(), statedef.GetStateCount()), true);
} }
} }
@ -2289,10 +2278,10 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
case AST_ExprFuncCall: case AST_ExprFuncCall:
{ {
auto fcall = static_cast<ZCC_ExprFuncCall *>(ast); auto fcall = static_cast<ZCC_ExprFuncCall *>(ast);
//return ConvertFunctionCall(fcall);
assert(fcall->Function->NodeType == AST_ExprID); // of course this cannot remain. Right now nothing more complex can come along but later this will have to be decomposed into 'self' and the actual function name. assert(fcall->Function->NodeType == AST_ExprID); // of course this cannot remain. Right now nothing more complex can come along but later this will have to be decomposed into 'self' and the actual function name.
auto fname = static_cast<ZCC_ExprID *>(fcall->Function)->Identifier; auto fname = static_cast<ZCC_ExprID *>(fcall->Function)->Identifier;
return new FxFunctionCall(nullptr, fname, ConvertNodeList(fcall->Parameters), *ast); return new FxFunctionCall(nullptr, fname, ConvertNodeList(fcall->Parameters), *ast);
//return ConvertFunctionCall(fcall->Function, ConvertNodeList(fcall->Parameters), ConvertClass, *ast);
} }
case AST_FuncParm: case AST_FuncParm:

View file

@ -143,6 +143,7 @@ private:
FArgumentList *ConvertNodeList(ZCC_TreeNode *head); FArgumentList *ConvertNodeList(ZCC_TreeNode *head);
DObject *Outer; DObject *Outer;
PClass *ConvertClass; // class type to be used when resoving symbold while converting an AST
PSymbolTable *GlobalTreeNodes; PSymbolTable *GlobalTreeNodes;
PSymbolTable *OutputSymbols; PSymbolTable *OutputSymbols;
ZCC_AST &AST; ZCC_AST &AST;