- pass the current function to the compile context instead of just the containing class.

This commit is contained in:
Christoph Oelckers 2016-10-15 17:40:27 +02:00
parent db8ab1bc4a
commit cbb990a79e
7 changed files with 101 additions and 48 deletions

View file

@ -2509,7 +2509,9 @@ unsigned PFunction::AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArra
if (flags & VARF_Method)
{
assert(proto->ArgumentTypes.Size() > 0);
variant.SelfClass = dyn_cast<PClass>(proto->ArgumentTypes[0]);
auto selftypeptr = dyn_cast<PPointer>(proto->ArgumentTypes[0]);
assert(selftypeptr != nullptr);
variant.SelfClass = dyn_cast<PClass>(selftypeptr->PointedType);
assert(variant.SelfClass != nullptr);
}

View file

@ -29,6 +29,7 @@ enum
VARF_In = (1<<10),
VARF_Out = (1<<11),
VARF_Implicit = (1<<12), // implicitly created parameters (i.e. do not compare when checking function signatures)
VARF_Static = (1<<13), // static class data (by necessity read only.)
};
// Symbol information -------------------------------------------------------

View file

@ -49,7 +49,7 @@
#include "thingdef.h"
#include "p_lnspec.h"
#include "doomstat.h"
#include "thingdef_exp.h"
#include "codegen.h"
#include "m_fixed.h"
#include "vmbuilder.h"
#include "v_text.h"
@ -91,13 +91,25 @@ static const FLOP FxFlops[] =
//
//==========================================================================
FCompileContext::FCompileContext(PClass *cls, PPrototype *ret) : Class(cls), ReturnProto(ret)
FCompileContext::FCompileContext(PFunction *fnc, PPrototype *ret) : ReturnProto(ret), Function(fnc), Class(nullptr)
{
if (fnc != nullptr) Class = fnc->OwningClass;
}
FCompileContext::FCompileContext(PClass *cls) : ReturnProto(nullptr), Function(nullptr), Class(cls)
{
}
PSymbol *FCompileContext::FindInClass(FName identifier)
PSymbol *FCompileContext::FindInClass(FName identifier, PSymbolTable *&symt)
{
return Class ? Class->Symbols.FindSymbol(identifier, true) : nullptr;
return Class != nullptr? Class->Symbols.FindSymbolInTable(identifier, symt) : nullptr;
}
PSymbol *FCompileContext::FindInSelfClass(FName identifier, PSymbolTable *&symt)
{
// If we have no self we cannot retrieve any values from it.
if (Function == nullptr || Function->Variants[0].SelfClass == nullptr) return nullptr;
return Function->Variants[0].SelfClass->Symbols.FindSymbolInTable(identifier, symt);
}
PSymbol *FCompileContext::FindGlobal(FName identifier)
{
@ -3178,15 +3190,16 @@ FxIdentifier::FxIdentifier(FName name, const FScriptPosition &pos)
FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
{
PSymbol * sym;
FxExpression *newex = NULL;
//FBaseCVar * cv = NULL;
//FString s;
FxExpression *newex = nullptr;
int num;
//const PClass *Class;
CHECKRESOLVED();
// Ugh, the horror. Constants need to be taken from the owning class, but members from the self class to catch invalid accesses here...
// see if the current class (if valid) defines something with this name.
if ((sym = ctx.FindInClass(Identifier)) != NULL)
PSymbolTable *symtbl;
if ((sym = ctx.FindInClass(Identifier, symtbl)) != nullptr)
{
if (sym->IsKindOf(RUNTIME_CLASS(PSymbolConst)))
{
@ -3195,15 +3208,64 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
}
else if (sym->IsKindOf(RUNTIME_CLASS(PField)))
{
ABORT(ctx.Function); // only valid when resolving a function.
PField *vsym = static_cast<PField*>(sym);
ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s' as member variable, index %d\n", Identifier.GetChars(), vsym->Offset);
newex = new FxClassMember((new FxSelf(ScriptPosition))->Resolve(ctx), vsym, ScriptPosition);
// We have 4 cases to consider here:
// 1. The symbol is a static/meta member (not implemented yet) which is always accessible.
// 2. This is a static function
// 3. This is an action function with a restricted self pointer
// 4. This is a normal member or unrestricted action function.
if (vsym->Flags & VARF_Deprecated)
{
ScriptPosition.Message(MSG_WARNING, "Accessing deprecated member variable %s", vsym->SymbolName.GetChars());
}
if ((vsym->Flags & VARF_Private) && symtbl != &ctx.Class->Symbols)
{
ScriptPosition.Message(MSG_ERROR, "Private member %s not accessible", vsym->SymbolName.GetChars());
delete this;
return nullptr;
}
if (vsym->Flags & VARF_Static)
{
// todo. For now these cannot be defined so let's just exit.
delete this;
return nullptr;
}
if (ctx.Function->Variants[0].SelfClass == nullptr)
{
ScriptPosition.Message(MSG_ERROR, "Unable to access class member from static function");
delete this;
return nullptr;
}
else
{
if (ctx.Function->Variants[0].SelfClass != ctx.Class)
{
// Check if the restricted class can access it.
PSymbol *sym2;
if ((sym2 = ctx.FindInSelfClass(Identifier, symtbl)) != nullptr)
{
if (sym != sym2)
{
ScriptPosition.Message(MSG_ERROR, "Member variable of %s not accessible through restricted self pointer", ctx.Class->TypeName.GetChars());
delete this;
return nullptr;
}
}
}
ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s' as member variable, index %d\n", Identifier.GetChars(), vsym->Offset);
newex = new FxClassMember((new FxSelf(ScriptPosition))->Resolve(ctx), vsym, ScriptPosition);
}
}
else
{
ScriptPosition.Message(MSG_ERROR, "Invalid member identifier '%s'\n", Identifier.GetChars());
}
}
// now check the global identifiers.
else if ((sym = ctx.FindGlobal(Identifier)) != NULL)
{
@ -3217,23 +3279,7 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
ScriptPosition.Message(MSG_ERROR, "Invalid global identifier '%s'\n", Identifier.GetChars());
}
}
/*
else if ((Class = PClass::FindClass(Identifier)))
{
pos.Message(MSG_DEBUGLOG, "Resolving name '%s' as class name\n", Identifier.GetChars());
newex = new FxClassType(Class, ScriptPosition);
}
}
*/
// also check for CVars
/*
else if ((cv = FindCVar(Identifier, NULL)) != NULL)
{
CLOG(CL_RESOLVE, LPrintf("Resolving name '%s' as cvar\n", Identifier.GetChars()));
newex = new FxCVar(cv, ScriptPosition);
}
*/
// and line specials
else if ((num = P_FindLineSpecial(Identifier, NULL, NULL)))
{
@ -3270,15 +3316,13 @@ FxSelf::FxSelf(const FScriptPosition &pos)
FxExpression *FxSelf::Resolve(FCompileContext& ctx)
{
CHECKRESOLVED();
if (!ctx.Class)
if (ctx.Function == nullptr || ctx.Function->Variants[0].SelfClass == nullptr)
{
// can't really happen with DECORATE's expression evaluator.
ScriptPosition.Message(MSG_ERROR, "self used outside of a member function");
delete this;
return NULL;
return nullptr;
}
ValueType = ctx.Class;
ValueType = NewPointer(RUNTIME_CLASS(DObject));
ValueType = NewPointer(ctx.Function->Variants[0].SelfClass);
return this;
}
@ -3614,8 +3658,10 @@ FxFunctionCall::~FxFunctionCall()
FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
{
// This part is mostly a kludge, it really needs to get the class type from Self.
ABORT(ctx.Class);
PFunction *afd = dyn_cast<PFunction>(ctx.Class->Symbols.FindSymbol(MethodName, true));
// Todo: More checks are needed here to make it work as expected.
if (afd != nullptr)
{
auto x = new FxVMFunctionCall(afd, ArgList, ScriptPosition, false);
@ -5172,9 +5218,9 @@ FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx)
delete this;
return NULL;
}
else if (!scope->IsDescendantOf(ctx.Class))
else if (!scope->IsAncestorOf(ctx.Class))
{
ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(),ctx.Class->TypeName.GetChars());
ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(), ctx.Class->TypeName.GetChars());
delete this;
return NULL;
}

View file

@ -67,11 +67,14 @@ struct FCompileContext
{
TArray<FxJumpStatement *> Jumps;
PPrototype *ReturnProto;
PClass *Class;
PFunction *Function; // The function that is currently being compiled (or nullptr for constant evaluation.)
PClass *Class; // The type of the owning class.
FCompileContext(PClass *cls = nullptr, PPrototype *ret = nullptr);
FCompileContext(PFunction *func = nullptr, PPrototype *ret = nullptr);
FCompileContext(PClass *cls); // only to be used to resolve constants!
PSymbol *FindInClass(FName identifier);
PSymbol *FindInClass(FName identifier, PSymbolTable *&symt);
PSymbol *FindInSelfClass(FName identifier, PSymbolTable *&symt);
PSymbol *FindGlobal(FName identifier);
void HandleJumps(int token, FxExpression *handler);

View file

@ -89,7 +89,7 @@ void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FNam
if (funcflags & VARF_Method)
{
// implied self pointer
if (args != nullptr) args->Push(cls);
if (args != nullptr) args->Push(NewPointer(cls));
if (argflags != nullptr) argflags->Push(VARF_Implicit);
if (argnames != nullptr) argnames->Push(NAME_self);
}
@ -101,11 +101,11 @@ void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FNam
// 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'
if (cls->IsDescendantOf(RUNTIME_CLASS(AStateProvider)))
{
args->Insert(0, RUNTIME_CLASS(AActor)); // this must go in before the real pointer to the containing class.
args->Insert(0, NewPointer(RUNTIME_CLASS(AActor))); // this must go in before the real pointer to the containing class.
}
else
{
args->Push(cls);
args->Push(NewPointer(cls));
}
args->Push(TypeState/*Info*/); // fixme: TypeState is not the correct type here!!!
}

View file

@ -686,9 +686,8 @@ 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->Variants[0].Flags & VARF_Action)]);
// We don't know the return type in advance for anonymous functions.
FCompileContext ctx(cls, nullptr);
FCompileContext ctx(item.Func, nullptr);
item.Code = item.Code->Resolve(ctx);
item.Proto = ctx.ReturnProto;
@ -715,10 +714,7 @@ void FFunctionBuildList::Build()
if (dump != nullptr)
{
char label[64];
int labellen = mysnprintf(label, countof(label), item.DumpName,
cls->TypeName.GetChars());
DumpFunction(dump, sfunc, label, labellen);
DumpFunction(dump, sfunc, item.DumpName.GetChars(), (int)item.DumpName.Len());
codesize += sfunc->CodeSize;
}
}

View file

@ -1888,6 +1888,11 @@ void ZCCCompiler::InitFunctions()
do
{
auto type = DetermineType(c->Type(), f, f->Name, t, false);
if (type->IsKindOf(RUNTIME_CLASS(PStruct)))
{
// structs and classes only get passed by pointer.
type = NewPointer(type);
}
// TBD: disallow certain types? For now, let everything pass that isn't an array.
rets.Push(type);
t = static_cast<decltype(t)>(t->SiblingNext);