Backend update from GZDoom.

This commit is contained in:
Christoph Oelckers 2023-11-07 18:35:11 +01:00
parent 0bcfd5ca92
commit c6e5ade0b3
31 changed files with 1213 additions and 66 deletions

View file

@ -120,6 +120,7 @@ xx(Vector)
xx(Map)
xx(MapIterator)
xx(Array)
xx(Function)
xx(Include)
xx(Sound)
xx(State)
@ -190,6 +191,7 @@ xx(SetNull)
xx(Key)
xx(Index)
xx(Find)
xx(Call)
// color channels
xx(a)
@ -269,6 +271,7 @@ xx(BuiltinRandom2)
xx(BuiltinFRandom)
xx(BuiltinNameToClass)
xx(BuiltinClassCast)
xx(BuiltinFunctionPtrCast)
xx(ScreenJobRunner)
xx(Action)

View file

@ -169,6 +169,7 @@ std2:
'map' { RET(TK_Map); }
'mapiterator' { RET(TK_MapIterator); }
'array' { RET(TK_Array); }
'function' { RET(ParseVersion >= MakeVersion(4, 12, 0)? TK_FunctionType : TK_Identifier); }
'in' { RET(TK_In); }
'sizeof' { RET(TK_SizeOf); }
'alignof' { RET(TK_AlignOf); }

View file

@ -131,6 +131,7 @@ xx(TK_Vector3, "'vector3'")
xx(TK_Map, "'map'")
xx(TK_MapIterator, "'mapiterator'")
xx(TK_Array, "'array'")
xx(TK_FunctionType, "'function'")
xx(TK_In, "'in'")
xx(TK_SizeOf, "'sizeof'")
xx(TK_AlignOf, "'alignof'")

View file

@ -1656,6 +1656,84 @@ FSerializer &Serialize(FSerializer &arc, const char *key, NumericValue &value, N
return arc;
}
//==========================================================================
//
// PFunctionPointer
//
//==========================================================================
void SerializeFunctionPointer(FSerializer &arc, const char *key, FunctionPointerValue *&p)
{
if (arc.isWriting())
{
if(p)
{
arc.BeginObject(key);
arc("Class",p->ClassName);
arc("Function",p->FunctionName);
arc.EndObject();
}
else
{
arc.WriteKey(key);
arc.w->Null();
}
}
else
{
assert(p);
auto v = arc.r->FindKey(key);
if(!v || v->IsNull())
{
p = nullptr;
}
else if(v->IsObject())
{
arc.r->mObjects.Push(FJSONObject(v)); // BeginObject
const char * cstr;
arc.StringPtr("Class", cstr);
if(!cstr)
{
arc.StringPtr("Function", cstr);
if(!cstr)
{
Printf(TEXTCOLOR_RED "Function Pointer missing Class and Function Fields in Object\n");
}
else
{
Printf(TEXTCOLOR_RED "Function Pointer missing Class Field in Object\n");
}
arc.mErrors++;
arc.EndObject();
p = nullptr;
return;
}
p->ClassName = FString(cstr);
arc.StringPtr("Function", cstr);
if(!cstr)
{
Printf(TEXTCOLOR_RED "Function Pointer missing Function Field in Object\n");
arc.mErrors++;
arc.EndObject();
p = nullptr;
return;
}
p->FunctionName = FString(cstr);
arc.EndObject();
}
else
{
Printf(TEXTCOLOR_RED "Function Pointer is not an Object\n");
arc.mErrors++;
p = nullptr;
}
}
}
#include "renderstyle.h"
FSerializer& Serialize(FSerializer& arc, const char* key, FRenderStyle& style, FRenderStyle* def)
{

View file

@ -54,6 +54,12 @@ struct NumericValue
}
};
struct FunctionPointerValue
{
FString ClassName;
FString FunctionName;
};
class FSerializer
{
@ -235,6 +241,8 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FString &sid, FString
FSerializer &Serialize(FSerializer &arc, const char *key, NumericValue &sid, NumericValue *def);
FSerializer &Serialize(FSerializer &arc, const char *key, struct ModelOverride &sid, struct ModelOverride *def);
void SerializeFunctionPointer(FSerializer &arc, const char *key, FunctionPointerValue *&p);
template <typename T/*, typename = std::enable_if_t<std::is_base_of_v<DObject, T>>*/>
FSerializer &Serialize(FSerializer &arc, const char *key, T *&value, T **)
{

View file

@ -17,8 +17,8 @@ public:
virtual VSMatrix GetViewToWorldMatrix() = 0;
virtual void BeginDrawHUDModel(FRenderStyle style, const VSMatrix &objectToWorldMatrix, bool mirrored) = 0;
virtual void EndDrawHUDModel(FRenderStyle style) = 0;
virtual void BeginDrawHUDModel(FRenderStyle style, const VSMatrix &objectToWorldMatrix, bool mirrored, FSpriteModelFrame *smf) = 0;
virtual void EndDrawHUDModel(FRenderStyle style, FSpriteModelFrame *smf) = 0;
virtual void SetInterpolation(double interpolation) = 0;
virtual void SetMaterial(FGameTexture *skin, bool clampNoFilter, int translation) = 0;

View file

@ -243,6 +243,7 @@ public:
inline DAngle &AngleVar(FName field);
inline FString &StringVar(FName field);
template<class T> T*& PointerVar(FName field);
inline int* IntArray(FName field);
// This is only needed for swapping out PlayerPawns and absolutely nothing else!
virtual size_t PointerSubstitution (DObject *old, DObject *notOld);
@ -433,6 +434,11 @@ inline int &DObject::IntVar(FName field)
return *(int*)ScriptVar(field, nullptr);
}
inline int* DObject::IntArray(FName field)
{
return (int*)ScriptVar(field, nullptr);
}
inline FTextureID &DObject::TextureIDVar(FName field)
{
return *(FTextureID*)ScriptVar(field, nullptr);

View file

@ -282,6 +282,11 @@ public:
return GC::ReadBarrier(o) == u;
}
constexpr bool operator==(TObjPtr<T> u) noexcept
{
return ForceGet() == u.ForceGet();
}
template<class U> friend inline void GC::Mark(TObjPtr<U> &obj);
template<class U> friend FSerializer &Serialize(FSerializer &arc, const char *key, TObjPtr<U> &value, TObjPtr<U> *);
template<class U> friend FSerializer &Serialize(FSerializer &arc, const char *key, TObjPtr<U> &value, U *);

View file

@ -158,7 +158,7 @@ namespace OpenGLESRenderer
Printf(PRINT_LOG, "GL_EXTENSIONS:\n");
for (unsigned i = 0; i < m_Extensions.Size(); i++)
{
Printf(" %s\n", m_Extensions[i].GetChars());
Printf(PRINT_LOG, " %s\n", m_Extensions[i].GetChars());
}
const char* glVersionStr = (const char*)glGetString(GL_VERSION);
double glVersion = strtod(glVersionStr, NULL);

View file

@ -268,6 +268,8 @@ PFunction *FindBuiltinFunction(FName funcname)
//
//==========================================================================
static bool AreCompatibleFnPtrTypes(PPrototype *to, PPrototype *from);
bool AreCompatiblePointerTypes(PType *dest, PType *source, bool forcompare)
{
if (dest->isPointer() && source->isPointer())
@ -297,6 +299,14 @@ bool AreCompatiblePointerTypes(PType *dest, PType *source, bool forcompare)
if (forcompare && tocls->IsDescendantOf(fromcls)) return true;
return (fromcls->IsDescendantOf(tocls));
}
if(source->isFunctionPointer() && dest->isFunctionPointer())
{
auto from = static_cast<PFunctionPointer*>(source);
auto to = static_cast<PFunctionPointer*>(dest);
if(from->PointedType == TypeVoid) return false;
return to->PointedType == TypeVoid || (AreCompatibleFnPtrTypes((PPrototype *)to->PointedType, (PPrototype *)from->PointedType) && from->ArgFlags == to->ArgFlags && FScopeBarrier::CheckSidesForFunctionPointer(from->Scope, to->Scope));
}
}
return false;
}
@ -906,6 +916,17 @@ FxExpression *FxBoolCast::Resolve(FCompileContext &ctx)
ExpEmit FxBoolCast::Emit(VMFunctionBuilder *build)
{
ExpEmit from = basex->Emit(build);
if(from.Konst && from.RegType == REGT_INT)
{ // this is needed here because the int const assign optimization returns a constant
ExpEmit to;
to.Konst = true;
to.RegType = REGT_INT;
to.RegNum = build->GetConstantInt(!!build->FindConstantInt(from.RegNum));
return to;
}
assert(!from.Konst);
assert(basex->ValueType->GetRegType() == REGT_INT || basex->ValueType->GetRegType() == REGT_FLOAT || basex->ValueType->GetRegType() == REGT_POINTER);
@ -1130,7 +1151,15 @@ FxExpression *FxFloatCast::Resolve(FCompileContext &ctx)
ExpEmit FxFloatCast::Emit(VMFunctionBuilder *build)
{
ExpEmit from = basex->Emit(build);
//assert(!from.Konst);
if(from.Konst && from.RegType == REGT_INT)
{ // this is needed here because the int const assign optimization returns a constant
ExpEmit to;
to.Konst = true;
to.RegType = REGT_FLOAT;
to.RegNum = build->GetConstantFloat(build->FindConstantInt(from.RegNum));
return to;
}
assert(basex->ValueType->GetRegType() == REGT_INT);
from.Free(build);
ExpEmit to(build, REGT_FLOAT);
@ -1608,6 +1637,35 @@ FxTypeCast::~FxTypeCast()
//
//==========================================================================
FxConstant * FxTypeCast::convertRawFunctionToFunctionPointer(FxExpression * in, FScriptPosition &ScriptPosition)
{
assert(in->isConstant() && in->ValueType == TypeRawFunction);
FxConstant *val = static_cast<FxConstant*>(in);
PFunction * fn = static_cast<PFunction*>(val->value.pointer);
if(fn && (fn->Variants[0].Flags & (VARF_Virtual | VARF_Action | VARF_Method)) == 0)
{
val->ValueType = val->value.Type = NewFunctionPointer(fn->Variants[0].Proto, TArray<uint32_t>(fn->Variants[0].ArgFlags), FScopeBarrier::SideFromFlags(fn->Variants[0].Flags));
return val;
}
else if(fn && (fn->Variants[0].Flags & (VARF_Virtual | VARF_Action | VARF_Method)) == VARF_Method)
{
TArray<uint32_t> flags(fn->Variants[0].ArgFlags);
flags[0] = 0;
val->ValueType = val->value.Type = NewFunctionPointer(fn->Variants[0].Proto, std::move(flags), FScopeBarrier::SideFromFlags(fn->Variants[0].Flags));
return val;
}
else if(!fn)
{
val->ValueType = val->value.Type = NewFunctionPointer(nullptr, {}, -1); // Function<void>
return val;
}
else
{
ScriptPosition.Message(MSG_ERROR, "virtual/action function pointers are not allowed");
return nullptr;
}
}
FxExpression *FxTypeCast::Resolve(FCompileContext &ctx)
{
CHECKRESOLVED();
@ -1619,6 +1677,22 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx)
if (result != this) return result;
}
if (basex->isConstant() && basex->ValueType == TypeRawFunction && ValueType->isFunctionPointer())
{
FxConstant *val = convertRawFunctionToFunctionPointer(basex, ScriptPosition);
if(!val)
{
delete this;
return nullptr;
}
}
else if (basex->isConstant() && basex->ValueType == TypeRawFunction && ValueType == TypeVMFunction)
{
FxConstant *val = static_cast<FxConstant*>(basex);
val->ValueType = val->value.Type = TypeVMFunction;
val->value.pointer = static_cast<PFunction*>(val->value.pointer)->Variants[0].Implementation;
}
// first deal with the simple types
if (ValueType == TypeError || basex->ValueType == TypeError || basex->ValueType == nullptr)
{
@ -1918,7 +1992,17 @@ ExpEmit FxMinusSign::Emit(VMFunctionBuilder *build)
{
//assert(ValueType == Operand->ValueType);
ExpEmit from = Operand->Emit(build);
if(from.Konst && from.RegType == REGT_INT)
{ // this is needed here because the int const assign optimization returns a constant
ExpEmit to;
to.Konst = true;
to.RegType = REGT_INT;
to.RegNum = build->GetConstantInt(-build->FindConstantInt(from.RegNum));
return to;
}
ExpEmit to;
assert(from.Konst == 0);
assert(ValueType->GetRegCount() == from.RegCount);
// Do it in-place, unless a local variable
@ -2038,6 +2122,16 @@ ExpEmit FxUnaryNotBitwise::Emit(VMFunctionBuilder *build)
{
assert(Operand->ValueType->GetRegType() == REGT_INT);
ExpEmit from = Operand->Emit(build);
if(from.Konst && from.RegType == REGT_INT)
{ // this is needed here because the int const assign optimization returns a constant
ExpEmit to;
to.Konst = true;
to.RegType = REGT_INT;
to.RegNum = build->GetConstantInt(~build->FindConstantInt(from.RegNum));
return to;
}
from.Free(build);
ExpEmit to(build, REGT_INT);
assert(!from.Konst);
@ -2109,6 +2203,16 @@ ExpEmit FxUnaryNotBoolean::Emit(VMFunctionBuilder *build)
assert(Operand->ValueType == TypeBool);
assert(ValueType == TypeBool || IsInteger()); // this may have been changed by an int cast.
ExpEmit from = Operand->Emit(build);
if(from.Konst && from.RegType == REGT_INT)
{ // this is needed here because the int const assign optimization returns a constant
ExpEmit to;
to.Konst = true;
to.RegType = REGT_INT;
to.RegNum = build->GetConstantInt(!build->FindConstantInt(from.RegNum));
return to;
}
from.Free(build);
ExpEmit to(build, REGT_INT);
assert(!from.Konst);
@ -6363,9 +6467,8 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
if (ctx.Version >= MakeVersion(4, 11, 100))
{
// VMFunction is only supported since 4.12 and Raze 1.8.
newex = new FxConstant(static_cast<PFunction*>(sym)->Variants[0].Implementation, ScriptPosition);
newex = new FxConstant(static_cast<PFunction*>(sym), ScriptPosition);
goto foundit;
}
}
}
@ -6384,7 +6487,7 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
if (ctx.Version >= MakeVersion(4, 11, 100))
{
// VMFunction is only supported since 4.12 and Raze 1.8.
newex = new FxConstant(static_cast<PFunction*>(sym)->Variants[0].Implementation, ScriptPosition);
newex = new FxConstant(static_cast<PFunction*>(sym), ScriptPosition);
goto foundit;
}
}
@ -6532,7 +6635,6 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PContainerType *
if (result != this) return result;
}
if (objtype != nullptr && (sym = objtype->Symbols.FindSymbolInTable(Identifier, symtbl)) != nullptr)
{
if (sym->IsKindOf(RUNTIME_CLASS(PSymbolConst)))
@ -6748,7 +6850,7 @@ FxExpression *FxMemberIdentifier::Resolve(FCompileContext& ctx)
SAFE_RESOLVE(Object, ctx);
// check for class or struct constants if the left side is a type name.
// check for class or struct constants/functions if the left side is a type name.
if (Object->ValueType == TypeError)
{
if (ccls != nullptr)
@ -6762,6 +6864,16 @@ FxExpression *FxMemberIdentifier::Resolve(FCompileContext& ctx)
delete this;
return FxConstant::MakeConstant(sym, ScriptPosition);
}
else if(sym->IsKindOf(RUNTIME_CLASS(PFunction)))
{
if (ctx.Version >= MakeVersion(4, 11, 100))
{
// VMFunction is only supported since 4.12 and Raze 1.8.
auto x = new FxConstant(static_cast<PFunction*>(sym), ScriptPosition);
delete this;
return x->Resolve(ctx);
}
}
else
{
auto f = dyn_cast<PField>(sym);
@ -8379,6 +8491,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
PContainerType *cls = nullptr;
bool staticonly = false;
bool novirtual = false;
bool fnptr = false;
bool isreadonly = false;
PContainerType *ccls = nullptr;
@ -8996,6 +9109,22 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
cls = static_cast<PStruct*>(Self->ValueType);
Self->ValueType = NewPointer(Self->ValueType);
}
else if (Self->ValueType->isFunctionPointer())
{
auto fn = static_cast<PFunctionPointer*>(Self->ValueType);
if(MethodName == NAME_Call && fn->PointedType != TypeVoid)
{ // calling a Function<void> pointer isn't allowed
fnptr = true;
afd_override = fn->FakeFunction;
}
else
{
ScriptPosition.Message(MSG_ERROR, "Unknown function %s", MethodName.GetChars());
delete this;
return nullptr;
}
}
else
{
ScriptPosition.Message(MSG_ERROR, "Invalid expression on left hand side of %s", MethodName.GetChars());
@ -9115,8 +9244,8 @@ isresolved:
}
// do not pass the self pointer to static functions.
auto self = (afd->Variants[0].Flags & VARF_Method) ? Self : nullptr;
auto x = new FxVMFunctionCall(self, afd, ArgList, ScriptPosition, staticonly|novirtual);
auto self = ( fnptr || (afd->Variants[0].Flags & VARF_Method)) ? Self : nullptr;
auto x = new FxVMFunctionCall(self, afd, ArgList, ScriptPosition, (staticonly || novirtual) && !fnptr);
if (Self == self) Self = nullptr;
delete this;
return x->Resolve(ctx);
@ -9130,7 +9259,7 @@ isresolved:
//==========================================================================
FxVMFunctionCall::FxVMFunctionCall(FxExpression *self, PFunction *func, FArgumentList &args, const FScriptPosition &pos, bool novirtual)
: FxExpression(EFX_VMFunctionCall, pos)
: FxExpression(EFX_VMFunctionCall, pos) , FnPtrCall((self && self->ValueType) ? self->ValueType->isFunctionPointer() : false)
{
Self = self;
Function = func;
@ -9204,7 +9333,7 @@ VMFunction *FxVMFunctionCall::GetDirectFunction(PFunction *callingfunc, const Ve
// definition can call that function directly without wrapping
// it inside VM code.
if (ArgList.Size() == 0 && !(Function->Variants[0].Flags & VARF_Virtual) && CheckAccessibility(ver) && CheckFunctionCompatiblity(ScriptPosition, callingfunc, Function))
if (ArgList.Size() == 0 && !(Function->Variants[0].Flags & VARF_Virtual) && !FnPtrCall && CheckAccessibility(ver) && CheckFunctionCompatiblity(ScriptPosition, callingfunc, Function))
{
unsigned imp = Function->GetImplicitArgs();
if (Function->Variants[0].ArgFlags.Size() > imp && !(Function->Variants[0].ArgFlags[imp] & VARF_Optional)) return nullptr;
@ -9229,7 +9358,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
auto &argtypes = proto->ArgumentTypes;
auto &argnames = Function->Variants[0].ArgNames;
auto &argflags = Function->Variants[0].ArgFlags;
auto &defaults = Function->Variants[0].Implementation->DefaultArgs;
auto *defaults = FnPtrCall ? nullptr : &Function->Variants[0].Implementation->DefaultArgs;
int implicit = Function->GetImplicitArgs();
@ -9297,6 +9426,12 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
if (ArgList[i]->ExprType == EFX_NamedNode)
{
if(FnPtrCall)
{
ScriptPosition.Message(MSG_ERROR, "Named arguments not supported in function pointer calls");
delete this;
return nullptr;
}
if (!(flag & VARF_Optional))
{
ScriptPosition.Message(MSG_ERROR, "Cannot use a named argument here - not all required arguments have been passed.");
@ -9346,7 +9481,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
}
if (ntype->GetRegCount() == 1)
{
auto x = new FxConstant(ntype, defaults[i + k + skipdefs + implicit], ScriptPosition);
auto x = new FxConstant(ntype, (*defaults)[i + k + skipdefs + implicit], ScriptPosition);
ArgList.Insert(i + k, x);
}
else
@ -9355,7 +9490,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
FxConstant *cs[4] = { nullptr };
for (int l = 0; l < ntype->GetRegCount(); l++)
{
cs[l] = new FxConstant(TypeFloat64, defaults[l + i + k + skipdefs + implicit], ScriptPosition);
cs[l] = new FxConstant(TypeFloat64, (*defaults)[l + i + k + skipdefs + implicit], ScriptPosition);
}
FxExpression *x = new FxVectorValue(cs[0], cs[1], cs[2], cs[3], ScriptPosition);
ArgList.Insert(i + k, x);
@ -9492,6 +9627,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
{
ValueType = TypeVoid;
}
return this;
}
@ -9517,11 +9653,14 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
}
}
VMFunction *vmfunc = Function->Variants[0].Implementation;
bool staticcall = ((vmfunc->VarFlags & VARF_Final) || vmfunc->VirtualIndex == ~0u || NoVirtual);
VMFunction *vmfunc = FnPtrCall ? nullptr : Function->Variants[0].Implementation;
bool staticcall = (FnPtrCall || (vmfunc->VarFlags & VARF_Final) || vmfunc->VirtualIndex == ~0u || NoVirtual);
count = 0;
FunctionCallEmitter emitters(vmfunc);
assert(!FnPtrCall || (FnPtrCall && Self && Self->ValueType && Self->ValueType->isFunctionPointer()));
FunctionCallEmitter emitters(FnPtrCall ? FunctionCallEmitter(PType::toFunctionPointer(Self->ValueType)) : FunctionCallEmitter(vmfunc));
// Emit code to pass implied parameters
ExpEmit selfemit;
if (Function->Variants[0].Flags & VARF_Method)
@ -9565,43 +9704,57 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
}
}
}
else if (FnPtrCall)
{
assert(Self != nullptr);
selfemit = Self->Emit(build);
assert(selfemit.RegType == REGT_POINTER);
build->Emit(OP_NULLCHECK, selfemit.RegNum, 0, 0);
staticcall = false;
}
else staticcall = true;
// Emit code to pass explicit parameters
for (unsigned i = 0; i < ArgList.Size(); ++i)
{
emitters.AddParameter(build, ArgList[i]);
}
// Complete the parameter list from the defaults.
auto &defaults = Function->Variants[0].Implementation->DefaultArgs;
for (unsigned i = emitters.Count(); i < defaults.Size(); i++)
if(!FnPtrCall)
{
switch (defaults[i].Type)
// Complete the parameter list from the defaults.
auto &defaults = Function->Variants[0].Implementation->DefaultArgs;
for (unsigned i = emitters.Count(); i < defaults.Size(); i++)
{
default:
case REGT_INT:
emitters.AddParameterIntConst(defaults[i].i);
break;
case REGT_FLOAT:
emitters.AddParameterFloatConst(defaults[i].f);
break;
case REGT_POINTER:
emitters.AddParameterPointerConst(defaults[i].a);
break;
case REGT_STRING:
emitters.AddParameterStringConst(defaults[i].s());
break;
switch (defaults[i].Type)
{
default:
case REGT_INT:
emitters.AddParameterIntConst(defaults[i].i);
break;
case REGT_FLOAT:
emitters.AddParameterFloatConst(defaults[i].f);
break;
case REGT_POINTER:
emitters.AddParameterPointerConst(defaults[i].a);
break;
case REGT_STRING:
emitters.AddParameterStringConst(defaults[i].s());
break;
}
}
}
ArgList.DeleteAndClear();
ArgList.ShrinkToFit();
if (!staticcall) emitters.SetVirtualReg(selfemit.RegNum);
int resultcount = vmfunc->Proto->ReturnTypes.Size() == 0 ? 0 : max(AssignCount, 1);
assert((unsigned)resultcount <= vmfunc->Proto->ReturnTypes.Size());
PPrototype * proto = FnPtrCall ? static_cast<PPrototype*>(static_cast<PFunctionPointer*>(Self->ValueType)->PointedType) : vmfunc->Proto;
int resultcount = proto->ReturnTypes.Size() == 0 ? 0 : max(AssignCount, 1);
assert((unsigned)resultcount <= proto->ReturnTypes.Size());
for (int i = 0; i < resultcount; i++)
{
emitters.AddReturn(vmfunc->Proto->ReturnTypes[i]->GetRegType(), vmfunc->Proto->ReturnTypes[i]->GetRegCount());
emitters.AddReturn(proto->ReturnTypes[i]->GetRegType(), proto->ReturnTypes[i]->GetRegCount());
}
return emitters.EmitCall(build, resultcount > 1? &ReturnRegs : nullptr);
}
@ -11226,6 +11379,10 @@ FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx)
{
mismatchSeverity = MSG_ERROR;
}
else if (protoRetCount > retCount)
{ // also warn when returning less values then the return count
mismatchSeverity = ctx.Version >= MakeVersion(4, 12) ? MSG_ERROR : MSG_WARNING;
}
}
if (mismatchSeverity != -1)
@ -11629,6 +11786,228 @@ ExpEmit FxClassPtrCast::Emit(VMFunctionBuilder *build)
return emitters.EmitCall(build);
}
//==========================================================================
//
//==========================================================================
FxFunctionPtrCast::FxFunctionPtrCast(PFunctionPointer *ftype, FxExpression *x)
: FxExpression(EFX_FunctionPtrCast, x->ScriptPosition)
{
ValueType = ftype;
basex = x;
}
//==========================================================================
//
//
//
//==========================================================================
FxFunctionPtrCast::~FxFunctionPtrCast()
{
SAFE_DELETE(basex);
}
//==========================================================================
//
//
//
//==========================================================================
static bool AreCompatibleFnPtrs(PFunctionPointer * to, PFunctionPointer * from);
bool CanNarrowTo(PClass * from, PClass * to)
{
return from->IsAncestorOf(to);
}
bool CanWidenTo(PClass * from, PClass * to)
{
return to->IsAncestorOf(from);
}
static bool AreCompatibleFnPtrTypes(PPrototype *to, PPrototype *from)
{
if(to->ArgumentTypes.Size() != from->ArgumentTypes.Size()
|| to->ReturnTypes.Size() != from->ReturnTypes.Size()) return false;
int n = to->ArgumentTypes.Size();
//allow narrowing of arguments
for(int i = 0; i < n; i++)
{
PType * fromType = from->ArgumentTypes[i];
PType * toType = to->ArgumentTypes[i];
if(fromType->isFunctionPointer() && toType->isFunctionPointer())
{
if(!AreCompatibleFnPtrs(static_cast<PFunctionPointer *>(toType), static_cast<PFunctionPointer *>(fromType))) return false;
}
else if(fromType->isClassPointer() && toType->isClassPointer())
{
PClassPointer * fromClass = static_cast<PClassPointer *>(fromType);
PClassPointer * toClass = static_cast<PClassPointer *>(toType);
//allow narrowing parameters
if(!CanNarrowTo(fromClass->ClassRestriction, toClass->ClassRestriction)) return false;
}
else if(fromType->isObjectPointer() && toType->isObjectPointer())
{
PObjectPointer * fromObj = static_cast<PObjectPointer *>(fromType);
PObjectPointer * toObj = static_cast<PObjectPointer *>(toType);
//allow narrowing parameters
if(!CanNarrowTo(fromObj->PointedClass(), toObj->PointedClass())) return false;
}
else if(fromType != toType)
{
return false;
}
}
n = to->ReturnTypes.Size();
for(int i = 0; i < n; i++)
{
PType * fromType = from->ReturnTypes[i];
PType * toType = to->ReturnTypes[i];
if(fromType->isFunctionPointer() && toType->isFunctionPointer())
{
if(!AreCompatibleFnPtrs(static_cast<PFunctionPointer *>(toType), static_cast<PFunctionPointer *>(fromType))) return false;
}
else if(fromType->isClassPointer() && toType->isClassPointer())
{
PClassPointer * fromClass = static_cast<PClassPointer *>(fromType);
PClassPointer * toClass = static_cast<PClassPointer *>(toType);
//allow widening returns
if(!CanWidenTo(fromClass->ClassRestriction, toClass->ClassRestriction)) return false;
}
else if(fromType->isObjectPointer() && toType->isObjectPointer())
{
PObjectPointer * fromObj = static_cast<PObjectPointer *>(fromType);
PObjectPointer * toObj = static_cast<PObjectPointer *>(toType);
//allow widening returns
if(!CanWidenTo(fromObj->PointedClass(), toObj->PointedClass())) return false;
}
else if(fromType != toType)
{
return false;
}
}
return true;
}
static bool AreCompatibleFnPtrs(PFunctionPointer * to, PFunctionPointer * from)
{
if(to->PointedType == TypeVoid) return true;
else if(from->PointedType == TypeVoid) return false;
PPrototype * toProto = (PPrototype *)to->PointedType;
PPrototype * fromProto = (PPrototype *)from->PointedType;
return
( FScopeBarrier::CheckSidesForFunctionPointer(from->Scope, to->Scope)
/*
&& toProto->ArgumentTypes == fromProto->ArgumentTypes
&& toProto->ReturnTypes == fromProto->ReturnTypes
*/
&& AreCompatibleFnPtrTypes(toProto, fromProto)
&& to->ArgFlags == from->ArgFlags
);
}
FxExpression *FxFunctionPtrCast::Resolve(FCompileContext &ctx)
{
CHECKRESOLVED();
SAFE_RESOLVE(basex, ctx);
if (basex->isConstant() && basex->ValueType == TypeRawFunction)
{
FxConstant *val = FxTypeCast::convertRawFunctionToFunctionPointer(basex, ScriptPosition);
if(!val)
{
delete this;
return nullptr;
}
}
if (!(basex->ValueType && basex->ValueType->isFunctionPointer()))
{
delete this;
return nullptr;
}
auto to = static_cast<PFunctionPointer *>(ValueType);
auto from = static_cast<PFunctionPointer *>(basex->ValueType);
if(from->PointedType == TypeVoid)
{ // nothing to check at compile-time for casts from Function<void>
return this;
}
else if(AreCompatibleFnPtrs(to, from))
{ // no need to do anything for (Function<void>)(...) or compatible casts
basex->ValueType = ValueType;
auto x = basex;
basex = nullptr;
delete this;
return x;
}
else
{
ScriptPosition.Message(MSG_ERROR, "Cannot cast %s to %s. The types are incompatible.", basex->ValueType->DescriptiveName(), to->DescriptiveName());
delete this;
return nullptr;
}
}
//==========================================================================
//
//
//
//==========================================================================
PFunction *NativeFunctionPointerCast(PFunction *from, const PFunctionPointer *to)
{
if(to->PointedType == TypeVoid)
{
return from;
}
else if(from && ((from->Variants[0].Flags & (VARF_Virtual | VARF_Action)) == 0) && FScopeBarrier::CheckSidesForFunctionPointer(FScopeBarrier::SideFromFlags(from->Variants[0].Flags), to->Scope))
{
if(to->ArgFlags.Size() != from->Variants[0].ArgFlags.Size()) return nullptr;
int n = to->ArgFlags.Size();
for(int i = from->GetImplicitArgs(); i < n; i++) // skip checking flags for implicit self
{
if(from->Variants[0].ArgFlags[i] != to->ArgFlags[i])
{
return nullptr;
}
}
return AreCompatibleFnPtrTypes(static_cast<PPrototype*>(to->PointedType), from->Variants[0].Proto) ? from : nullptr;
}
else
{ // cannot cast virtual/action functions to anything
return nullptr;
}
}
DEFINE_ACTION_FUNCTION_NATIVE(DObject, BuiltinFunctionPtrCast, NativeFunctionPointerCast)
{
PARAM_PROLOGUE;
PARAM_POINTER(from, PFunction);
PARAM_POINTER(to, PFunctionPointer);
ACTION_RETURN_POINTER(NativeFunctionPointerCast(from, to));
}
ExpEmit FxFunctionPtrCast::Emit(VMFunctionBuilder *build)
{
ExpEmit funcptr = basex->Emit(build);
auto sym = FindBuiltinFunction(NAME_BuiltinFunctionPtrCast);
assert(sym);
FunctionCallEmitter emitters(sym->Variants[0].Implementation);
emitters.AddParameter(funcptr, false);
emitters.AddParameterPointerConst(ValueType);
emitters.AddReturn(REGT_POINTER);
return emitters.EmitCall(build);
}
//==========================================================================
//
// declares a single local variable (no arrays)
@ -11693,6 +12072,17 @@ FxExpression *FxLocalVariableDeclaration::Resolve(FCompileContext &ctx)
return nullptr;
}
SAFE_RESOLVE(Init, ctx);
if(Init->isConstant() && Init->ValueType == TypeRawFunction)
{
FxConstant *val = FxTypeCast::convertRawFunctionToFunctionPointer(Init, ScriptPosition);
if(!val)
{
delete this;
return nullptr;
}
}
ValueType = Init->ValueType;
if (ValueType->RegType == REGT_NIL)
{

View file

@ -277,6 +277,7 @@ enum EFxType
EFX_ReturnStatement,
EFX_ClassTypeCast,
EFX_ClassPtrCast,
EFX_FunctionPtrCast,
EFX_StateByIndex,
EFX_RuntimeStateIndex,
EFX_MultiNameState,
@ -512,6 +513,13 @@ public:
ValueType = value.Type = TypeVMFunction;
isresolved = true;
}
FxConstant(PFunction* rawptr, const FScriptPosition& pos) : FxExpression(EFX_Constant, pos)
{
value.pointer = rawptr;
ValueType = value.Type = TypeRawFunction;
isresolved = true;
}
FxConstant(const FScriptPosition &pos) : FxExpression(EFX_Constant, pos)
{
@ -557,6 +565,8 @@ public:
return value;
}
ExpEmit Emit(VMFunctionBuilder *build);
friend class FxTypeCast;
};
//==========================================================================
@ -735,6 +745,8 @@ public:
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build);
static FxConstant * convertRawFunctionToFunctionPointer(FxExpression * in, FScriptPosition &ScriptPosition);
};
//==========================================================================
@ -1818,6 +1830,7 @@ class FxVMFunctionCall : public FxExpression
bool CheckAccessibility(const VersionInfo &ver);
public:
const bool FnPtrCall;
FArgumentList ArgList;
PFunction* Function;
@ -2127,6 +2140,24 @@ public:
//
//==========================================================================
class FxFunctionPtrCast : public FxExpression
{
FxExpression *basex;
public:
FxFunctionPtrCast (PFunctionPointer *ftype, FxExpression *x);
~FxFunctionPtrCast();
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build);
};
//==========================================================================
//
//
//
//==========================================================================
class FxNop : public FxExpression
{
public:

View file

@ -290,6 +290,24 @@ unsigned VMFunctionBuilder::GetConstantAddress(void *ptr)
}
}
//==========================================================================
//
// VMFunctionBuilder :: FindConstantInt
//
// Returns a constant register initialized with the given value.
//
//==========================================================================
int VMFunctionBuilder::FindConstantInt(unsigned index)
{
if(IntConstantList.Size() < index)
{
return IntConstantList[index];
}
return 0;
}
//==========================================================================
//
// VMFunctionBuilder :: AllocConstants*
@ -298,7 +316,7 @@ unsigned VMFunctionBuilder::GetConstantAddress(void *ptr)
//
//==========================================================================
unsigned VMFunctionBuilder::AllocConstantsInt(unsigned count, int *values)
unsigned VMFunctionBuilder::AllocConstantsInt(unsigned int count, int *values)
{
unsigned addr = IntConstantList.Reserve(count);
memcpy(&IntConstantList[addr], values, count * sizeof(int));
@ -309,7 +327,7 @@ unsigned VMFunctionBuilder::AllocConstantsInt(unsigned count, int *values)
return addr;
}
unsigned VMFunctionBuilder::AllocConstantsFloat(unsigned count, double *values)
unsigned VMFunctionBuilder::AllocConstantsFloat(unsigned int count, double *values)
{
unsigned addr = FloatConstantList.Reserve(count);
memcpy(&FloatConstantList[addr], values, count * sizeof(double));
@ -320,7 +338,7 @@ unsigned VMFunctionBuilder::AllocConstantsFloat(unsigned count, double *values)
return addr;
}
unsigned VMFunctionBuilder::AllocConstantsAddress(unsigned count, void **ptrs)
unsigned VMFunctionBuilder::AllocConstantsAddress(unsigned int count, void **ptrs)
{
unsigned addr = AddressConstantList.Reserve(count);
memcpy(&AddressConstantList[addr], ptrs, count * sizeof(void *));
@ -331,7 +349,7 @@ unsigned VMFunctionBuilder::AllocConstantsAddress(unsigned count, void **ptrs)
return addr;
}
unsigned VMFunctionBuilder::AllocConstantsString(unsigned count, FString *ptrs)
unsigned VMFunctionBuilder::AllocConstantsString(unsigned int count, FString *ptrs)
{
unsigned addr = StringConstantList.Reserve(count);
for (unsigned i = 0; i < count; i++)
@ -949,6 +967,18 @@ void FFunctionBuildList::DumpJit(bool include_gzdoom_pk3)
#endif // HAVE_VM_JIT
}
FunctionCallEmitter::FunctionCallEmitter(VMFunction *func)
{
target = func;
is_vararg = target->VarFlags & VARF_VarArg;
}
FunctionCallEmitter::FunctionCallEmitter(class PFunctionPointer *func)
{
fnptr = func;
is_vararg = false; // function pointers cannot point to vararg functions
}
void FunctionCallEmitter::AddParameter(VMFunctionBuilder *build, FxExpression *operand)
{
@ -959,7 +989,7 @@ void FunctionCallEmitter::AddParameter(VMFunctionBuilder *build, FxExpression *o
operand->ScriptPosition.Message(MSG_ERROR, "Attempted to pass a non-value");
}
numparams += where.RegCount;
if (target->VarFlags & VARF_VarArg)
if (is_vararg)
for (unsigned i = 0; i < where.RegCount; i++) reginfo.Push(where.RegType & REGT_TYPE);
emitters.push_back([=](VMFunctionBuilder *build) -> int
@ -982,7 +1012,7 @@ void FunctionCallEmitter::AddParameter(VMFunctionBuilder *build, FxExpression *o
void FunctionCallEmitter::AddParameter(ExpEmit &emit, bool reference)
{
numparams += emit.RegCount;
if (target->VarFlags & VARF_VarArg)
if (is_vararg)
{
if (reference) reginfo.Push(REGT_POINTER);
else for (unsigned i = 0; i < emit.RegCount; i++) reginfo.Push(emit.RegType & REGT_TYPE);
@ -999,7 +1029,7 @@ void FunctionCallEmitter::AddParameter(ExpEmit &emit, bool reference)
void FunctionCallEmitter::AddParameterPointerConst(void *konst)
{
numparams++;
if (target->VarFlags & VARF_VarArg)
if (is_vararg)
reginfo.Push(REGT_POINTER);
emitters.push_back([=](VMFunctionBuilder *build) ->int
{
@ -1011,7 +1041,7 @@ void FunctionCallEmitter::AddParameterPointerConst(void *konst)
void FunctionCallEmitter::AddParameterPointer(int index, bool konst)
{
numparams++;
if (target->VarFlags & VARF_VarArg)
if (is_vararg)
reginfo.Push(REGT_POINTER);
emitters.push_back([=](VMFunctionBuilder *build) ->int
{
@ -1023,7 +1053,7 @@ void FunctionCallEmitter::AddParameterPointer(int index, bool konst)
void FunctionCallEmitter::AddParameterFloatConst(double konst)
{
numparams++;
if (target->VarFlags & VARF_VarArg)
if (is_vararg)
reginfo.Push(REGT_FLOAT);
emitters.push_back([=](VMFunctionBuilder *build) ->int
{
@ -1035,7 +1065,7 @@ void FunctionCallEmitter::AddParameterFloatConst(double konst)
void FunctionCallEmitter::AddParameterIntConst(int konst)
{
numparams++;
if (target->VarFlags & VARF_VarArg)
if (is_vararg)
reginfo.Push(REGT_INT);
emitters.push_back([=](VMFunctionBuilder *build) ->int
{
@ -1055,7 +1085,7 @@ void FunctionCallEmitter::AddParameterIntConst(int konst)
void FunctionCallEmitter::AddParameterStringConst(const FString &konst)
{
numparams++;
if (target->VarFlags & VARF_VarArg)
if (is_vararg)
reginfo.Push(REGT_STRING);
emitters.push_back([=](VMFunctionBuilder *build) ->int
{
@ -1074,7 +1104,7 @@ ExpEmit FunctionCallEmitter::EmitCall(VMFunctionBuilder *build, TArray<ExpEmit>
paramcount += func(build);
}
assert(paramcount == numparams);
if (target->VarFlags & VARF_VarArg)
if (is_vararg)
{
// Pass a hidden type information parameter to vararg functions.
// It would really be nicer to actually pass real types but that'd require a far more complex interface on the compiler side than what we have.
@ -1084,9 +1114,24 @@ ExpEmit FunctionCallEmitter::EmitCall(VMFunctionBuilder *build, TArray<ExpEmit>
paramcount++;
}
if(fnptr)
{
ExpEmit reg(build, REGT_POINTER);
assert(fnptr->Scope != -1);
assert(fnptr->PointedType != TypeVoid);
// OP_LP , Load from memory. rA = *(rB + rkC)
// reg = &PFunction->Variants[0] -- PFunction::Variant*
build->Emit(OP_LP, reg.RegNum, virtualselfreg, build->GetConstantInt(offsetof(PFunction, Variants) + offsetof(FArray, Array)));
// reg = (&PFunction->Variants[0])->Implementation -- VMFunction*
build->Emit(OP_LP, reg.RegNum, reg.RegNum, build->GetConstantInt(offsetof(PFunction::Variant, Implementation)));
if (virtualselfreg == -1)
build->Emit(OP_CALL, reg.RegNum, paramcount, vm_jit? static_cast<PPrototype*>(fnptr->PointedType)->ReturnTypes.Size() : returns.Size());
reg.Free(build);
}
else if (virtualselfreg == -1)
{
build->Emit(OP_CALL_K, build->GetConstantAddress(target), paramcount, vm_jit ? target->Proto->ReturnTypes.Size() : returns.Size());
}
@ -1110,9 +1155,13 @@ ExpEmit FunctionCallEmitter::EmitCall(VMFunctionBuilder *build, TArray<ExpEmit>
}
if (vm_jit) // The JIT compiler needs this, but the VM interpreter does not.
{
for (unsigned i = returns.Size(); i < target->Proto->ReturnTypes.Size(); i++)
assert(!fnptr || fnptr->PointedType != TypeVoid);
PPrototype * proto = fnptr ? static_cast<PPrototype*>(fnptr->PointedType) : target->Proto;
for (unsigned i = returns.Size(); i < proto->ReturnTypes.Size(); i++)
{
ExpEmit reg(build, target->Proto->ReturnTypes[i]->RegType, target->Proto->ReturnTypes[i]->RegCount);
ExpEmit reg(build, proto->ReturnTypes[i]->RegType, proto->ReturnTypes[i]->RegCount);
build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum);
reg.Free(build);
}

View file

@ -65,6 +65,11 @@ public:
unsigned GetConstantAddress(void *ptr);
unsigned GetConstantString(FString str);
int FindConstantInt(unsigned index);
//double FindConstantFloat(unsigned index);
//void * FindConstantAddress(unsigned index);
//const FString& FindConstantString(unsigned index);
unsigned AllocConstantsInt(unsigned int count, int *values);
unsigned AllocConstantsFloat(unsigned int count, double *values);
unsigned AllocConstantsAddress(unsigned int count, void **ptrs);
@ -180,13 +185,12 @@ class FunctionCallEmitter
TArray<uint8_t> reginfo;
unsigned numparams = 0; // This counts the number of pushed elements, which can differ from the number of emitters with vectors.
VMFunction *target = nullptr;
class PFunctionPointer *fnptr = nullptr;
int virtualselfreg = -1;
bool is_vararg;
public:
FunctionCallEmitter(VMFunction *func)
{
target = func;
}
FunctionCallEmitter(VMFunction *func);
FunctionCallEmitter(class PFunctionPointer *func);
void SetVirtualReg(int virtreg)
{

View file

@ -208,6 +208,14 @@ void FScopeBarrier::AddFlags(int flags1, int flags2, const char* name)
}
}
bool FScopeBarrier::CheckSidesForFunctionPointer(int from, int to)
{
if(to == -1) return true;
if(from == Side_Clear) from = Side_PlainData;
return ((from == to) || (from == Side_PlainData));
}
// these are for vmexec.h
void FScopeBarrier::ValidateNew(PClass* cls, int outerside)
{

View file

@ -62,6 +62,9 @@ struct FScopeBarrier
// This struct is used so that the logic is in a single place.
void AddFlags(int flags1, int flags2, const char* name);
static bool CheckSidesForFunctionPointer(int from, int to);
// this is called from vmexec.h
static void ValidateNew(PClass* cls, int scope);
static void ValidateCall(PClass* selftype, VMFunction *calledfunc, int outerside);

View file

@ -75,6 +75,7 @@ PStruct *TypeStringStruct;
PStruct* TypeQuaternionStruct;
PPointer *TypeNullPtr;
PPointer *TypeVoidPtr;
PPointer *TypeRawFunction;
PPointer* TypeVMFunction;
@ -323,6 +324,8 @@ void PType::StaticInit()
TypeTable.AddType(TypeTextureID = new PTextureID, NAME_TextureID);
TypeVoidPtr = NewPointer(TypeVoid, false);
TypeRawFunction = new PPointer;
TypeRawFunction->mDescriptiveName = "Raw Function Pointer";
TypeVMFunction = NewPointer(NewStruct("VMFunction", nullptr, true));
TypeColorStruct = NewStruct("@ColorStruct", nullptr); //This name is intentionally obfuscated so that it cannot be used explicitly. The point of this type is to gain access to the single channels of a color value.
TypeStringStruct = NewStruct("Stringstruct", nullptr, true);
@ -2818,6 +2821,239 @@ PMapIterator *NewMapIterator(PType *keyType, PType *valueType)
return (PMapIterator *)mapIteratorType;
}
/* PFunctionPointer *******************************************************/
//==========================================================================
//
// PFunctionPointer - Parameterized Constructor
//
//==========================================================================
static FString MakeFunctionPointerDescriptiveName(PPrototype * proto,const TArray<uint32_t> &ArgFlags, int scope)
{
FString mDescriptiveName;
mDescriptiveName = "Function<";
switch(scope)
{
case FScopeBarrier::Side_PlainData:
mDescriptiveName += "clearscope ";
break;
case FScopeBarrier::Side_Play:
mDescriptiveName += "play ";
break;
case FScopeBarrier::Side_UI:
mDescriptiveName += "ui ";
break;
}
if(proto->ReturnTypes.Size() > 0)
{
mDescriptiveName += proto->ReturnTypes[0]->DescriptiveName();
const unsigned n = proto->ReturnTypes.Size();
for(unsigned i = 1; i < n; i++)
{
mDescriptiveName += ", ";
mDescriptiveName += proto->ReturnTypes[i]->DescriptiveName();
}
mDescriptiveName += " (";
}
else
{
mDescriptiveName += "void (";
}
if(proto->ArgumentTypes.Size() > 0)
{
if(ArgFlags[0] == VARF_Out) mDescriptiveName += "out ";
mDescriptiveName += proto->ArgumentTypes[0]->DescriptiveName();
const unsigned n = proto->ArgumentTypes.Size();
for(unsigned i = 1; i < n; i++)
{
mDescriptiveName += ", ";
if(ArgFlags[i] == VARF_Out) mDescriptiveName += "out ";
mDescriptiveName += proto->ArgumentTypes[i]->DescriptiveName();
}
mDescriptiveName += ")>";
}
else
{
mDescriptiveName += "void)>";
}
return mDescriptiveName;
}
FString PFunctionPointer::GenerateNameForError(const PFunction * from)
{
return MakeFunctionPointerDescriptiveName(from->Variants[0].Proto, from->Variants[0].ArgFlags, FScopeBarrier::SideFromFlags(from->Variants[0].Flags));
}
PFunctionPointer::PFunctionPointer(PPrototype * proto, TArray<uint32_t> && argflags, int scope)
: PPointer(proto ? (PType*) proto : TypeVoid, false), ArgFlags(std::move(argflags)), Scope(scope)
{
if(!proto)
{
mDescriptiveName = "Function<void>";
}
else
{
mDescriptiveName = MakeFunctionPointerDescriptiveName(proto, ArgFlags, scope);
}
Flags |= TYPE_FunctionPointer;
if(proto)
{
assert(Scope != -1); // for now, a scope is always required
TArray<FName> ArgNames;
TArray<uint32_t> ArgFlags2(ArgFlags); // AddVariant calls std::move on this, so it needs to be a copy,
// but it takes it as a regular reference, so it needs to be a full variable instead of a temporary
ArgNames.Resize(ArgFlags.Size());
FakeFunction = Create<PFunction>();
FakeFunction->AddVariant(proto, ArgFlags2, ArgNames, nullptr, FScopeBarrier::FlagsFromSide(Scope), 0);
}
else
{
FakeFunction = nullptr;
}
}
void PFunctionPointer::WriteValue(FSerializer &ar, const char *key, const void *addr) const
{
auto p = *(const PFunction**)(addr);
if(p)
{
FunctionPointerValue val;
FunctionPointerValue *fpv = &val;
val.ClassName = FString((p->OwningClass ? p->OwningClass->TypeName : NAME_None).GetChars());
val.FunctionName = FString(p->SymbolName.GetChars());
SerializeFunctionPointer(ar, key, fpv);
}
else
{
FunctionPointerValue *fpv = nullptr;
SerializeFunctionPointer(ar, key, fpv);
}
}
PFunction *NativeFunctionPointerCast(PFunction *from, const PFunctionPointer *to);
bool PFunctionPointer::ReadValue(FSerializer &ar, const char *key, void *addr) const
{
FunctionPointerValue val;
FunctionPointerValue *fpv = &val;
SerializeFunctionPointer(ar, key, fpv);
PFunction ** fn = (PFunction**)(addr);
if(fpv)
{
auto cls = PClass::FindClass(val.ClassName);
if(!cls)
{
*fn = nullptr;
Printf(TEXTCOLOR_RED "Function Pointer ('%s::%s'): '%s' is not a valid class\n",
val.ClassName.GetChars(),
val.FunctionName.GetChars(),
val.ClassName.GetChars()
);
ar.mErrors++;
return false;
}
auto sym = cls->FindSymbol(FName(val.FunctionName), true);
if(!sym)
{
*fn = nullptr;
Printf(TEXTCOLOR_RED "Function Pointer ('%s::%s'): symbol '%s' does not exist in class '%s'\n",
val.ClassName.GetChars(),
val.FunctionName.GetChars(),
val.FunctionName.GetChars(),
val.ClassName.GetChars()
);
ar.mErrors++;
return false;
}
PFunction* p = dyn_cast<PFunction>(sym);
if(!p)
{
*fn = nullptr;
Printf(TEXTCOLOR_RED "Function Pointer (%s::%s): '%s' in class '%s' is a variable, not a function\n",
val.ClassName.GetChars(),
val.FunctionName.GetChars(),
val.FunctionName.GetChars(),
val.ClassName.GetChars()
);
ar.mErrors++;
return false;
}
*fn = NativeFunctionPointerCast(p, this);
if(!*fn)
{
if((p->Variants[0].Flags & (VARF_Action | VARF_Virtual)) != 0)
{
*fn = nullptr;
Printf(TEXTCOLOR_RED "Function Pointer (%s::%s): function '%s' in class '%s' is %s, not a static function\n",
val.ClassName.GetChars(),
val.FunctionName.GetChars(),
val.FunctionName.GetChars(),
val.ClassName.GetChars(),
(p->GetImplicitArgs() == 1 ? "a virtual function" : "an action function")
);
}
else
{
FString fn_name = MakeFunctionPointerDescriptiveName(p->Variants[0].Proto,p->Variants[0].ArgFlags, FScopeBarrier::SideFromFlags(p->Variants[0].Flags));
Printf(TEXTCOLOR_RED "Function Pointer (%s::%s) has incompatible type (Pointer is '%s', Function is '%s')\n",
val.ClassName.GetChars(),
val.FunctionName.GetChars(),
fn_name.GetChars(),
mDescriptiveName.GetChars()
);
}
ar.mErrors++;
return false;
}
return true;
}
else
{
*fn = nullptr;
}
return true;
}
bool PFunctionPointer::IsMatch(intptr_t id1, intptr_t id2) const
{
const PPrototype * proto = (const PPrototype*) id1;
const PFunctionPointer::FlagsAndScope * flags_and_scope = (const PFunctionPointer::FlagsAndScope *) id2;
return (proto == (PointedType == TypeVoid ? nullptr : PointedType))
&& (Scope == flags_and_scope->Scope)
&& (ArgFlags == *flags_and_scope->ArgFlags);
}
void PFunctionPointer::GetTypeIDs(intptr_t &id1, intptr_t &id2) const
{ //NOT SUPPORTED
assert(0 && "GetTypeIDs not supported for PFunctionPointer");
}
PFunctionPointer * NewFunctionPointer(PPrototype * proto, TArray<uint32_t> && argflags, int scope)
{
size_t bucket;
PFunctionPointer::FlagsAndScope flags_and_scope { &argflags, scope };
PType *fn = TypeTable.FindType(NAME_Function, (intptr_t)proto, (intptr_t)&flags_and_scope, &bucket);
if (fn == nullptr)
{
fn = new PFunctionPointer(proto, std::move(argflags), scope);
flags_and_scope.ArgFlags = &static_cast<PFunctionPointer *>(fn)->ArgFlags;
TypeTable.AddType(fn, NAME_Function, (intptr_t)proto, (intptr_t)&flags_and_scope, bucket);
}
return (PFunctionPointer *)fn;
}
/* PStruct ****************************************************************/
//==========================================================================
@ -3227,13 +3463,13 @@ size_t FTypeTable::Hash(FName p1, intptr_t p2, intptr_t p3)
// to transform this into a ROR or ROL.
i1 = (i1 >> (sizeof(size_t)*4)) | (i1 << (sizeof(size_t)*4));
if (p1 != NAME_Prototype)
if (p1 != NAME_Prototype && p1 != NAME_Function)
{
size_t i2 = (size_t)p2;
size_t i3 = (size_t)p3;
return (~i1 ^ i2) + i3 * 961748927; // i3 is multiplied by a prime
}
else
else if(p1 == NAME_Prototype)
{ // Prototypes need to hash the TArrays at p2 and p3
const TArray<PType *> *a2 = (const TArray<PType *> *)p2;
const TArray<PType *> *a3 = (const TArray<PType *> *)p3;
@ -3247,6 +3483,18 @@ size_t FTypeTable::Hash(FName p1, intptr_t p2, intptr_t p3)
}
return i1;
}
else // if(p1 == NAME_Function)
{ // functions need custom hashing as well
size_t i2 = (size_t)p2;
const PFunctionPointer::FlagsAndScope * flags_and_scope = (const PFunctionPointer::FlagsAndScope *) p3;
const TArray<uint32_t> * a3 = flags_and_scope->ArgFlags;
i1 = (~i1 ^ i2);
for (unsigned i = 0; i < a3->Size(); ++i)
{
i1 = (i1 * 961748927) + (size_t)((*a3)[i]);
}
return (i1 * 961748927) + (size_t)flags_and_scope->Scope;
}
}
//==========================================================================

View file

@ -66,6 +66,7 @@ enum
class PContainerType;
class PPointer;
class PClassPointer;
class PFunctionPointer;
class PArray;
class PStruct;
class PClassType;
@ -86,6 +87,7 @@ protected:
TYPE_ObjectPointer = 64,
TYPE_ClassPointer = 128,
TYPE_Array = 256,
TYPE_FunctionPointer = 512,
TYPE_IntCompatible = TYPE_Int | TYPE_IntNotInt, // must be the combination of all flags that are subtypes of int and can be cast to an int.
};
@ -194,9 +196,10 @@ public:
bool isIntCompatible() const { return !!(Flags & TYPE_IntCompatible); }
bool isFloat() const { return !!(Flags & TYPE_Float); }
bool isPointer() const { return !!(Flags & TYPE_Pointer); }
bool isRealPointer() const { return (Flags & (TYPE_Pointer|TYPE_ClassPointer)) == TYPE_Pointer; } // This excludes class pointers which use their PointedType differently
bool isRealPointer() const { return (Flags & (TYPE_Pointer | TYPE_ClassPointer | TYPE_FunctionPointer)) == TYPE_Pointer; } // This excludes class and function pointers which use their PointedType differently
bool isObjectPointer() const { return !!(Flags & TYPE_ObjectPointer); }
bool isClassPointer() const { return !!(Flags & TYPE_ClassPointer); }
bool isFunctionPointer() const { return !!(Flags & TYPE_FunctionPointer); }
bool isEnum() const { return TypeTableType == NAME_Enum; }
bool isArray() const { return !!(Flags & TYPE_Array); }
bool isStaticArray() const { return TypeTableType == NAME_StaticArray; }
@ -210,6 +213,7 @@ public:
PContainerType *toContainer() { return isContainer() ? (PContainerType*)this : nullptr; }
PPointer *toPointer() { return isPointer() ? (PPointer*)this : nullptr; }
static PClassPointer *toClassPointer(PType *t) { return t && t->isClassPointer() ? (PClassPointer*)t : nullptr; }
static PFunctionPointer *toFunctionPointer(PType *t) { return t && t->isFunctionPointer() ? (PFunctionPointer*)t : nullptr; }
static PClassType *toClass(PType *t) { return t && t->isClass() ? (PClassType*)t : nullptr; }
};
@ -595,6 +599,33 @@ public:
void DestroyValue(void *addr) const override;
};
class PFunctionPointer : public PPointer
{
public:
//PointedType = PPrototype or TypeVoid
PFunctionPointer(PPrototype * proto, TArray<uint32_t> &&argflags, int scope);
static FString GenerateNameForError(const PFunction * from);
TArray<uint32_t> ArgFlags;
int Scope;
PFunction *FakeFunction; // used for type checking in FxFunctionCall
void WriteValue(FSerializer &ar, const char *key, const void *addr) const override;
bool ReadValue(FSerializer &ar, const char *key, void *addr) const override;
struct FlagsAndScope
{ // used for IsMatch's id2
TArray<uint32_t> * ArgFlags;
int Scope;
};
bool IsMatch(intptr_t id1, intptr_t id2) const override;
void GetTypeIDs(intptr_t &id1, intptr_t &id2) const override; //NOT SUPPORTED
};
class PStruct : public PContainerType
{
public:
@ -657,6 +688,7 @@ PMapIterator *NewMapIterator(PType *keytype, PType *valuetype);
PArray *NewArray(PType *type, unsigned int count);
PStaticArray *NewStaticArray(PType *type);
PDynArray *NewDynArray(PType *type);
PFunctionPointer *NewFunctionPointer(PPrototype * proto, TArray<uint32_t> && argflags, int scope);
PPointer *NewPointer(PType *type, bool isconst = false);
PPointer *NewPointer(PClass *type, bool isconst = false);
PClassPointer *NewClassPointer(PClass *restrict);
@ -697,6 +729,7 @@ extern PPointer *TypeFont;
extern PStateLabel *TypeStateLabel;
extern PPointer *TypeNullPtr;
extern PPointer *TypeVoidPtr;
extern PPointer* TypeRawFunction;
extern PPointer* TypeVMFunction;

View file

@ -46,6 +46,9 @@
#define LKP MODE_AP | MODE_BCJOINT | MODE_BCKP
#define LFP MODE_AP | MODE_BUNUSED | MODE_CUNUSED
#define RP MODE_AP | MODE_BUNUSED | MODE_CUNUSED
#define RIRPKI MODE_AI | MODE_BP | MODE_CKI
#define RIRPRI MODE_AI | MODE_BP | MODE_CI
#define RFRPKI MODE_AF | MODE_BP | MODE_CKI

View file

@ -521,6 +521,26 @@ static void PrintDynArrayType(FLispString &out, const ZCC_TreeNode *node)
out.Close();
}
static void PrintFuncPtrParamDecl(FLispString &out, const ZCC_TreeNode *node)
{
ZCC_FuncPtrParamDecl *dnode = (ZCC_FuncPtrParamDecl *)node;
out.Break();
out.Open("func-ptr-param-decl");
PrintNodes(out, dnode->Type);
out.AddHex(dnode->Flags);
out.Close();
}
static void PrintFuncPtrType(FLispString &out, const ZCC_TreeNode *node){
ZCC_FuncPtrType *dnode = (ZCC_FuncPtrType *)node;
out.Break();
out.Open("func-ptr-type");
PrintNodes(out, dnode->RetType);
PrintNodes(out, dnode->Params);
out.AddHex(dnode->Scope);
out.Close();
}
static void PrintClassType(FLispString &out, const ZCC_TreeNode *node)
{
ZCC_ClassType *tnode = (ZCC_ClassType *)node;
@ -628,6 +648,16 @@ static void PrintExprClassCast(FLispString &out, const ZCC_TreeNode *node)
out.Close();
}
static void PrintExprFunctionPtrCast(FLispString &out, const ZCC_TreeNode *node)
{
ZCC_FunctionPtrCast *enode = (ZCC_FunctionPtrCast *)node;
assert(enode->Operation == PEX_FunctionPtrCast);
out.Open("expr-func-ptr-cast");
PrintNodes(out, enode->PtrType, false);
PrintNodes(out, enode->Expr, false);
out.Close();
}
static void PrintStaticArray(FLispString &out, const ZCC_TreeNode *node)
{
ZCC_StaticArrayStatement *enode = (ZCC_StaticArrayStatement *)node;
@ -976,6 +1006,8 @@ static const NodePrinterFunc TreeNodePrinter[] =
PrintMapType,
PrintMapIteratorType,
PrintDynArrayType,
PrintFuncPtrParamDecl,
PrintFuncPtrType,
PrintClassType,
PrintExpression,
PrintExprID,
@ -1011,6 +1043,7 @@ static const NodePrinterFunc TreeNodePrinter[] =
PrintVectorInitializer,
PrintDeclFlags,
PrintExprClassCast,
PrintExprFunctionPtrCast,
PrintStaticArrayState,
PrintProperty,
PrintFlagDef,

View file

@ -988,6 +988,71 @@ aggregate_type(X) ::= ARRAY(T) LT type_or_array(A) GT. /* TArray<type> */
X = arr;
}
aggregate_type(X) ::= func_ptr_type(A). { X = A; /*X-overwrites-A*/ }
%type func_ptr_type {ZCC_FuncPtrType *}
%type func_ptr_params {ZCC_FuncPtrParamDecl *}
%type func_ptr_param_list {ZCC_FuncPtrParamDecl *}
%type func_ptr_param {ZCC_FuncPtrParamDecl *}
//fn_ptr_flag(X) ::= . { X.Int = 0; } //implicit scope not allowed
fn_ptr_flag(X) ::= UI. { X.Int = ZCC_UIFlag; }
fn_ptr_flag(X) ::= PLAY. { X.Int = ZCC_Play; }
fn_ptr_flag(X) ::= CLEARSCOPE. { X.Int = ZCC_ClearScope; }
//fn_ptr_flag(X) ::= VIRTUALSCOPE. { X.Int = ZCC_VirtualScope; } //virtual scope not allowed
func_ptr_type(X) ::= FNTYPE(T) LT fn_ptr_flag(F) type_list_or_void(A) LPAREN func_ptr_params(B) RPAREN GT. /* Function<...(...)> */
{
NEW_AST_NODE(FuncPtrType,fn_ptr,T);
fn_ptr->RetType = A;
fn_ptr->Params = B;
fn_ptr->Scope = F.Int;
X = fn_ptr;
}
func_ptr_type(X) ::= FNTYPE(T) LT VOID GT. /* Function<void> */
{
NEW_AST_NODE(FuncPtrType,fn_ptr,T);
fn_ptr->RetType = nullptr;
fn_ptr->Params = nullptr;
fn_ptr->Scope = -1;
X = fn_ptr;
}
func_ptr_params(X) ::= . /* empty */ { X = NULL; }
func_ptr_params(X) ::= VOID. { X = NULL; }
func_ptr_params(X) ::= func_ptr_param_list(X).
// varargs function pointers not currently supported
//func_ptr_params(X) ::= func_ptr_param_list(A) COMMA ELLIPSIS.
//{
// NEW_AST_NODE(FuncPtrParamDecl,parm,stat->sc->GetMessageLine());
// parm->Type = nullptr;
// parm->Flags = 0;
// X = A; /*X-overwrites-A*/
// AppendTreeNodeSibling(X, parm);
//}
func_ptr_param_list(X) ::= func_ptr_param(X).
func_ptr_param_list(X) ::= func_ptr_param_list(A) COMMA func_ptr_param(B). { X = A; /*X-overwrites-A*/ AppendTreeNodeSibling(X, B); }
func_ptr_param(X) ::= func_param_flags(A) type(B).
{
NEW_AST_NODE(FuncPtrParamDecl,parm,A.SourceLoc ? A.SourceLoc : B->SourceLoc);
parm->Type = B;
parm->Flags = A.Int;
X = parm;
}
func_ptr_param(X) ::= func_param_flags(A) type(B) AND.
{
NEW_AST_NODE(FuncPtrParamDecl,parm,A.SourceLoc ? A.SourceLoc : B->SourceLoc);
parm->Type = B;
parm->Flags = A.Int | ZCC_Out;
X = parm;
}
aggregate_type(X) ::= CLASS(T) class_restrictor(A). /* class<type> */
{
NEW_AST_NODE(ClassType,cls,T);
@ -1408,6 +1473,17 @@ primary(X) ::= LPAREN CLASS LT IDENTIFIER(A) GT RPAREN LPAREN func_expr_list(B)
expr->Parameters = B;
X = expr;
}
primary(X) ::= LPAREN func_ptr_type(A) RPAREN LPAREN expr(B) RPAREN. [DOT] // function pointer type cast
{
NEW_AST_NODE(FunctionPtrCast, expr, A);
expr->Operation = PEX_FunctionPtrCast;
A->ArraySize = NULL;
expr->PtrType = A;
expr->Expr = B;
X = expr;
}
primary(X) ::= primary(A) LBRACKET expr(B) RBRACKET. [DOT] // Array access
{
NEW_AST_NODE(ExprBinary, expr, B);
@ -1417,6 +1493,7 @@ primary(X) ::= primary(A) LBRACKET expr(B) RBRACKET. [DOT] // Array access
expr->Right = B;
X = expr;
}
primary(X) ::= primary(A) DOT IDENTIFIER(B). // Member access
{
NEW_AST_NODE(ExprMemberAccess, expr, B);

View file

@ -2024,6 +2024,52 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
}
break;
}
case AST_FuncPtrType:
{
auto fn = static_cast<ZCC_FuncPtrType*>(ztype);
if(fn->Scope == -1)
{ // Function<void>
retval = NewFunctionPointer(nullptr, {}, -1);
}
else
{
TArray<PType*> returns;
TArray<PType*> args;
TArray<uint32_t> argflags;
if(auto *t = fn->RetType; t != nullptr) do {
returns.Push(DetermineType(outertype, field, name, t, false, false));
} while( (t = (ZCC_Type *)t->SiblingNext) != fn->RetType);
if(auto *t = fn->Params; t != nullptr) do {
args.Push(DetermineType(outertype, field, name, t->Type, false, false));
argflags.Push(t->Flags == ZCC_Out ? VARF_Out : 0);
} while( (t = (ZCC_FuncPtrParamDecl *) t->SiblingNext) != fn->Params);
auto proto = NewPrototype(returns,args);
switch(fn->Scope)
{ // only play/ui/clearscope functions are allowed, no data or virtual scope functions
case ZCC_Play:
fn->Scope = FScopeBarrier::Side_Play;
break;
case ZCC_UIFlag:
fn->Scope = FScopeBarrier::Side_UI;
break;
case ZCC_ClearScope:
fn->Scope = FScopeBarrier::Side_PlainData;
break;
case 0:
fn->Scope = -1;
break;
default:
Error(field, "Invalid Scope for Function Pointer");
break;
}
retval = NewFunctionPointer(proto, std::move(argflags), fn->Scope);
}
break;
}
case AST_ClassType:
{
auto ctype = static_cast<ZCC_ClassType *>(ztype);
@ -2992,6 +3038,17 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast, bool substitute)
return new FxClassPtrCast(cls, ConvertNode(cc->Parameters));
}
case AST_FunctionPtrCast:
{
auto cast = static_cast<ZCC_FunctionPtrCast *>(ast);
auto type = DetermineType(ConvertClass, cast, NAME_None, cast->PtrType, false, false);
assert(type->isFunctionPointer());
auto ptrType = static_cast<PFunctionPointer*>(type);
return new FxFunctionPtrCast(ptrType, ConvertNode(cast->Expr));
}
case AST_StaticArrayStatement:
{
auto sas = static_cast<ZCC_StaticArrayStatement *>(ast);

View file

@ -9,6 +9,7 @@ xx(FuncCall, '(')
xx(ArrayAccess, TK_Array)
xx(MemberAccess, '.')
xx(ClassCast, TK_Class)
xx(FunctionPtrCast, TK_FunctionType)
xx(TypeRef, TK_Class)
xx(Vector, TK_Vector2)

View file

@ -222,6 +222,7 @@ static void InitTokenMap()
TOKENDEF2(TK_Map, ZCC_MAP, NAME_Map);
TOKENDEF2(TK_MapIterator, ZCC_MAPITERATOR,NAME_MapIterator);
TOKENDEF2(TK_Array, ZCC_ARRAY, NAME_Array);
TOKENDEF2(TK_FunctionType, ZCC_FNTYPE, NAME_Function);
TOKENDEF2(TK_Include, ZCC_INCLUDE, NAME_Include);
TOKENDEF (TK_Void, ZCC_VOID);
TOKENDEF (TK_True, ZCC_TRUE);
@ -925,6 +926,29 @@ ZCC_TreeNode *TreeNodeDeepCopy_Internal(ZCC_AST *ast, ZCC_TreeNode *orig, bool c
break;
}
case AST_FuncPtrParamDecl:
{
TreeNodeDeepCopy_Start(FuncPtrParamDecl);
// ZCC_FuncPtrParamDecl
copy->Type = static_cast<ZCC_Type *>(TreeNodeDeepCopy_Internal(ast, origCasted->Type, true, copiedNodesList));
copy->Flags = origCasted->Flags;
break;
}
case AST_FuncPtrType:
{
TreeNodeDeepCopy_Start(FuncPtrType);
// ZCC_FuncPtrType
copy->RetType = static_cast<ZCC_Type *>(TreeNodeDeepCopy_Internal(ast, origCasted->RetType, true, copiedNodesList));
copy->Params = static_cast<ZCC_FuncPtrParamDecl *>(TreeNodeDeepCopy_Internal(ast, origCasted->Params, true, copiedNodesList));
copy->Scope = origCasted->Scope;
break;
}
case AST_ClassType:
{
TreeNodeDeepCopy_Start(ClassType);
@ -1371,7 +1395,21 @@ ZCC_TreeNode *TreeNodeDeepCopy_Internal(ZCC_AST *ast, ZCC_TreeNode *orig, bool c
break;
}
case AST_FunctionPtrCast:
{
TreeNodeDeepCopy_Start(FunctionPtrCast);
// ZCC_Expression
copy->Operation = origCasted->Operation;
copy->Type = origCasted->Type;
// ZCC_FunctionPtrCast
copy->PtrType = static_cast<ZCC_FuncPtrType *>(TreeNodeDeepCopy_Internal(ast, origCasted->PtrType, true, copiedNodesList));
copy->Expr = static_cast<ZCC_Expression *>(TreeNodeDeepCopy_Internal(ast, origCasted->Expr, true, copiedNodesList));
break;
}
case AST_StaticArrayStatement:
{
TreeNodeDeepCopy_Start(StaticArrayStatement);

View file

@ -101,6 +101,8 @@ enum EZCCTreeNodeType
AST_MapType,
AST_MapIteratorType,
AST_DynArrayType,
AST_FuncPtrParamDecl,
AST_FuncPtrType,
AST_ClassType,
AST_Expression,
AST_ExprID,
@ -136,6 +138,7 @@ enum EZCCTreeNodeType
AST_VectorValue,
AST_DeclFlags,
AST_ClassCast,
AST_FunctionPtrCast,
AST_StaticArrayStatement,
AST_Property,
AST_FlagDef,
@ -382,6 +385,19 @@ struct ZCC_DynArrayType : ZCC_Type
ZCC_Type *ElementType;
};
struct ZCC_FuncPtrParamDecl : ZCC_TreeNode
{
ZCC_Type *Type;
int Flags;
};
struct ZCC_FuncPtrType : ZCC_Type
{
ZCC_Type *RetType;
ZCC_FuncPtrParamDecl *Params;
int Scope;
};
struct ZCC_ClassType : ZCC_Type
{
ZCC_Identifier *Restriction;
@ -428,6 +444,12 @@ struct ZCC_ClassCast : ZCC_Expression
ZCC_FuncParm *Parameters;
};
struct ZCC_FunctionPtrCast : ZCC_Expression
{
ZCC_FuncPtrType *PtrType;
ZCC_Expression *Expr;
};
struct ZCC_ExprMemberAccess : ZCC_Expression
{
ZCC_Expression *Left;

View file

@ -54,6 +54,7 @@
#include "i_time.h"
#include "maps.h"
#include "types.h"
static ZSMap<FName, DObject*> AllServices;
@ -847,6 +848,13 @@ DEFINE_ACTION_FUNCTION(_Wads, ReadLump)
ACTION_RETURN_STRING(isLumpValid ? GetStringFromLump(lump, false) : FString());
}
DEFINE_ACTION_FUNCTION(_Wads, GetLumpLength)
{
PARAM_PROLOGUE;
PARAM_INT(lump);
ACTION_RETURN_INT(fileSystem.FileLength(lump));
}
//==========================================================================
//
// CVARs
@ -1348,3 +1356,19 @@ DEFINE_ACTION_FUNCTION_NATIVE(_QuatStruct, Inverse, QuatInverse)
QuatInverse(self->X, self->Y, self->Z, self->W, &quat);
ACTION_RETURN_QUAT(quat);
}
PFunction * FindFunctionPointer(PClass * cls, int fn_name)
{
auto fn = dyn_cast<PFunction>(cls->FindSymbol(ENamedName(fn_name), true));
return (fn && (fn->Variants[0].Flags & (VARF_Action | VARF_Virtual)) == 0 ) ? fn : nullptr;
}
DEFINE_ACTION_FUNCTION_NATIVE(DObject, FindFunction, FindFunctionPointer)
{
PARAM_PROLOGUE;
PARAM_CLASS(cls, DObject);
PARAM_NAME(fn);
ACTION_RETURN_POINTER(FindFunctionPointer(cls, fn.GetIndex()));
}

View file

@ -719,3 +719,8 @@ asmjit::FuncSignature JitCompiler::CreateFuncSignature()
signature.init(CallConv::kIdHost, rettype, cachedArgs->Data(), cachedArgs->Size());
return signature;
}
void JitCompiler::EmitNULLCHECK()
{
EmitNullPointerThrow(A, X_READ_NIL);
}

View file

@ -1937,6 +1937,15 @@ static int ExecScriptFunc(VMFrameStack *stack, VMReturn *ret, int numret)
CMPJMP(reg.a[B] == konsta[C].v);
NEXTOP;
OP(NULLCHECK):
ASSERTA(a);
if (PA == nullptr)
{
ThrowAbortException(X_WRITE_NIL, nullptr);
return 0;
}
NEXTOP;
OP(NOP):
NEXTOP;
}

View file

@ -289,4 +289,7 @@ xx(SUBA, sub, RIRPRP, NOP, 0, 0) // dA = pB - pC
xx(EQA_R, beq, CPRR, NOP, 0, 0) // if ((pB == pkC) != A) then pc++
xx(EQA_K, beq, CPRK, EQA_R, 4, REGT_POINTER)
// Null check
xx(NULLCHECK, nullcheck, RP, NOP, 0, 0) // EmitNullPointerThrow(pA)
#undef xx

View file

@ -455,6 +455,13 @@ public:
return start;
}
unsigned AddUnique(const T& obj)
{
auto f = Find(obj);
if (f == Size()) Push(obj);
return f;
}
bool Pop ()
{
if (Count > 0)

View file

@ -61,7 +61,7 @@ void FHWModelRenderer::EndDrawModel(FRenderStyle style, FSpriteModelFrame *smf)
state.SetCulling(Cull_None);
}
void FHWModelRenderer::BeginDrawHUDModel(FRenderStyle style, const VSMatrix &objectToWorldMatrix, bool mirrored)
void FHWModelRenderer::BeginDrawHUDModel(FRenderStyle style, const VSMatrix &objectToWorldMatrix, bool mirrored, FSpriteModelFrame*)
{
state.SetDepthFunc(DF_LEqual);
state.SetCulling(mirrored ? Cull_CCW : Cull_CW);
@ -69,7 +69,7 @@ void FHWModelRenderer::BeginDrawHUDModel(FRenderStyle style, const VSMatrix &obj
state.EnableModelMatrix(true);
}
void FHWModelRenderer::EndDrawHUDModel(FRenderStyle style)
void FHWModelRenderer::EndDrawHUDModel(FRenderStyle style, FSpriteModelFrame*)
{
state.EnableModelMatrix(false);

View file

@ -46,8 +46,8 @@ public:
void EndDrawModel(FRenderStyle style, FSpriteModelFrame *smf) override;
IModelVertexBuffer *CreateVertexBuffer(bool needindex, bool singleframe) override;
VSMatrix GetViewToWorldMatrix() override;
void BeginDrawHUDModel(FRenderStyle style, const VSMatrix &objectToWorldMatrix, bool mirrored) override;
void EndDrawHUDModel(FRenderStyle style) override;
void BeginDrawHUDModel(FRenderStyle style, const VSMatrix &objectToWorldMatrix, bool mirrored, FSpriteModelFrame* smf) override;
void EndDrawHUDModel(FRenderStyle style, FSpriteModelFrame* smf) override;
void SetInterpolation(double interpolation) override;
void SetMaterial(FGameTexture *skin, bool clampNoFilter, int translation) override;
void DrawArrays(int start, int count) override;