- give PFunction a bit more information:

* explicitly require passing the owning class when creating it.
 * extract self pointer class when adding a variant.
 * put the flags on the single variants, we can not fully rule out that they will be 100% identical, if variants ever get allowed.
This commit is contained in:
Christoph Oelckers 2016-10-15 15:50:45 +02:00
parent 091da92819
commit db8ab1bc4a
10 changed files with 196 additions and 33 deletions

View file

@ -2119,7 +2119,7 @@ static int PatchCodePtrs (int dummy)
else
{
TArray<DWORD> &args = sym->Variants[0].ArgFlags;
if ((sym->Flags & (VARF_Method | VARF_Action)) != (VARF_Method | VARF_Action) || (args.Size() > NAP && !(args[NAP] & VARF_Optional)))
if ((sym->Variants[0].Flags & (VARF_Method | VARF_Action)) != (VARF_Method | VARF_Action) || (args.Size() > NAP && !(args[NAP] & VARF_Optional)))
{
Printf("Frame %d: Incompatible code pointer '%s'\n", frame, Line2);
sym = NULL;
@ -2731,7 +2731,7 @@ static bool LoadDehSupp ()
else
{
TArray<DWORD> &args = sym->Variants[0].ArgFlags;
if ((sym->Flags & (VARF_Method|VARF_Action)) != (VARF_Method | VARF_Action) || (args.Size() > NAP && !(args[NAP] & VARF_Optional)))
if ((sym->Variants[0].Flags & (VARF_Method|VARF_Action)) != (VARF_Method | VARF_Action) || (args.Size() > NAP && !(args[NAP] & VARF_Optional)))
{
sc.ScriptMessage("Incompatible code pointer '%s'", sc.String);
}

View file

@ -2475,7 +2475,7 @@ size_t PFunction::PropagateMark()
{
for (unsigned i = 0; i < Variants.Size(); ++i)
{
//GC::Mark(Variants[i].Proto);
GC::Mark(Variants[i].Proto);
GC::Mark(Variants[i].Implementation);
}
return Variants.Size() * sizeof(Variants[0]) + Super::PropagateMark();
@ -2490,14 +2490,29 @@ size_t PFunction::PropagateMark()
//
//==========================================================================
unsigned PFunction::AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArray<FName> &argnames, VMFunction *impl)
unsigned PFunction::AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArray<FName> &argnames, VMFunction *impl, int flags)
{
Variant variant;
// I do not think we really want to deal with overloading here...
assert(Variants.Size() == 0);
variant.Flags = flags;
variant.Proto = proto;
variant.ArgFlags = argflags;
variant.Implementation = impl;
if (impl != nullptr) impl->Proto = proto;
// SelfClass can differ from OwningClass, but this is variant-dependent.
// Unlike the owner there can be cases where different variants can have different SelfClasses.
// (Of course only if this ever gets enabled...)
if (flags & VARF_Method)
{
assert(proto->ArgumentTypes.Size() > 0);
variant.SelfClass = dyn_cast<PClass>(proto->ArgumentTypes[0]);
assert(variant.SelfClass != nullptr);
}
return Variants.Push(variant);
}

View file

@ -696,16 +696,17 @@ public:
VMFunction *Implementation;
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.
DWORD Flags;
PClass *SelfClass;
};
TArray<Variant> Variants;
DWORD Flags;
PClass *OwningClass = nullptr;
unsigned AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArray<FName> &argnames, VMFunction *impl);
unsigned AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArray<FName> &argnames, VMFunction *impl, int flags);
size_t PropagateMark();
PFunction(FName name) : PSymbol(name), Flags(0) {}
PFunction() : PSymbol(NAME_None), Flags(0) {}
PFunction(PClass *owner = nullptr, FName name = NAME_None) : PSymbol(name), OwningClass(owner) {}
};
// Meta-info for every class derived from DObject ---------------------------

View file

