mirror of
https://github.com/ZDoom/Raze.git
synced 2024-11-26 05:51:30 +00:00
Backend update from GZDoom.
This commit is contained in:
parent
0bcfd5ca92
commit
c6e5ade0b3
31 changed files with 1213 additions and 66 deletions
|
@ -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)
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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'")
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 **)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue