diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index c43abf9a76..966663f129 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -2509,7 +2509,9 @@ unsigned PFunction::AddVariant(PPrototype *proto, TArray &argflags, TArra if (flags & VARF_Method) { assert(proto->ArgumentTypes.Size() > 0); - variant.SelfClass = dyn_cast(proto->ArgumentTypes[0]); + auto selftypeptr = dyn_cast(proto->ArgumentTypes[0]); + assert(selftypeptr != nullptr); + variant.SelfClass = dyn_cast(selftypeptr->PointedType); assert(variant.SelfClass != nullptr); } diff --git a/src/dobjtype.h b/src/dobjtype.h index 07d7ae10f2..a5da177a4f 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -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 ------------------------------------------------------- diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 801df8c652..4ed7128346 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -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(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(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; } diff --git a/src/scripting/codegeneration/codegen.h b/src/scripting/codegeneration/codegen.h index 3c52547c50..d69323ce0b 100644 --- a/src/scripting/codegeneration/codegen.h +++ b/src/scripting/codegeneration/codegen.h @@ -67,11 +67,14 @@ struct FCompileContext { TArray 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); diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index f80a8f87a0..413999ddc2 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -89,7 +89,7 @@ void SetImplicitArgs(TArray *args, TArray *argflags, TArrayPush(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 *args, TArray *argflags, TArrayIsDescendantOf(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!!! } diff --git a/src/scripting/vm/vmbuilder.cpp b/src/scripting/vm/vmbuilder.cpp index f366ec6505..7c51184670 100644 --- a/src/scripting/vm/vmbuilder.cpp +++ b/src/scripting/vm/vmbuilder.cpp @@ -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(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; } } diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index ee40868d37..aee7e8b763 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -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(t->SiblingNext);