@ -3898,7 +3898,7 @@ VMFunction *FxVMFunctionCall::GetDirectFunction()
// 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->Flags & VARF_Action))
if ((ArgList ? ArgList->Size() : 0) == 0 && (Function->Variants[0].Flags & VARF_Action))
{
return Function->Variants[0].Implementation;
}
@ -3920,8 +3920,8 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
auto argtypes = proto->ArgumentTypes;
int implicit;
if (Function->Flags & VARF_Action) implicit = 3;
else if (Function->Flags & VARF_Method) implicit = 1;
if (Function->Variants[0].Flags & VARF_Action) implicit = 3;
else if (Function->Variants[0].Flags & VARF_Method) implicit = 1;
else implicit = 0;
if (ArgList != NULL)
@ -4014,15 +4014,15 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
// For ZSCRIPT 'self' is properly used for the state's owning actor, meaning we have to pass the second argument here.
// If both functions are non-action or both are action, there is no need for special treatment.
if (!OwnerIsSelf || (!!(Function->Flags & VARF_Action) == build->IsActionFunc))
if (!OwnerIsSelf || (!!(Function->Variants[0].Flags & VARF_Action) == build->IsActionFunc))
{
// Emit code to pass implied parameters
if (Function->Flags & VARF_Method)
if (Function->Variants[0].Flags & VARF_Method)
{
build->Emit(OP_PARAM, 0, REGT_POINTER, 0);
count += 1;
}
if (Function->Flags & VARF_Action)
if (Function->Variants[0].Flags & VARF_Action)
{
static_assert(NAP == 3, "This code needs to be updated if NAP changes");
if (build->IsActionFunc)
@ -4041,17 +4041,14 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
}
else
{
// There are two possibilities here:
// Calling a non-action function from an action function.
// In that case the 'stateowner' pointer needs to be used as self.
if (build->IsActionFunc && (Function->Flags & VARF_Method))
if (build->IsActionFunc && (Function->Variants[0].Flags & VARF_Method))
{
build->Emit(OP_PARAM, 0, REGT_POINTER, 1);
build->Emit(OP_PARAM, 0, REGT_POINTER, 0);
count += 1;
}
// and calling an action function from a non-action function.
// This must be blocked because it lacks crucial information.
if (!build->IsActionFunc && (Function->Flags & VARF_Action))
if (!build->IsActionFunc && (Function->Variants[0].Flags & VARF_Action))
{
// This case should be eliminated in the analyzing stage.
I_Error("Cannot call action function from non-action functions.");

View file

@ -0,0 +1,153 @@
#if 0
/*
** cg_functioncall.cpp
**
**---------------------------------------------------------------------------
** Copyright 2016 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "doomtype.h"
#include "codegen.h"
#include "thingdef.h"
#include "zscript/zcc_parser.h"
// just some rough concepts for now...
//==========================================================================
//
// FindClassMemberFunction
//
// Looks for a name in a class's
//
//==========================================================================
PFunction *FindClassMemberFunction(PClass *cls, FName name, FScriptPosition &sc)
{
PSymbolTable *symtable;
auto symbol = cls->Symbols.FindSymbolInTable(name, symtable);
auto funcsym = dyn_cast<PFunction>(symbol);
if (symbol != nullptr)
{
if (funcsym == nullptr)
{
sc.Message(MSG_ERROR, "%s is not a member function of %s", name.GetChars(), cls->TypeName.GetChars());
}
else if (funcsym->Flags & VARF_Private && symtable != &cls->Symbols)
{
sc.Message(MSG_ERROR, "%s is declared private and not accessible", symbol->SymbolName.GetChars());
}
else if (funcsym->Flags & VARF_Deprecated)
{
sc.Message(MSG_WARNING, "Call to deprecated function %s", symbol->SymbolName.GetChars());
}
}
return funcsym;
}
//==========================================================================
//
// let's save some work down the line by analyzing the type of function
// that's being called right here and create appropriate nodes.
// A simple function call expression must be immediately resolvable in
// the context it gets compiled in, if the function name cannot be
// resolved here, it will never.
//
//==========================================================================
FxExpression *ConvertSimpleFunctionCall(ZCC_ExprID *function, FArgumentList *args, PClass *cls, FScriptPosition &sc)
{
// Priority is as follows:
//1. member functions of the containing class.
//2. action specials.
//3. floating point operations
//4. global builtins
if (cls != nullptr)
{
// There is an action function ACS_NamedExecuteWithResult which must be ignored here for this to work.
if (function->Identifier != NAME_ACS_NamedExecuteWithResult)
{
{
args = ConvertNodeList(fcall->Parameters);
if (args->Size() == 0)
{
delete args;
args = nullptr;
}
return new FxMemberunctionCall(new FxSelf(), new FxInvoker(), func, args, sc, false);
}
}
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression *ConvertFunctionCall(ZCC_Expression *function, FArgumentList *args, PClass *cls, FScriptPosition &sc)
{
// function names can either be
// - plain identifiers
// - class members
// - array syntax for random() calls.
switch(function->NodeType)
{
case AST_ExprID:
return ConvertSimpleFunctionCall(static_cast<ZCC_ExprID *>(function), args, cls, sc);
case AST_ExprMemberAccess:
return ConvertMemberCall(fcall);
case AST_ExprBinary:
// Array access syntax is wrapped into a ZCC_ExprBinary object.
if (fcall->Function->Operation == PEX_ArrayAccess)
{
return ConvertArrayFunctionCall(fcall);
}
break;
default:
break;
}
Error(fcall, "Invalid function identifier");
return new FxNop(*fcall); // return something so that we can continue
}
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;
return new FxFunctionCall(nullptr, fname, ConvertNodeList(fcall->Parameters), *ast);
#endif

View file

@ -474,9 +474,8 @@ void ParseFunctionDef(FScanner &sc, PClassActor *cls, FName funcname,
if (afd != NULL)
{
PFunction *sym = new PFunction(funcname);
sym->AddVariant(NewPrototype(rets, args), argflags, argnames, *(afd->VMPointer));
sym->Flags = funcflags;
PFunction *sym = new PFunction(cls, funcname);
sym->AddVariant(NewPrototype(rets, args), argflags, argnames, *(afd->VMPointer), funcflags);
if (cls->Symbols.AddSymbol(sym) == NULL)
{
delete sym;

View file

@ -602,12 +602,12 @@ void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression
int pnum = 0;
bool zeroparm;
if (afd->Flags & VARF_Method)
if (afd->Variants[0].Flags & VARF_Method)
{
numparams--;
pnum++;
}
if (afd->Flags & VARF_Action)
if (afd->Variants[0].Flags & VARF_Action)
{
numparams -= 2;
pnum += 2;

View file

@ -142,9 +142,8 @@ PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, i
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;
PFunction *sym = new PFunction(containingclass, NAME_None); // anonymous functions do not have names.
sym->AddVariant(NewPrototype(rets, args), argflags, argnames, nullptr, VARF_Action|VARF_Method);
return sym;
}

View file

@ -686,7 +686,7 @@ void FFunctionBuildList::Build()
// 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)]);
auto cls = static_cast<PClass*>(item.Func->Variants[0].Proto->ArgumentTypes[!!(item.Func->Variants[0].Flags & VARF_Action)]);
// We don't know the return type in advance for anonymous functions.
FCompileContext ctx(cls, nullptr);
item.Code = item.Code->Resolve(ctx);

View file

@ -1960,9 +1960,8 @@ void ZCCCompiler::InitFunctions()
} while (p != f->Params);
}
PFunction *sym = new PFunction(f->Name);
sym->AddVariant(NewPrototype(rets, args), argflags, argnames, afd == nullptr? nullptr : *(afd->VMPointer));
sym->Flags = varflags;
PFunction *sym = new PFunction(c->Type(), f->Name);
sym->AddVariant(NewPrototype(rets, args), argflags, argnames, afd == nullptr? nullptr : *(afd->VMPointer), varflags);
c->Type()->Symbols.ReplaceSymbol(sym);
// todo: Check inheritance.
@ -2010,7 +2009,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->Flags & VARF_Action))
if (fc->Parameters == nullptr && (afd->Variants[0].Flags & VARF_Action))
{
// We can use this function directly without wrapping it in a caller.
return new FxVMFunctionCall(afd, nullptr, *af, true);