Merge remote-tracking branch 'gzdoom/master' into merge-gzdoom
This commit is contained in:
commit
e75e5a387b
600 changed files with 40006 additions and 59374 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -1394,7 +1404,7 @@ FxExpression *FxColorCast::Resolve(FCompileContext &ctx)
|
|||
}
|
||||
else
|
||||
{
|
||||
FxExpression *x = new FxConstant(V_GetColor(constval.GetString(), &ScriptPosition), ScriptPosition);
|
||||
FxExpression *x = new FxConstant(V_GetColor(constval.GetString().GetChars(), &ScriptPosition), ScriptPosition);
|
||||
delete this;
|
||||
return x;
|
||||
}
|
||||
|
|
@ -1474,7 +1484,7 @@ FxExpression *FxSoundCast::Resolve(FCompileContext &ctx)
|
|||
if (basex->isConstant())
|
||||
{
|
||||
ExpVal constval = static_cast<FxConstant *>(basex)->GetValue();
|
||||
FxExpression *x = new FxConstant(S_FindSound(constval.GetString()), ScriptPosition);
|
||||
FxExpression *x = new FxConstant(S_FindSound(constval.GetString().GetChars()), ScriptPosition);
|
||||
delete this;
|
||||
return x;
|
||||
}
|
||||
|
|
@ -1552,7 +1562,7 @@ FxExpression *FxFontCast::Resolve(FCompileContext &ctx)
|
|||
else if ((basex->ValueType == TypeString || basex->ValueType == TypeName) && basex->isConstant())
|
||||
{
|
||||
ExpVal constval = static_cast<FxConstant *>(basex)->GetValue();
|
||||
FFont *font = V_GetFont(constval.GetString());
|
||||
FFont *font = V_GetFont(constval.GetString().GetChars());
|
||||
// Font must exist. Most internal functions working with fonts do not like null pointers.
|
||||
// If checking is needed scripts will have to call Font.GetFont themselves.
|
||||
if (font == nullptr)
|
||||
|
|
@ -1608,6 +1618,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 +1658,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)
|
||||
{
|
||||
|
|
@ -2454,7 +2509,17 @@ FxExpression *FxAssign::Resolve(FCompileContext &ctx)
|
|||
SAFE_RESOLVE(Right, ctx);
|
||||
}
|
||||
}
|
||||
else if (Base->ValueType == Right->ValueType)
|
||||
else if (Right->IsNativeStruct() && Base->ValueType->isRealPointer() && Base->ValueType->toPointer()->PointedType == Right->ValueType)
|
||||
{
|
||||
// allow conversion of native structs to pointers of the same type. This is necessary to assign elements from global arrays like players, sectors, etc. to local pointers.
|
||||
// For all other types this is not needed. Structs are not assignable and classes can only exist as references.
|
||||
Right->RequestAddress(ctx, nullptr);
|
||||
Right->ValueType = Base->ValueType;
|
||||
}
|
||||
else if ( Base->ValueType == Right->ValueType
|
||||
|| (Base->ValueType->isRealPointer() && Base->ValueType->toPointer()->PointedType == Right->ValueType)
|
||||
|| (Right->ValueType->isRealPointer() && Right->ValueType->toPointer()->PointedType == Base->ValueType)
|
||||
)
|
||||
{
|
||||
if (Base->ValueType->isArray())
|
||||
{
|
||||
|
|
@ -2462,28 +2527,103 @@ FxExpression *FxAssign::Resolve(FCompileContext &ctx)
|
|||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
else if (Base->IsDynamicArray())
|
||||
else if(!Base->IsVector() && !Base->IsQuaternion())
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Cannot assign dynamic arrays, use Copy() or Move() function instead");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
if (!Base->IsVector() && !Base->IsQuaternion() && Base->ValueType->isStruct())
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Struct assignment not implemented yet");
|
||||
delete this;
|
||||
return nullptr;
|
||||
PType * btype = Base->ValueType;
|
||||
|
||||
if(btype->isRealPointer())
|
||||
{
|
||||
PType * p = static_cast<PPointer *>(btype)->PointedType;
|
||||
if( p->isDynArray() || p->isMap()
|
||||
|| (p->isStruct() && !static_cast<PStruct*>(p)->isNative)
|
||||
)
|
||||
{ //un-pointer dynarrays, maps and non-native structs for assignment checking
|
||||
btype = p;
|
||||
}
|
||||
}
|
||||
|
||||
if (btype->isDynArray())
|
||||
{
|
||||
if(ctx.Version >= MakeVersion(4, 11, 1))
|
||||
{
|
||||
FArgumentList args;
|
||||
args.Push(Right);
|
||||
auto call = new FxMemberFunctionCall(Base, NAME_Copy, args, ScriptPosition);
|
||||
Right = Base = nullptr;
|
||||
delete this;
|
||||
return call->Resolve(ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(Base->ValueType->isRealPointer() && Right->ValueType->isRealPointer())
|
||||
{
|
||||
ScriptPosition.Message(MSG_WARNING, "Dynamic Array assignments not allowed in ZScript versions below 4.11.1, use the Copy() or Move() functions instead\n"
|
||||
TEXTCOLOR_RED " Assigning an out array pointer to another out array pointer\n"
|
||||
" does not alter either of the underlying arrays' values\n"
|
||||
" it only swaps the pointers below ZScript version 4.11.1!!");
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Dynamic Array assignments not allowed in ZScript versions below 4.11.1, use the Copy() or Move() functions instead");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (btype->isMap())
|
||||
{
|
||||
if(ctx.Version >= MakeVersion(4, 11, 1))
|
||||
{
|
||||
FArgumentList args;
|
||||
args.Push(Right);
|
||||
auto call = new FxMemberFunctionCall(Base, NAME_Copy, args, ScriptPosition);
|
||||
Right = Base = nullptr;
|
||||
delete this;
|
||||
return call->Resolve(ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(Base->ValueType->isRealPointer() && Right->ValueType->isRealPointer())
|
||||
{ // don't break existing code, but warn that it's a no-op
|
||||
ScriptPosition.Message(MSG_WARNING, "Map assignments not allowed in ZScript versions below 4.11.1, use the Copy() or Move() functions instead\n"
|
||||
TEXTCOLOR_RED " Assigning an out map pointer to another out map pointer\n"
|
||||
" does not alter either of the underlying maps' values\n"
|
||||
" it only swaps the pointers below ZScript version 4.11.1!!");
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Map assignments not allowed in ZScript versions below 4.11.1, use the Copy() or Move() functions instead");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (btype->isMapIterator())
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Cannot assign map iterators");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
else if (btype->isStruct())
|
||||
{
|
||||
if(Base->ValueType->isRealPointer() && Right->ValueType->isRealPointer())
|
||||
{ // don't break existing code, but warn that it's a no-op
|
||||
ScriptPosition.Message(MSG_WARNING, "Struct assignment not implemented yet\n"
|
||||
TEXTCOLOR_RED " Assigning an out struct pointer to another out struct pointer\n"
|
||||
" does not alter either of the underlying structs' values\n"
|
||||
" it only swaps the pointers!!");
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Struct assignment not implemented yet");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Both types are the same so this is ok.
|
||||
}
|
||||
else if (Right->IsNativeStruct() && Base->ValueType->isRealPointer() && Base->ValueType->toPointer()->PointedType == Right->ValueType)
|
||||
{
|
||||
// allow conversion of native structs to pointers of the same type. This is necessary to assign elements from global arrays like players, sectors, etc. to local pointers.
|
||||
// For all other types this is not needed. Structs are not assignable and classes can only exist as references.
|
||||
bool writable;
|
||||
Right->RequestAddress(ctx, &writable);
|
||||
Right->ValueType = Base->ValueType;
|
||||
}
|
||||
else
|
||||
{
|
||||
// pass it to FxTypeCast for complete handling.
|
||||
|
|
@ -2606,7 +2746,6 @@ FxExpression *FxAssignSelf::Resolve(FCompileContext &ctx)
|
|||
|
||||
ExpEmit FxAssignSelf::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
assert(ValueType == Assignment->ValueType);
|
||||
ExpEmit pointer = Assignment->Address; // FxAssign should have already emitted it
|
||||
if (!pointer.Target)
|
||||
{
|
||||
|
|
@ -6274,6 +6413,15 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
|
|||
ABORT(newex);
|
||||
goto foundit;
|
||||
}
|
||||
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.
|
||||
newex = new FxConstant(static_cast<PFunction*>(sym), ScriptPosition);
|
||||
goto foundit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now check in the owning class.
|
||||
|
|
@ -6285,6 +6433,15 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
|
|||
newex = FxConstant::MakeConstant(sym, ScriptPosition);
|
||||
goto foundit;
|
||||
}
|
||||
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.
|
||||
newex = new FxConstant(static_cast<PFunction*>(sym), ScriptPosition);
|
||||
goto foundit;
|
||||
}
|
||||
}
|
||||
else if (ctx.Function == nullptr)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Unable to access class member %s from constant declaration", sym->SymbolName.GetChars());
|
||||
|
|
@ -6429,7 +6586,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)))
|
||||
|
|
@ -6645,7 +6801,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)
|
||||
|
|
@ -6659,6 +6815,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);
|
||||
|
|
@ -7181,7 +7347,7 @@ bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable)
|
|||
if (membervar->Flags & VARF_Meta)
|
||||
{
|
||||
// Meta variables are read only.
|
||||
*writable = false;
|
||||
if(writable != nullptr) *writable = false;
|
||||
}
|
||||
else if (writable != nullptr)
|
||||
{
|
||||
|
|
@ -8276,6 +8442,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
PContainerType *cls = nullptr;
|
||||
bool staticonly = false;
|
||||
bool novirtual = false;
|
||||
bool fnptr = false;
|
||||
bool isreadonly = false;
|
||||
|
||||
PContainerType *ccls = nullptr;
|
||||
|
|
@ -8893,6 +9060,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());
|
||||
|
|
@ -9012,8 +9195,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);
|
||||
|
|
@ -9027,7 +9210,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;
|
||||
|
|
@ -9101,7 +9284,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;
|
||||
|
|
@ -9126,7 +9309,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();
|
||||
|
||||
|
|
@ -9194,6 +9377,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.");
|
||||
|
|
@ -9243,7 +9432,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
|
||||
|
|
@ -9252,7 +9441,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);
|
||||
|
|
@ -9389,6 +9578,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
|
|||
{
|
||||
ValueType = TypeVoid;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -9414,11 +9604,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)
|
||||
|
|
@ -9462,43 +9655,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);
|
||||
}
|
||||
|
|
@ -11123,6 +11330,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)
|
||||
|
|
@ -11526,6 +11737,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)
|
||||
|
|
@ -11590,6 +12023,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,
|
||||
|
|
@ -506,6 +507,20 @@ public:
|
|||
isresolved = true;
|
||||
}
|
||||
|
||||
FxConstant(VMFunction* state, const FScriptPosition& pos) : FxExpression(EFX_Constant, pos)
|
||||
{
|
||||
value.pointer = state;
|
||||
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)
|
||||
{
|
||||
value.pointer = nullptr;
|
||||
|
|
@ -550,6 +565,8 @@ public:
|
|||
return value;
|
||||
}
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
|
||||
friend class FxTypeCast;
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
|
@ -728,6 +745,8 @@ public:
|
|||
FxExpression *Resolve(FCompileContext&);
|
||||
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
|
||||
static FxConstant * convertRawFunctionToFunctionPointer(FxExpression * in, FScriptPosition &ScriptPosition);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
|
@ -1811,6 +1830,7 @@ class FxVMFunctionCall : public FxExpression
|
|||
bool CheckAccessibility(const VersionInfo &ver);
|
||||
|
||||
public:
|
||||
const bool FnPtrCall;
|
||||
|
||||
FArgumentList ArgList;
|
||||
PFunction* Function;
|
||||
|
|
@ -2120,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:
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include "m_argv.h"
|
||||
#include "c_cvars.h"
|
||||
#include "jit.h"
|
||||
#include "filesystem.h"
|
||||
|
||||
CVAR(Bool, strictdecorate, false, CVAR_GLOBALCONFIG | CVAR_ARCHIVE)
|
||||
|
||||
|
|
@ -790,7 +791,7 @@ VMFunction *FFunctionBuildList::AddFunction(PNamespace *gnspc, const VersionInfo
|
|||
it.PrintableName = name;
|
||||
it.Function = new VMScriptFunction;
|
||||
it.Function->Name = functype->SymbolName;
|
||||
it.Function->QualifiedName = it.Function->PrintableName = ClassDataAllocator.Strdup(name);
|
||||
it.Function->QualifiedName = it.Function->PrintableName = ClassDataAllocator.Strdup(name.GetChars());
|
||||
it.Function->ImplicitArgs = functype->GetImplicitArgs();
|
||||
it.Proto = nullptr;
|
||||
it.FromDecorate = fromdecorate;
|
||||
|
|
@ -922,13 +923,17 @@ void FFunctionBuildList::Build()
|
|||
VMFunction::CreateRegUseInfo();
|
||||
FScriptPosition::StrictErrors = strictdecorate;
|
||||
|
||||
if (FScriptPosition::ErrorCounter == 0 && Args->CheckParm("-dumpjit")) DumpJit();
|
||||
if (FScriptPosition::ErrorCounter == 0)
|
||||
{
|
||||
if (Args->CheckParm("-dumpjit")) DumpJit(true);
|
||||
else if (Args->CheckParm("-dumpjitmod")) DumpJit(false);
|
||||
}
|
||||
mItems.Clear();
|
||||
mItems.ShrinkToFit();
|
||||
FxAlloc.FreeAllBlocks();
|
||||
}
|
||||
|
||||
void FFunctionBuildList::DumpJit()
|
||||
void FFunctionBuildList::DumpJit(bool include_gzdoom_pk3)
|
||||
{
|
||||
#ifdef HAVE_VM_JIT
|
||||
FILE *dump = fopen("dumpjit.txt", "w");
|
||||
|
|
@ -937,13 +942,25 @@ void FFunctionBuildList::DumpJit()
|
|||
|
||||
for (auto &item : mItems)
|
||||
{
|
||||
JitDumpLog(dump, item.Function);
|
||||
if(include_gzdoom_pk3 || fileSystem.GetFileContainer(item.Lump)) JitDumpLog(dump, item.Function);
|
||||
}
|
||||
|
||||
fclose(dump);
|
||||
#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)
|
||||
{
|
||||
|
|
@ -954,7 +971,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
|
||||
|
|
@ -977,7 +994,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);
|
||||
|
|
@ -994,7 +1011,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
|
||||
{
|
||||
|
|
@ -1006,7 +1023,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
|
||||
{
|
||||
|
|
@ -1018,7 +1035,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
|
||||
{
|
||||
|
|
@ -1030,7 +1047,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
|
||||
{
|
||||
|
|
@ -1050,7 +1067,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
|
||||
{
|
||||
|
|
@ -1069,7 +1086,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.
|
||||
|
|
@ -1079,9 +1096,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());
|
||||
}
|
||||
|
|
@ -1105,9 +1137,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);
|
||||
}
|
||||
|
|
@ -1148,7 +1184,7 @@ void VMDisassemblyDumper::Write(VMScriptFunction *sfunc, const FString &fname)
|
|||
|
||||
assert(sfunc != nullptr);
|
||||
|
||||
DumpFunction(dump, sfunc, fname, (int)fname.Len());
|
||||
DumpFunction(dump, sfunc, fname.GetChars(), (int)fname.Len());
|
||||
codesize += sfunc->CodeSize;
|
||||
datasize += sfunc->LineInfoCount * sizeof(FStatementInfo) + sfunc->ExtraSpace + sfunc->NumKonstD * sizeof(int) +
|
||||
sfunc->NumKonstA * sizeof(void*) + sfunc->NumKonstF * sizeof(double) + sfunc->NumKonstS * sizeof(FString);
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ class FFunctionBuildList
|
|||
|
||||
TArray<Item> mItems;
|
||||
|
||||
void DumpJit();
|
||||
void DumpJit(bool include_gzdoom_pk3);
|
||||
|
||||
public:
|
||||
VMFunction *AddFunction(PNamespace *curglobals, const VersionInfo &ver, PFunction *func, FxExpression *code, const FString &name, bool fromdecorate, int currentstate, int statecnt, int lumpnum);
|
||||
|
|
@ -180,13 +180,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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -147,6 +147,24 @@ VMFunction *FindVMFunction(PClass *cls, const char *name)
|
|||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Find an action function in AActor's table from a qualified name
|
||||
// This cannot search in structs. sorry. :(
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
VMFunction* FindVMFunction( const char* name)
|
||||
{
|
||||
auto p = strchr(name, '.');
|
||||
if (p == nullptr) return nullptr;
|
||||
std::string clsname(name, p - name);
|
||||
auto cls = PClass::FindClass(clsname.c_str());
|
||||
if (cls == nullptr) return nullptr;
|
||||
return FindVMFunction(cls, p + 1);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Sorting helpers
|
||||
|
|
@ -198,8 +216,8 @@ void InitImports()
|
|||
{
|
||||
assert(afunc->VMPointer != NULL);
|
||||
*(afunc->VMPointer) = new VMNativeFunction(afunc->Function, afunc->FuncName);
|
||||
(*(afunc->VMPointer))->QualifiedName = ClassDataAllocator.Strdup(FStringf("%s.%s", afunc->ClassName + 1, afunc->FuncName));
|
||||
(*(afunc->VMPointer))->PrintableName = ClassDataAllocator.Strdup(FStringf("%s.%s [Native]", afunc->ClassName+1, afunc->FuncName));
|
||||
(*(afunc->VMPointer))->QualifiedName = ClassDataAllocator.Strdup(FStringf("%s.%s", afunc->ClassName + 1, afunc->FuncName).GetChars());
|
||||
(*(afunc->VMPointer))->PrintableName = ClassDataAllocator.Strdup(FStringf("%s.%s [Native]", afunc->ClassName+1, afunc->FuncName).GetChars());
|
||||
(*(afunc->VMPointer))->DirectNativeCall = afunc->DirectNative;
|
||||
AFTable.Push(*afunc);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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,8 @@ PStruct *TypeStringStruct;
|
|||
PStruct* TypeQuaternionStruct;
|
||||
PPointer *TypeNullPtr;
|
||||
PPointer *TypeVoidPtr;
|
||||
PPointer *TypeRawFunction;
|
||||
PPointer* TypeVMFunction;
|
||||
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
|
@ -322,6 +324,9 @@ 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);
|
||||
TypeQuaternionStruct = NewStruct("QuatStruct", nullptr, true);
|
||||
|
|
@ -853,6 +858,7 @@ void PFloat::SetDoubleSymbols()
|
|||
{ NAME_Min_Normal, DBL_MIN },
|
||||
{ NAME_Max, DBL_MAX },
|
||||
{ NAME_Epsilon, DBL_EPSILON },
|
||||
{ NAME_Equal_Epsilon, EQUAL_EPSILON },
|
||||
{ NAME_NaN, std::numeric_limits<double>::quiet_NaN() },
|
||||
{ NAME_Infinity, std::numeric_limits<double>::infinity() },
|
||||
{ NAME_Min_Denormal, std::numeric_limits<double>::denorm_min() }
|
||||
|
|
@ -885,6 +891,7 @@ void PFloat::SetSingleSymbols()
|
|||
{ NAME_Min_Normal, FLT_MIN },
|
||||
{ NAME_Max, FLT_MAX },
|
||||
{ NAME_Epsilon, FLT_EPSILON },
|
||||
{ NAME_Equal_Epsilon, (float)EQUAL_EPSILON },
|
||||
{ NAME_NaN, std::numeric_limits<float>::quiet_NaN() },
|
||||
{ NAME_Infinity, std::numeric_limits<float>::infinity() },
|
||||
{ NAME_Min_Denormal, std::numeric_limits<float>::denorm_min() }
|
||||
|
|
@ -2375,60 +2382,50 @@ void PMap::GetTypeIDs(intptr_t &id1, intptr_t &id2) const
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
#define FOR_EACH_MAP_TYPE(FN) \
|
||||
case PMap::MAP_I32_I8: \
|
||||
FN( uint32_t , uint8_t ) \
|
||||
case PMap::MAP_I32_I16: \
|
||||
FN( uint32_t , uint16_t ) \
|
||||
case PMap::MAP_I32_I32: \
|
||||
FN( uint32_t , uint32_t ) \
|
||||
case PMap::MAP_I32_F32: \
|
||||
FN( uint32_t , float ) \
|
||||
case PMap::MAP_I32_F64: \
|
||||
FN( uint32_t , double ) \
|
||||
case PMap::MAP_I32_OBJ: \
|
||||
FN( uint32_t , DObject* ) \
|
||||
case PMap::MAP_I32_PTR: \
|
||||
FN( uint32_t , void* ) \
|
||||
case PMap::MAP_I32_STR: \
|
||||
FN( uint32_t , FString ) \
|
||||
case PMap::MAP_STR_I8: \
|
||||
FN( FString , uint8_t ) \
|
||||
case PMap::MAP_STR_I16: \
|
||||
FN( FString , uint16_t ) \
|
||||
case PMap::MAP_STR_I32: \
|
||||
FN( FString , uint32_t ) \
|
||||
case PMap::MAP_STR_F32: \
|
||||
FN( FString , float ) \
|
||||
case PMap::MAP_STR_F64: \
|
||||
FN( FString , double ) \
|
||||
case PMap::MAP_STR_OBJ: \
|
||||
FN( FString , DObject* ) \
|
||||
case PMap::MAP_STR_PTR: \
|
||||
FN( FString , void* ) \
|
||||
case PMap::MAP_STR_STR: \
|
||||
FN( FString , FString )
|
||||
|
||||
void PMap::Construct(void * addr) const {
|
||||
switch(BackingClass)
|
||||
{
|
||||
case MAP_I32_I8:
|
||||
new(addr) ZSMap<uint32_t, uint8_t>();
|
||||
break;
|
||||
case MAP_I32_I16:
|
||||
new(addr) ZSMap<uint32_t, uint16_t>();
|
||||
break;
|
||||
case MAP_I32_I32:
|
||||
new(addr) ZSMap<uint32_t, uint32_t>();
|
||||
break;
|
||||
case MAP_I32_F32:
|
||||
new(addr) ZSMap<uint32_t, float>();
|
||||
break;
|
||||
case MAP_I32_F64:
|
||||
new(addr) ZSMap<uint32_t, double>();
|
||||
break;
|
||||
case MAP_I32_OBJ:
|
||||
new(addr) ZSMap<uint32_t, DObject*>();
|
||||
break;
|
||||
case MAP_I32_PTR:
|
||||
new(addr) ZSMap<uint32_t, void*>();
|
||||
break;
|
||||
case MAP_I32_STR:
|
||||
new(addr) ZSMap<uint32_t, FString>();
|
||||
break;
|
||||
case MAP_STR_I8:
|
||||
new(addr) ZSMap<FString, uint8_t>();
|
||||
break;
|
||||
case MAP_STR_I16:
|
||||
new(addr) ZSMap<FString, uint16_t>();
|
||||
break;
|
||||
case MAP_STR_I32:
|
||||
new(addr) ZSMap<FString, uint32_t>();
|
||||
break;
|
||||
case MAP_STR_F32:
|
||||
new(addr) ZSMap<FString, float>();
|
||||
break;
|
||||
case MAP_STR_F64:
|
||||
new(addr) ZSMap<FString, double>();
|
||||
break;
|
||||
case MAP_STR_OBJ:
|
||||
new(addr) ZSMap<FString, DObject*>();
|
||||
break;
|
||||
case MAP_STR_PTR:
|
||||
new(addr) ZSMap<FString, void*>();
|
||||
break;
|
||||
case MAP_STR_STR:
|
||||
new(addr) ZSMap<FString, FString>();
|
||||
break;
|
||||
#define MAP_CONSTRUCT(KT, VT) new(addr) ZSMap< KT, VT >(); break;
|
||||
FOR_EACH_MAP_TYPE(MAP_CONSTRUCT)
|
||||
#undef MAP_CONSTRUCT
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
void PMap::InitializeValue(void *addr, const void *def) const
|
||||
{
|
||||
Construct(addr);
|
||||
|
|
@ -2444,54 +2441,9 @@ void PMap::DestroyValue(void *addr) const
|
|||
{
|
||||
switch(BackingClass)
|
||||
{
|
||||
case MAP_I32_I8:
|
||||
static_cast<ZSMap<uint32_t, uint8_t>*>(addr)->~ZSMap();
|
||||
break;
|
||||
case MAP_I32_I16:
|
||||
static_cast<ZSMap<uint32_t, uint16_t>*>(addr)->~ZSMap();
|
||||
break;
|
||||
case MAP_I32_I32:
|
||||
static_cast<ZSMap<uint32_t, uint32_t>*>(addr)->~ZSMap();
|
||||
break;
|
||||
case MAP_I32_F32:
|
||||
static_cast<ZSMap<uint32_t, float>*>(addr)->~ZSMap();
|
||||
break;
|
||||
case MAP_I32_F64:
|
||||
static_cast<ZSMap<uint32_t, double>*>(addr)->~ZSMap();
|
||||
break;
|
||||
case MAP_I32_OBJ:
|
||||
static_cast<ZSMap<uint32_t, DObject*>*>(addr)->~ZSMap();
|
||||
break;
|
||||
case MAP_I32_PTR:
|
||||
static_cast<ZSMap<uint32_t, void*>*>(addr)->~ZSMap();
|
||||
break;
|
||||
case MAP_I32_STR:
|
||||
static_cast<ZSMap<uint32_t, FString>*>(addr)->~ZSMap();
|
||||
break;
|
||||
case MAP_STR_I8:
|
||||
static_cast<ZSMap<FString, uint8_t>*>(addr)->~ZSMap();
|
||||
break;
|
||||
case MAP_STR_I16:
|
||||
static_cast<ZSMap<FString, uint16_t>*>(addr)->~ZSMap();
|
||||
break;
|
||||
case MAP_STR_I32:
|
||||
static_cast<ZSMap<FString, uint32_t>*>(addr)->~ZSMap();
|
||||
break;
|
||||
case MAP_STR_F32:
|
||||
static_cast<ZSMap<FString, float>*>(addr)->~ZSMap();
|
||||
break;
|
||||
case MAP_STR_F64:
|
||||
static_cast<ZSMap<FString, double>*>(addr)->~ZSMap();
|
||||
break;
|
||||
case MAP_STR_OBJ:
|
||||
static_cast<ZSMap<FString, DObject*>*>(addr)->~ZSMap();
|
||||
break;
|
||||
case MAP_STR_PTR:
|
||||
static_cast<ZSMap<FString, void*>*>(addr)->~ZSMap();
|
||||
break;
|
||||
case MAP_STR_STR:
|
||||
static_cast<ZSMap<FString, FString>*>(addr)->~ZSMap();
|
||||
break;
|
||||
#define MAP_DESTRUCT(KT, VT) static_cast<ZSMap< KT, VT >*>(addr)->~ZSMap(); break;
|
||||
FOR_EACH_MAP_TYPE(MAP_DESTRUCT)
|
||||
#undef MAP_DESTRUCT
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2566,54 +2518,9 @@ void PMap::WriteValue(FSerializer &ar, const char *key, const void *addr) const
|
|||
{
|
||||
switch(BackingClass)
|
||||
{
|
||||
case MAP_I32_I8:
|
||||
PMapValueWriter(ar, static_cast<const ZSMap<uint32_t, uint8_t>*>(addr), this);
|
||||
break;
|
||||
case MAP_I32_I16:
|
||||
PMapValueWriter(ar, static_cast<const ZSMap<uint32_t, uint16_t>*>(addr), this);
|
||||
break;
|
||||
case MAP_I32_I32:
|
||||
PMapValueWriter(ar, static_cast<const ZSMap<uint32_t, uint32_t>*>(addr), this);
|
||||
break;
|
||||
case MAP_I32_F32:
|
||||
PMapValueWriter(ar, static_cast<const ZSMap<uint32_t, float>*>(addr), this);
|
||||
break;
|
||||
case MAP_I32_F64:
|
||||
PMapValueWriter(ar, static_cast<const ZSMap<uint32_t, double>*>(addr), this);
|
||||
break;
|
||||
case MAP_I32_OBJ:
|
||||
PMapValueWriter(ar, static_cast<const ZSMap<uint32_t, DObject*>*>(addr), this);
|
||||
break;
|
||||
case MAP_I32_PTR:
|
||||
PMapValueWriter(ar, static_cast<const ZSMap<uint32_t, void*>*>(addr), this);
|
||||
break;
|
||||
case MAP_I32_STR:
|
||||
PMapValueWriter(ar, static_cast<const ZSMap<uint32_t, FString>*>(addr), this);
|
||||
break;
|
||||
case MAP_STR_I8:
|
||||
PMapValueWriter(ar, static_cast<const ZSMap<FString, uint8_t>*>(addr), this);
|
||||
break;
|
||||
case MAP_STR_I16:
|
||||
PMapValueWriter(ar, static_cast<const ZSMap<FString, uint16_t>*>(addr), this);
|
||||
break;
|
||||
case MAP_STR_I32:
|
||||
PMapValueWriter(ar, static_cast<const ZSMap<FString, uint32_t>*>(addr), this);
|
||||
break;
|
||||
case MAP_STR_F32:
|
||||
PMapValueWriter(ar, static_cast<const ZSMap<FString, float>*>(addr), this);
|
||||
break;
|
||||
case MAP_STR_F64:
|
||||
PMapValueWriter(ar, static_cast<const ZSMap<FString, double>*>(addr), this);
|
||||
break;
|
||||
case MAP_STR_OBJ:
|
||||
PMapValueWriter(ar, static_cast<const ZSMap<FString, DObject*>*>(addr), this);
|
||||
break;
|
||||
case MAP_STR_PTR:
|
||||
PMapValueWriter(ar, static_cast<const ZSMap<FString, void*>*>(addr), this);
|
||||
break;
|
||||
case MAP_STR_STR:
|
||||
PMapValueWriter(ar, static_cast<const ZSMap<FString, FString>*>(addr), this);
|
||||
break;
|
||||
#define MAP_WRITE(KT, VT) PMapValueWriter(ar, static_cast<const ZSMap< KT, VT >*>(addr), this); break;
|
||||
FOR_EACH_MAP_TYPE(MAP_WRITE)
|
||||
#undef MAP_WRITE
|
||||
}
|
||||
ar.EndObject();
|
||||
}
|
||||
|
|
@ -2666,38 +2573,9 @@ bool PMap::ReadValue(FSerializer &ar, const char *key, void *addr) const
|
|||
{
|
||||
switch(BackingClass)
|
||||
{
|
||||
case MAP_I32_I8:
|
||||
return PMapValueReader(ar, static_cast<ZSMap<uint32_t, uint8_t>*>(addr), this);
|
||||
case MAP_I32_I16:
|
||||
return PMapValueReader(ar, static_cast<ZSMap<uint32_t, uint16_t>*>(addr), this);
|
||||
case MAP_I32_I32:
|
||||
return PMapValueReader(ar, static_cast<ZSMap<uint32_t, uint32_t>*>(addr), this);
|
||||
case MAP_I32_F32:
|
||||
return PMapValueReader(ar, static_cast<ZSMap<uint32_t, float>*>(addr), this);
|
||||
case MAP_I32_F64:
|
||||
return PMapValueReader(ar, static_cast<ZSMap<uint32_t, double>*>(addr), this);
|
||||
case MAP_I32_OBJ:
|
||||
return PMapValueReader(ar, static_cast<ZSMap<uint32_t, DObject*>*>(addr), this);
|
||||
case MAP_I32_PTR:
|
||||
return PMapValueReader(ar, static_cast<ZSMap<uint32_t, void*>*>(addr), this);
|
||||
case MAP_I32_STR:
|
||||
return PMapValueReader(ar, static_cast<ZSMap<uint32_t, FString>*>(addr), this);
|
||||
case MAP_STR_I8:
|
||||
return PMapValueReader(ar, static_cast<ZSMap<FString, uint8_t>*>(addr), this);
|
||||
case MAP_STR_I16:
|
||||
return PMapValueReader(ar, static_cast<ZSMap<FString, uint16_t>*>(addr), this);
|
||||
case MAP_STR_I32:
|
||||
return PMapValueReader(ar, static_cast<ZSMap<FString, uint32_t>*>(addr), this);
|
||||
case MAP_STR_F32:
|
||||
return PMapValueReader(ar, static_cast<ZSMap<FString, float>*>(addr), this);
|
||||
case MAP_STR_F64:
|
||||
return PMapValueReader(ar, static_cast<ZSMap<FString, double>*>(addr), this);
|
||||
case MAP_STR_OBJ:
|
||||
return PMapValueReader(ar, static_cast<ZSMap<FString, DObject*>*>(addr), this);
|
||||
case MAP_STR_PTR:
|
||||
return PMapValueReader(ar, static_cast<ZSMap<FString, void*>*>(addr), this);
|
||||
case MAP_STR_STR:
|
||||
return PMapValueReader(ar, static_cast<ZSMap<FString, FString>*>(addr), this);
|
||||
#define MAP_READ(KT, VT) return PMapValueReader(ar, static_cast<ZSMap< KT, VT >*>(addr), this);
|
||||
FOR_EACH_MAP_TYPE(MAP_READ)
|
||||
#undef MAP_READ
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
@ -2788,11 +2666,11 @@ PMap *NewMap(PType *keyType, PType *valueType)
|
|||
return (PMap *)mapType;
|
||||
}
|
||||
|
||||
/* PMap *******************************************************************/
|
||||
/* PMapIterator ***********************************************************/
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// PMap - Parameterized Constructor
|
||||
// PMapIterator - Parameterized Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
|
|
@ -2842,54 +2720,9 @@ void PMapIterator::GetTypeIDs(intptr_t &id1, intptr_t &id2) const
|
|||
void PMapIterator::Construct(void * addr) const {
|
||||
switch(BackingClass)
|
||||
{
|
||||
case PMap::MAP_I32_I8:
|
||||
new(addr) ZSMapIterator<uint32_t, uint8_t>();
|
||||
break;
|
||||
case PMap::MAP_I32_I16:
|
||||
new(addr) ZSMapIterator<uint32_t, uint16_t>();
|
||||
break;
|
||||
case PMap::MAP_I32_I32:
|
||||
new(addr) ZSMapIterator<uint32_t, uint32_t>();
|
||||
break;
|
||||
case PMap::MAP_I32_F32:
|
||||
new(addr) ZSMapIterator<uint32_t, float>();
|
||||
break;
|
||||
case PMap::MAP_I32_F64:
|
||||
new(addr) ZSMapIterator<uint32_t, double>();
|
||||
break;
|
||||
case PMap::MAP_I32_OBJ:
|
||||
new(addr) ZSMapIterator<uint32_t, DObject*>();
|
||||
break;
|
||||
case PMap::MAP_I32_PTR:
|
||||
new(addr) ZSMapIterator<uint32_t, void*>();
|
||||
break;
|
||||
case PMap::MAP_I32_STR:
|
||||
new(addr) ZSMapIterator<uint32_t, FString>();
|
||||
break;
|
||||
case PMap::MAP_STR_I8:
|
||||
new(addr) ZSMapIterator<FString, uint8_t>();
|
||||
break;
|
||||
case PMap::MAP_STR_I16:
|
||||
new(addr) ZSMapIterator<FString, uint16_t>();
|
||||
break;
|
||||
case PMap::MAP_STR_I32:
|
||||
new(addr) ZSMapIterator<FString, uint32_t>();
|
||||
break;
|
||||
case PMap::MAP_STR_F32:
|
||||
new(addr) ZSMapIterator<FString, float>();
|
||||
break;
|
||||
case PMap::MAP_STR_F64:
|
||||
new(addr) ZSMapIterator<FString, double>();
|
||||
break;
|
||||
case PMap::MAP_STR_OBJ:
|
||||
new(addr) ZSMapIterator<FString, DObject*>();
|
||||
break;
|
||||
case PMap::MAP_STR_PTR:
|
||||
new(addr) ZSMapIterator<FString, void*>();
|
||||
break;
|
||||
case PMap::MAP_STR_STR:
|
||||
new(addr) ZSMapIterator<FString, FString>();
|
||||
break;
|
||||
#define MAP_IT_CONSTRUCT(KT, VT) new(addr) ZSMapIterator< KT, VT >(); break;
|
||||
FOR_EACH_MAP_TYPE(MAP_IT_CONSTRUCT)
|
||||
#undef MAP_IT_CONSTRUCT
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -2908,54 +2741,9 @@ void PMapIterator::DestroyValue(void *addr) const
|
|||
{
|
||||
switch(BackingClass)
|
||||
{
|
||||
case PMap::MAP_I32_I8:
|
||||
static_cast<ZSMapIterator<uint32_t, uint8_t>*>(addr)->~ZSMapIterator();
|
||||
break;
|
||||
case PMap::MAP_I32_I16:
|
||||
static_cast<ZSMapIterator<uint32_t, uint16_t>*>(addr)->~ZSMapIterator();
|
||||
break;
|
||||
case PMap::MAP_I32_I32:
|
||||
static_cast<ZSMapIterator<uint32_t, uint32_t>*>(addr)->~ZSMapIterator();
|
||||
break;
|
||||
case PMap::MAP_I32_F32:
|
||||
static_cast<ZSMapIterator<uint32_t, float>*>(addr)->~ZSMapIterator();
|
||||
break;
|
||||
case PMap::MAP_I32_F64:
|
||||
static_cast<ZSMapIterator<uint32_t, double>*>(addr)->~ZSMapIterator();
|
||||
break;
|
||||
case PMap::MAP_I32_OBJ:
|
||||
static_cast<ZSMapIterator<uint32_t, DObject*>*>(addr)->~ZSMapIterator();
|
||||
break;
|
||||
case PMap::MAP_I32_PTR:
|
||||
static_cast<ZSMapIterator<uint32_t, void*>*>(addr)->~ZSMapIterator();
|
||||
break;
|
||||
case PMap::MAP_I32_STR:
|
||||
static_cast<ZSMapIterator<uint32_t, FString>*>(addr)->~ZSMapIterator();
|
||||
break;
|
||||
case PMap::MAP_STR_I8:
|
||||
static_cast<ZSMapIterator<FString, uint8_t>*>(addr)->~ZSMapIterator();
|
||||
break;
|
||||
case PMap::MAP_STR_I16:
|
||||
static_cast<ZSMapIterator<FString, uint16_t>*>(addr)->~ZSMapIterator();
|
||||
break;
|
||||
case PMap::MAP_STR_I32:
|
||||
static_cast<ZSMapIterator<FString, uint32_t>*>(addr)->~ZSMapIterator();
|
||||
break;
|
||||
case PMap::MAP_STR_F32:
|
||||
static_cast<ZSMapIterator<FString, float>*>(addr)->~ZSMapIterator();
|
||||
break;
|
||||
case PMap::MAP_STR_F64:
|
||||
static_cast<ZSMapIterator<FString, double>*>(addr)->~ZSMapIterator();
|
||||
break;
|
||||
case PMap::MAP_STR_OBJ:
|
||||
static_cast<ZSMapIterator<FString, DObject*>*>(addr)->~ZSMapIterator();
|
||||
break;
|
||||
case PMap::MAP_STR_PTR:
|
||||
static_cast<ZSMapIterator<FString, void*>*>(addr)->~ZSMapIterator();
|
||||
break;
|
||||
case PMap::MAP_STR_STR:
|
||||
static_cast<ZSMapIterator<FString, FString>*>(addr)->~ZSMapIterator();
|
||||
break;
|
||||
#define MAP_IT_DESTROY(KT, VT) static_cast<ZSMapIterator< KT, VT >*>(addr)->~ZSMapIterator(); break;
|
||||
FOR_EACH_MAP_TYPE(MAP_IT_DESTROY)
|
||||
#undef MAP_IT_DESTROY
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3033,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 ****************************************************************/
|
||||
|
||||
//==========================================================================
|
||||
|
|
@ -3174,7 +3195,7 @@ bool PStruct::ReadValue(FSerializer &ar, const char *key, void *addr) const
|
|||
PField *PStruct::AddField(FName name, PType *type, uint32_t flags)
|
||||
{
|
||||
assert(type->Size > 0);
|
||||
return Symbols.AddField(name, type, flags, Size, &Align);
|
||||
return Symbols.AddField(name, type, flags, Size, &Align, mDefFileNo);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
@ -3442,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;
|
||||
|
|
@ -3462,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,9 @@ extern PPointer *TypeFont;
|
|||
extern PStateLabel *TypeStateLabel;
|
||||
extern PPointer *TypeNullPtr;
|
||||
extern PPointer *TypeVoidPtr;
|
||||
extern PPointer* TypeRawFunction;
|
||||
extern PPointer* TypeVMFunction;
|
||||
|
||||
|
||||
inline FString &DObject::StringVar(FName field)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ static void SetNodeLine(ZCC_TreeNode *name, int line)
|
|||
struct ClassFlagsBlock {
|
||||
VM_UWORD Flags;
|
||||
ZCC_Identifier *Replaces;
|
||||
ZCC_Identifier *Sealed;
|
||||
VersionInfo Version;
|
||||
};
|
||||
|
||||
|
|
@ -242,6 +243,7 @@ class_head(X) ::= CLASS(T) IDENTIFIER(A) class_ancestry(B) class_flags(C).
|
|||
head->ParentName = B;
|
||||
head->Flags = C.Flags;
|
||||
head->Replaces = C.Replaces;
|
||||
head->Sealed = C.Sealed;
|
||||
head->Version = C.Version;
|
||||
head->Type = nullptr;
|
||||
head->Symbol = nullptr;
|
||||
|
|
@ -253,13 +255,15 @@ class_ancestry(X) ::= . { X = NULL; }
|
|||
class_ancestry(X) ::= COLON dottable_id(A). { X = A; /*X-overwrites-A*/ }
|
||||
|
||||
%type class_flags{ClassFlagsBlock}
|
||||
class_flags(X) ::= . { X.Flags = 0; X.Replaces = NULL; X.Version = {0,0}; }
|
||||
class_flags(X) ::= class_flags(A) ABSTRACT. { X.Flags = A.Flags | ZCC_Abstract; X.Replaces = A.Replaces; }
|
||||
class_flags(X) ::= class_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; X.Replaces = A.Replaces; }
|
||||
class_flags(X) ::= class_flags(A) UI. { X.Flags = A.Flags | ZCC_UIFlag; X.Replaces = A.Replaces; }
|
||||
class_flags(X) ::= class_flags(A) PLAY. { X.Flags = A.Flags | ZCC_Play; X.Replaces = A.Replaces; }
|
||||
class_flags(X) ::= class_flags(A) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; }
|
||||
class_flags(X) ::= class_flags(A) VERSION LPAREN STRCONST(C) RPAREN. { X.Flags = A.Flags | ZCC_Version; X.Replaces = A.Replaces; X.Version = C.String->GetChars(); }
|
||||
class_flags(X) ::= . { X.Flags = 0; X.Replaces = NULL; X.Version = {0,0}; X.Sealed = NULL; }
|
||||
class_flags(X) ::= class_flags(A) ABSTRACT. { X.Flags = A.Flags | ZCC_Abstract; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed; }
|
||||
class_flags(X) ::= class_flags(A) FINAL. { X.Flags = A.Flags | ZCC_Final; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed;}
|
||||
class_flags(X) ::= class_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed; }
|
||||
class_flags(X) ::= class_flags(A) UI. { X.Flags = A.Flags | ZCC_UIFlag; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed; }
|
||||
class_flags(X) ::= class_flags(A) PLAY. { X.Flags = A.Flags | ZCC_Play; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed; }
|
||||
class_flags(X) ::= class_flags(A) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; X.Version = A.Version; X.Sealed = A.Sealed; }
|
||||
class_flags(X) ::= class_flags(A) VERSION LPAREN STRCONST(C) RPAREN. { X.Flags = A.Flags | ZCC_Version; X.Replaces = A.Replaces; X.Version = C.String->GetChars(); X.Sealed = A.Sealed; }
|
||||
class_flags(X) ::= class_flags(A) SEALED LPAREN states_opt(B) RPAREN. { X.Flags = A.Flags | ZCC_Sealed; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = B; }
|
||||
|
||||
/*----- Dottable Identifier -----*/
|
||||
// This can be either a single identifier or two identifiers connected by a .
|
||||
|
|
@ -374,6 +378,15 @@ flag_def(X) ::= FLAGDEF(T) IDENTIFIER(A) COLON IDENTIFIER(B) COMMA INTCONST(C) S
|
|||
X = def;
|
||||
}
|
||||
|
||||
flag_def(X) ::= FLAGDEF(T) INTERNAL IDENTIFIER(A) COLON IDENTIFIER(B) COMMA INTCONST(C) SEMICOLON.
|
||||
{
|
||||
NEW_AST_NODE(FlagDef,def,T);
|
||||
def->NodeName = A.Name();
|
||||
def->RefName = B.Name();
|
||||
def->BitValue = C.Int | 0x10000;
|
||||
X = def;
|
||||
}
|
||||
|
||||
|
||||
identifier_list(X) ::= IDENTIFIER(A).
|
||||
{
|
||||
|
|
@ -433,6 +446,7 @@ struct_member(X) ::= declarator(A). { X = A; /*X-overwrites-A*/ }
|
|||
struct_member(X) ::= enum_def(A). { X = A; /*X-overwrites-A*/ }
|
||||
struct_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ }
|
||||
struct_member(X) ::= staticarray_statement(A). { X = A; /*X-overwrites-A*/ }
|
||||
struct_member(X) ::= flag_def(A). { X = A; /*X-overwrites-A*/ }
|
||||
|
||||
/*----- Constant Definition ------*/
|
||||
/* Like UnrealScript, a constant's type is implied by its value's type. */
|
||||
|
|
@ -974,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);
|
||||
|
|
@ -1285,6 +1364,26 @@ func_param(X) ::= func_param_flags(A) type(B) IDENTIFIER(C) EQ expr(D).
|
|||
X = parm;
|
||||
}
|
||||
|
||||
func_param(X) ::= func_param_flags(A) type(B) AND IDENTIFIER(C).
|
||||
{
|
||||
NEW_AST_NODE(FuncParamDecl,parm,A.SourceLoc ? A.SourceLoc : B->SourceLoc);
|
||||
parm->Type = B;
|
||||
parm->Name = C.Name();
|
||||
parm->Flags = A.Int | ZCC_Out;
|
||||
parm->Default = nullptr;
|
||||
X = parm;
|
||||
}
|
||||
|
||||
func_param(X) ::= func_param_flags(A) type(B) AND IDENTIFIER(C) EQ expr(D).
|
||||
{
|
||||
NEW_AST_NODE(FuncParamDecl,parm,A.SourceLoc ? A.SourceLoc : B->SourceLoc);
|
||||
parm->Type = B;
|
||||
parm->Name = C.Name();
|
||||
parm->Flags = A.Int | ZCC_Out;
|
||||
parm->Default = D;
|
||||
X = parm;
|
||||
}
|
||||
|
||||
func_param_flags(X) ::= . { X.Int = 0; X.SourceLoc = 0; }
|
||||
func_param_flags(X) ::= func_param_flags(A) IN(T). { X.Int = A.Int | ZCC_In; X.SourceLoc = T.SourceLoc; }
|
||||
func_param_flags(X) ::= func_param_flags(A) OUT(T). { X.Int = A.Int | ZCC_Out; X.SourceLoc = T.SourceLoc; }
|
||||
|
|
@ -1374,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);
|
||||
|
|
@ -1383,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);
|
||||
|
|
|
|||
|
|
@ -57,6 +57,13 @@ double GetFloatConst(FxExpression *ex, FCompileContext &ctx)
|
|||
return ex ? static_cast<FxConstant*>(ex)->GetValue().GetFloat() : 0;
|
||||
}
|
||||
|
||||
VMFunction* GetFuncConst(FxExpression* ex, FCompileContext& ctx)
|
||||
{
|
||||
ex = new FxTypeCast(ex, TypeVMFunction, false);
|
||||
ex = ex->Resolve(ctx);
|
||||
return static_cast<VMFunction*>(ex ? static_cast<FxConstant*>(ex)->GetValue().GetPointer() : nullptr);
|
||||
}
|
||||
|
||||
const char * ZCCCompiler::GetStringConst(FxExpression *ex, FCompileContext &ctx)
|
||||
{
|
||||
ex = new FxStringCast(ex);
|
||||
|
|
@ -452,6 +459,11 @@ void ZCCCompiler::ProcessStruct(ZCC_Struct *cnode, PSymbolTreeNode *treenode, ZC
|
|||
}
|
||||
break;
|
||||
|
||||
case AST_FlagDef:
|
||||
cls->FlagDefs.Push(static_cast<ZCC_FlagDef*>(node));
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
assert(0 && "Unhandled AST node type");
|
||||
break;
|
||||
|
|
@ -662,7 +674,7 @@ void ZCCCompiler::MessageV(ZCC_TreeNode *node, const char *txtcolor, const char
|
|||
composed.Format("%s%s, line %d: ", txtcolor, node->SourceName->GetChars(), node->SourceLoc);
|
||||
composed.VAppendFormat(msg, argptr);
|
||||
composed += '\n';
|
||||
PrintString(PRINT_HIGH, composed);
|
||||
PrintString(PRINT_HIGH, composed.GetChars());
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
@ -792,8 +804,14 @@ void ZCCCompiler::CreateClassTypes()
|
|||
PClass *parent;
|
||||
auto ParentName = c->cls->ParentName;
|
||||
|
||||
if (ParentName != nullptr && ParentName->SiblingNext == ParentName) parent = PClass::FindClass(ParentName->Id);
|
||||
else if (ParentName == nullptr) parent = RUNTIME_CLASS(DObject);
|
||||
if (ParentName != nullptr && ParentName->SiblingNext == ParentName)
|
||||
{
|
||||
parent = PClass::FindClass(ParentName->Id);
|
||||
}
|
||||
else if (ParentName == nullptr)
|
||||
{
|
||||
parent = RUNTIME_CLASS(DObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The parent is a dotted name which the type system currently does not handle.
|
||||
|
|
@ -813,6 +831,15 @@ void ZCCCompiler::CreateClassTypes()
|
|||
|
||||
if (parent != nullptr && (parent->VMType != nullptr || c->NodeName() == NAME_Object))
|
||||
{
|
||||
if(parent->bFinal)
|
||||
{
|
||||
Error(c->cls, "Class '%s' cannot extend final class '%s'", FName(c->NodeName()).GetChars(), parent->TypeName.GetChars());
|
||||
}
|
||||
else if(parent->bSealed && !parent->SealedRestriction.Contains(c->NodeName()))
|
||||
{
|
||||
Error(c->cls, "Class '%s' cannot extend sealed class '%s'", FName(c->NodeName()).GetChars(), parent->TypeName.GetChars());
|
||||
}
|
||||
|
||||
// The parent exists, we may create a type for this class
|
||||
if (c->cls->Flags & ZCC_Native)
|
||||
{
|
||||
|
|
@ -874,6 +901,25 @@ void ZCCCompiler::CreateClassTypes()
|
|||
{
|
||||
c->Type()->mVersion = c->cls->Version;
|
||||
}
|
||||
|
||||
|
||||
if (c->cls->Flags & ZCC_Final)
|
||||
{
|
||||
c->ClassType()->bFinal = true;
|
||||
}
|
||||
|
||||
if (c->cls->Flags & ZCC_Sealed)
|
||||
{
|
||||
PClass * ccls = c->ClassType();
|
||||
ccls->bSealed = true;
|
||||
ZCC_Identifier * it = c->cls->Sealed;
|
||||
if(it) do
|
||||
{
|
||||
ccls->SealedRestriction.Push(FName(it->Id));
|
||||
it = (ZCC_Identifier*) it->SiblingNext;
|
||||
}
|
||||
while(it != c->cls->Sealed);
|
||||
}
|
||||
//
|
||||
if (mVersion >= MakeVersion(2, 4, 0))
|
||||
{
|
||||
|
|
@ -1866,19 +1912,17 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
|
|||
auto keytype = DetermineType(outertype, field, name, mtype->KeyType, false, false);
|
||||
auto valuetype = DetermineType(outertype, field, name, mtype->ValueType, false, false);
|
||||
|
||||
if (keytype->GetRegType() == REGT_INT)
|
||||
if (keytype->GetRegType() != REGT_STRING && !(keytype->GetRegType() == REGT_INT && keytype->Size == 4))
|
||||
{
|
||||
if (keytype->Size != 4)
|
||||
if(name != NAME_None)
|
||||
{
|
||||
Error(field, "%s : Map<%s , ...> not implemented yet", name.GetChars(), keytype->DescriptiveName());
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(field, "Map<%s , ...> not implemented yet", keytype->DescriptiveName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (keytype->GetRegType() != REGT_STRING)
|
||||
{
|
||||
Error(field, "Map<%s , ...> not implemented yet", keytype->DescriptiveName());
|
||||
break;
|
||||
}
|
||||
|
||||
switch(valuetype->GetRegType())
|
||||
{
|
||||
|
|
@ -1888,14 +1932,20 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
|
|||
case REGT_POINTER:
|
||||
if (valuetype->GetRegCount() > 1)
|
||||
{
|
||||
Error(field, "%s : Base type for map value types must be integral, but got %s", name.GetChars(), valuetype->DescriptiveName());
|
||||
default:
|
||||
if(name != NAME_None)
|
||||
{
|
||||
Error(field, "%s : Base type for map value types must be integral, but got %s", name.GetChars(), valuetype->DescriptiveName());
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(field, "Base type for map value types must be integral, but got %s", valuetype->DescriptiveName());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
retval = NewMap(keytype, valuetype);
|
||||
break;
|
||||
default:
|
||||
Error(field, "%s: Base type for map value types must be integral, but got %s", name.GetChars(), valuetype->DescriptiveName());
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
@ -1913,17 +1963,17 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
|
|||
auto keytype = DetermineType(outertype, field, name, mtype->KeyType, false, false);
|
||||
auto valuetype = DetermineType(outertype, field, name, mtype->ValueType, false, false);
|
||||
|
||||
if (keytype->GetRegType() == REGT_INT)
|
||||
if (keytype->GetRegType() != REGT_STRING && !(keytype->GetRegType() == REGT_INT && keytype->Size == 4))
|
||||
{
|
||||
if (keytype->Size != 4)
|
||||
if(name != NAME_None)
|
||||
{
|
||||
Error(field, "%s : MapIterator<%s , ...> not implemented yet", name.GetChars(), keytype->DescriptiveName());
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(field, "MapIterator<%s , ...> not implemented yet", keytype->DescriptiveName());
|
||||
}
|
||||
}
|
||||
else if (keytype->GetRegType() != REGT_STRING)
|
||||
{
|
||||
Error(field, "MapIterator<%s , ...> not implemented yet", keytype->DescriptiveName());
|
||||
}
|
||||
|
||||
switch(valuetype->GetRegType())
|
||||
{
|
||||
|
|
@ -1933,13 +1983,19 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
|
|||
case REGT_POINTER:
|
||||
if (valuetype->GetRegCount() > 1)
|
||||
{
|
||||
Error(field, "%s : Base type for map value types must be integral, but got %s", name.GetChars(), valuetype->DescriptiveName());
|
||||
default:
|
||||
if(name != NAME_None)
|
||||
{
|
||||
Error(field, "%s : Base type for map value types must be integral, but got %s", name.GetChars(), valuetype->DescriptiveName());
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(field, "Base type for map value types must be integral, but got %s", valuetype->DescriptiveName());
|
||||
}
|
||||
break;
|
||||
}
|
||||
retval = NewMapIterator(keytype, valuetype);
|
||||
break;
|
||||
default:
|
||||
Error(field, "%s: Base type for map value types must be integral, but got %s", name.GetChars(), valuetype->DescriptiveName());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -1968,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);
|
||||
|
|
@ -2244,6 +2346,11 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
Error(f, "The return type of a function cannot be a dynamic array");
|
||||
break;
|
||||
}
|
||||
else if (type->isMap())
|
||||
{
|
||||
Error(f, "The return type of a function cannot be a map");
|
||||
break;
|
||||
}
|
||||
else if (type == TypeFVector2)
|
||||
{
|
||||
type = TypeVector2;
|
||||
|
|
@ -2931,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);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
struct Baggage;
|
||||
struct FPropertyInfo;
|
||||
class AActor;
|
||||
class FxExpression;
|
||||
typedef TDeletingArray<FxExpression*> FArgumentList;
|
||||
|
||||
|
|
@ -23,6 +22,7 @@ struct ZCC_StructWork
|
|||
TArray<ZCC_VarDeclarator *> Fields;
|
||||
TArray<ZCC_FuncDeclarator *> Functions;
|
||||
TArray<ZCC_StaticArrayStatement *> Arrays;
|
||||
TArray<ZCC_FlagDef*> FlagDefs;
|
||||
|
||||
ZCC_StructWork()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ static FString ResolveIncludePath(const FString &path,const FString &lumpname){
|
|||
|
||||
auto end = lumpname.LastIndexOf("/"); // find last '/'
|
||||
|
||||
// it's a top-level file, if it's a folder being loaded ( /xxx/yyy/:whatever.zs ) end is before than start, or if it's a zip ( xxx.zip/whatever.zs ) end would be -1
|
||||
// it's a top-level file, if it's a folder being loaded ( /xxx/yyy/:whatever.zs ) end is before than start, or if it's a zip ( xxx.zip:whatever.zs ) end would be -1
|
||||
bool topLevelFile = start > end ;
|
||||
|
||||
FString fullPath = topLevelFile ? FString {} : lumpname.Mid(start + 1, end - start - 1); // get path from lumpname (format 'wad:filepath/filename')
|
||||
|
|
@ -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);
|
||||
|
|
@ -232,6 +233,7 @@ static void InitTokenMap()
|
|||
TOKENDEF (TK_Out, ZCC_OUT);
|
||||
TOKENDEF (TK_Super, ZCC_SUPER);
|
||||
TOKENDEF (TK_Null, ZCC_NULLPTR);
|
||||
TOKENDEF (TK_Sealed, ZCC_SEALED);
|
||||
TOKENDEF ('~', ZCC_TILDE);
|
||||
TOKENDEF ('!', ZCC_BANG);
|
||||
TOKENDEF (TK_SizeOf, ZCC_SIZEOF);
|
||||
|
|
@ -407,8 +409,6 @@ PNamespace *ParseOneScript(const int baselump, ZCCParseState &state)
|
|||
int lumpnum = baselump;
|
||||
auto fileno = fileSystem.GetFileContainer(lumpnum);
|
||||
|
||||
FString file = fileSystem.GetFileFullPath(lumpnum);
|
||||
|
||||
state.FileNo = fileno;
|
||||
|
||||
if (TokenMap.CountUsed() == 0)
|
||||
|
|
@ -473,7 +473,7 @@ PNamespace *ParseOneScript(const int baselump, ZCCParseState &state)
|
|||
ParseSingleFile(&sc, nullptr, lumpnum, parser, state);
|
||||
for (unsigned i = 0; i < Includes.Size(); i++)
|
||||
{
|
||||
lumpnum = fileSystem.CheckNumForFullName(Includes[i], true);
|
||||
lumpnum = fileSystem.CheckNumForFullName(Includes[i].GetChars(), true);
|
||||
if (lumpnum == -1)
|
||||
{
|
||||
IncludeLocs[i].Message(MSG_ERROR, "Include script lump %s not found", Includes[i].GetChars());
|
||||
|
|
@ -503,7 +503,7 @@ PNamespace *ParseOneScript(const int baselump, ZCCParseState &state)
|
|||
// If the parser fails, there is no point starting the compiler, because it'd only flood the output with endless errors.
|
||||
if (FScriptPosition::ErrorCounter > 0)
|
||||
{
|
||||
I_Error("%d errors while parsing %s", FScriptPosition::ErrorCounter, fileSystem.GetFileFullPath(baselump).GetChars());
|
||||
I_Error("%d errors while parsing %s", FScriptPosition::ErrorCounter, fileSystem.GetFileFullPath(baselump).c_str());
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
|
@ -517,10 +517,10 @@ PNamespace *ParseOneScript(const int baselump, ZCCParseState &state)
|
|||
if (Args->CheckParm("-dumpast"))
|
||||
{
|
||||
FString ast = ZCC_PrintAST(state.TopNode);
|
||||
FString filename = fileSystem.GetFileFullPath(baselump);
|
||||
FString filename = fileSystem.GetFileFullPath(baselump).c_str();
|
||||
filename.ReplaceChars(":\\/?|", '.');
|
||||
filename << ".ast";
|
||||
FileWriter *ff = FileWriter::Open(filename);
|
||||
FileWriter *ff = FileWriter::Open(filename.GetChars());
|
||||
if (ff != NULL)
|
||||
{
|
||||
ff->Write(ast.GetChars(), ast.Len());
|
||||
|
|
@ -926,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);
|
||||
|
|
@ -1372,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);
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ enum
|
|||
ZCC_VirtualScope = 1 << 20,
|
||||
ZCC_Version = 1 << 21,
|
||||
ZCC_Internal = 1 << 22,
|
||||
ZCC_Sealed = 1 << 23,
|
||||
};
|
||||
|
||||
// Function parameter modifiers
|
||||
|
|
@ -100,6 +101,8 @@ enum EZCCTreeNodeType
|
|||
AST_MapType,
|
||||
AST_MapIteratorType,
|
||||
AST_DynArrayType,
|
||||
AST_FuncPtrParamDecl,
|
||||
AST_FuncPtrType,
|
||||
AST_ClassType,
|
||||
AST_Expression,
|
||||
AST_ExprID,
|
||||
|
|
@ -135,6 +138,7 @@ enum EZCCTreeNodeType
|
|||
AST_VectorValue,
|
||||
AST_DeclFlags,
|
||||
AST_ClassCast,
|
||||
AST_FunctionPtrCast,
|
||||
AST_StaticArrayStatement,
|
||||
AST_Property,
|
||||
AST_FlagDef,
|
||||
|
|
@ -251,6 +255,7 @@ struct ZCC_Class : ZCC_Struct
|
|||
{
|
||||
ZCC_Identifier *ParentName;
|
||||
ZCC_Identifier *Replaces;
|
||||
ZCC_Identifier *Sealed;
|
||||
|
||||
PClass *CType() { return static_cast<PClassType *>(Type)->Descriptor; }
|
||||
};
|
||||
|
|
@ -380,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;
|
||||
|
|
@ -426,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;
|
||||
|
|
|
|||
|
|
@ -408,13 +408,13 @@ DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, ByteAt, StringByteAt)
|
|||
|
||||
static void StringFilter(FString *self, FString *result)
|
||||
{
|
||||
*result = strbin1(*self);
|
||||
*result = strbin1(self->GetChars());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, Filter, StringFilter)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FString);
|
||||
ACTION_RETURN_STRING(strbin1(*self));
|
||||
ACTION_RETURN_STRING(strbin1(self->GetChars()));
|
||||
}
|
||||
|
||||
static int StringIndexOf(FString *self, const FString &substr, int startIndex)
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@
|
|||
#include "i_time.h"
|
||||
|
||||
#include "maps.h"
|
||||
#include "types.h"
|
||||
|
||||
static ZSMap<FName, DObject*> AllServices;
|
||||
|
||||
|
|
@ -148,7 +149,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawTexture, SBar_DrawTexture)
|
|||
void SBar_DrawImage(DStatusBarCore* self, const FString& texid, double x, double y, int flags, double alpha, double w, double h, double scaleX, double scaleY, int style, int color, int translation, double clipwidth)
|
||||
{
|
||||
if (!twod->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function");
|
||||
self->DrawGraphic(TexMan.CheckForTexture(texid, ETextureType::Any), x, y, flags, alpha, w, h, scaleX, scaleY, ERenderStyle(style), color, translation, clipwidth);
|
||||
self->DrawGraphic(TexMan.CheckForTexture(texid.GetChars(), ETextureType::Any), x, y, flags, alpha, w, h, scaleX, scaleY, ERenderStyle(style), color, translation, clipwidth);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawImage, SBar_DrawImage)
|
||||
|
|
@ -174,7 +175,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawImage, SBar_DrawImage)
|
|||
void SBar_DrawImageRotated(DStatusBarCore* self, const FString& texid, double x, double y, int flags, double angle, double alpha, double scaleX, double scaleY, int style, int color, int translation)
|
||||
{
|
||||
if (!twod->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function");
|
||||
self->DrawRotated(TexMan.CheckForTexture(texid, ETextureType::Any), x, y, flags, angle, alpha, scaleX, scaleY, color, translation, (ERenderStyle)style);
|
||||
self->DrawRotated(TexMan.CheckForTexture(texid.GetChars(), ETextureType::Any), x, y, flags, angle, alpha, scaleX, scaleY, color, translation, (ERenderStyle)style);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawImageRotated, SBar_DrawImageRotated)
|
||||
|
|
@ -424,7 +425,7 @@ DEFINE_ACTION_FUNCTION(_TexMan, GetName)
|
|||
{
|
||||
// Textures for full path names do not have their own name, they merely link to the source lump.
|
||||
auto lump = tex->GetSourceLump();
|
||||
if (fileSystem.GetLinkedTexture(lump) == tex)
|
||||
if (TexMan.GetLinkedTexture(lump) == tex)
|
||||
retval = fileSystem.GetFileFullName(lump);
|
||||
}
|
||||
}
|
||||
|
|
@ -433,7 +434,7 @@ DEFINE_ACTION_FUNCTION(_TexMan, GetName)
|
|||
|
||||
static int CheckForTexture(const FString& name, int type, int flags)
|
||||
{
|
||||
return TexMan.CheckForTexture(name, static_cast<ETextureType>(type), flags).GetIndex();
|
||||
return TexMan.CheckForTexture(name.GetChars(), static_cast<ETextureType>(type), flags).GetIndex();
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, CheckForTexture, CheckForTexture)
|
||||
|
|
@ -560,7 +561,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, CheckRealHeight, CheckRealHeight)
|
|||
|
||||
static int OkForLocalization_(int index, const FString& substitute)
|
||||
{
|
||||
return sysCallbacks.OkForLocalization? sysCallbacks.OkForLocalization(FSetTextureID(index), substitute) : false;
|
||||
return sysCallbacks.OkForLocalization? sysCallbacks.OkForLocalization(FSetTextureID(index), substitute.GetChars()) : false;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, OkForLocalization, OkForLocalization_)
|
||||
|
|
@ -667,9 +668,9 @@ DEFINE_ACTION_FUNCTION_NATIVE(FFont, GetBottomAlignOffset, GetBottomAlignOffset)
|
|||
ACTION_RETURN_FLOAT(GetBottomAlignOffset(self, code));
|
||||
}
|
||||
|
||||
static int StringWidth(FFont *font, const FString &str)
|
||||
static int StringWidth(FFont *font, const FString &str, bool localize)
|
||||
{
|
||||
const char *txt = str[0] == '$' ? GStrings(&str[1]) : str.GetChars();
|
||||
const char *txt = (localize && str[0] == '$') ? GStrings(&str[1]) : str.GetChars();
|
||||
return font->StringWidth(txt);
|
||||
}
|
||||
|
||||
|
|
@ -677,12 +678,13 @@ DEFINE_ACTION_FUNCTION_NATIVE(FFont, StringWidth, StringWidth)
|
|||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FFont);
|
||||
PARAM_STRING(str);
|
||||
ACTION_RETURN_INT(StringWidth(self, str));
|
||||
PARAM_BOOL(localize);
|
||||
ACTION_RETURN_INT(StringWidth(self, str, localize));
|
||||
}
|
||||
|
||||
static int GetMaxAscender(FFont* font, const FString& str)
|
||||
static int GetMaxAscender(FFont* font, const FString& str, bool localize)
|
||||
{
|
||||
const char* txt = str[0] == '$' ? GStrings(&str[1]) : str.GetChars();
|
||||
const char* txt = (localize && str[0] == '$') ? GStrings(&str[1]) : str.GetChars();
|
||||
return font->GetMaxAscender(txt);
|
||||
}
|
||||
|
||||
|
|
@ -690,12 +692,13 @@ DEFINE_ACTION_FUNCTION_NATIVE(FFont, GetMaxAscender, GetMaxAscender)
|
|||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FFont);
|
||||
PARAM_STRING(str);
|
||||
ACTION_RETURN_INT(GetMaxAscender(self, str));
|
||||
PARAM_BOOL(localize);
|
||||
ACTION_RETURN_INT(GetMaxAscender(self, str, localize));
|
||||
}
|
||||
|
||||
static int CanPrint(FFont *font, const FString &str)
|
||||
static int CanPrint(FFont *font, const FString &str, bool localize)
|
||||
{
|
||||
const char *txt = str[0] == '$' ? GStrings(&str[1]) : str.GetChars();
|
||||
const char *txt = (localize && str[0] == '$') ? GStrings(&str[1]) : str.GetChars();
|
||||
return font->CanPrint(txt);
|
||||
}
|
||||
|
||||
|
|
@ -703,7 +706,8 @@ DEFINE_ACTION_FUNCTION_NATIVE(FFont, CanPrint, CanPrint)
|
|||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FFont);
|
||||
PARAM_STRING(str);
|
||||
ACTION_RETURN_INT(CanPrint(self, str));
|
||||
PARAM_BOOL(localize);
|
||||
ACTION_RETURN_INT(CanPrint(self, str, localize));
|
||||
}
|
||||
|
||||
static int FindFontColor(int name)
|
||||
|
|
@ -785,14 +789,14 @@ DEFINE_ACTION_FUNCTION(_Wads, CheckNumForName)
|
|||
PARAM_INT(ns);
|
||||
PARAM_INT(wadnum);
|
||||
PARAM_BOOL(exact);
|
||||
ACTION_RETURN_INT(fileSystem.CheckNumForName(name, ns, wadnum, exact));
|
||||
ACTION_RETURN_INT(fileSystem.CheckNumForName(name.GetChars(), ns, wadnum, exact));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Wads, CheckNumForFullName)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_STRING(name);
|
||||
ACTION_RETURN_INT(fileSystem.CheckNumForFullName(name));
|
||||
ACTION_RETURN_INT(fileSystem.CheckNumForFullName(name.GetChars()));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Wads, FindLump)
|
||||
|
|
@ -802,7 +806,7 @@ DEFINE_ACTION_FUNCTION(_Wads, FindLump)
|
|||
PARAM_INT(startlump);
|
||||
PARAM_INT(ns);
|
||||
const bool isLumpValid = startlump >= 0 && startlump < fileSystem.GetNumEntries();
|
||||
ACTION_RETURN_INT(isLumpValid ? fileSystem.FindLump(name, &startlump, 0 != ns) : -1);
|
||||
ACTION_RETURN_INT(isLumpValid ? fileSystem.FindLump(name.GetChars(), &startlump, 0 != ns) : -1);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Wads, FindLumpFullName)
|
||||
|
|
@ -812,16 +816,14 @@ DEFINE_ACTION_FUNCTION(_Wads, FindLumpFullName)
|
|||
PARAM_INT(startlump);
|
||||
PARAM_BOOL(noext);
|
||||
const bool isLumpValid = startlump >= 0 && startlump < fileSystem.GetNumEntries();
|
||||
ACTION_RETURN_INT(isLumpValid ? fileSystem.FindLumpFullName(name, &startlump, noext) : -1);
|
||||
ACTION_RETURN_INT(isLumpValid ? fileSystem.FindLumpFullName(name.GetChars(), &startlump, noext) : -1);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Wads, GetLumpName)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(lump);
|
||||
FString lumpname;
|
||||
fileSystem.GetFileShortName(lumpname, lump);
|
||||
ACTION_RETURN_STRING(lumpname);
|
||||
ACTION_RETURN_STRING(fileSystem.GetFileShortName(lump));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Wads, GetLumpFullName)
|
||||
|
|
@ -843,7 +845,7 @@ DEFINE_ACTION_FUNCTION(_Wads, ReadLump)
|
|||
PARAM_PROLOGUE;
|
||||
PARAM_INT(lump);
|
||||
const bool isLumpValid = lump >= 0 && lump < fileSystem.GetNumEntries();
|
||||
ACTION_RETURN_STRING(isLumpValid ? fileSystem.ReadFile(lump).GetString() : FString());
|
||||
ACTION_RETURN_STRING(isLumpValid ? GetStringFromLump(lump, false) : FString());
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
@ -1022,7 +1024,7 @@ DEFINE_ACTION_FUNCTION(FKeyBindings, SetBind)
|
|||
}
|
||||
|
||||
|
||||
self->SetBind(k, cmd);
|
||||
self->SetBind(k, cmd.GetChars());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1040,7 +1042,8 @@ DEFINE_ACTION_FUNCTION(FKeyBindings, NameAllKeys)
|
|||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_POINTER(array, TArray<int>);
|
||||
auto buffer = C_NameKeys(array->Data(), array->Size(), true);
|
||||
PARAM_BOOL(color);
|
||||
auto buffer = C_NameKeys(array->Data(), array->Size(), color);
|
||||
ACTION_RETURN_STRING(buffer);
|
||||
}
|
||||
|
||||
|
|
@ -1060,7 +1063,7 @@ DEFINE_ACTION_FUNCTION(FKeyBindings, GetAllKeysForCommand)
|
|||
PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings);
|
||||
PARAM_POINTER(array, TArray<int>);
|
||||
PARAM_STRING(cmd);
|
||||
*array = self->GetKeysForCommand(cmd);
|
||||
*array = self->GetKeysForCommand(cmd.GetChars());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1082,7 +1085,7 @@ DEFINE_ACTION_FUNCTION(FKeyBindings, UnbindACommand)
|
|||
I_FatalError("Attempt to unbind key bindings for '%s' outside of menu code", cmd.GetChars());
|
||||
}
|
||||
|
||||
self->UnbindACommand(cmd);
|
||||
self->UnbindACommand(cmd.GetChars());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1100,7 +1103,7 @@ DEFINE_ACTION_FUNCTION(DOptionMenuItemCommand, DoCommand)
|
|||
}
|
||||
|
||||
UnsafeExecutionScope scope(unsafe);
|
||||
AddCommandString(cmd);
|
||||
AddCommandString(cmd.GetChars());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1145,7 +1148,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(_System, StopAllSounds, StopAllSounds)
|
|||
|
||||
static int PlayMusic(const FString& musname, int order, int looped)
|
||||
{
|
||||
return S_ChangeMusic(musname, order, !!looped, true);
|
||||
return S_ChangeMusic(musname.GetChars(), order, !!looped, true);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_System, PlayMusic, PlayMusic)
|
||||
|
|
@ -1346,3 +1349,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()));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,13 @@ void JitDumpLog(FILE *file, VMScriptFunction *sfunc)
|
|||
{
|
||||
using namespace asmjit;
|
||||
StringLogger logger;
|
||||
|
||||
if(sfunc->VarFlags & VARF_Abstract)
|
||||
{
|
||||
// Printf(TEXTCOLOR_ORANGE "Skipping abstract function during JIT dump: %s\n", sfunc->PrintableName.GetChars());
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ThrowingErrorHandler errorHandler;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ static int CastS2I(FString *b) { return (int)b->ToLong(); }
|
|||
static double CastS2F(FString *b) { return b->ToDouble(); }
|
||||
static int CastS2N(FString *b) { return b->Len() == 0 ? NAME_None : FName(*b).GetIndex(); }
|
||||
static void CastN2S(FString *a, int b) { FName name = FName(ENamedName(b)); *a = name.IsValidName() ? name.GetChars() : ""; }
|
||||
static int CastS2Co(FString *b) { return V_GetColor(*b); }
|
||||
static int CastS2Co(FString *b) { return V_GetColor(b->GetChars()); }
|
||||
static void CastCo2S(FString *a, int b) { PalEntry c(b); a->Format("%02x %02x %02x", c.r, c.g, c.b); }
|
||||
static int CastS2So(FString *b) { return S_FindSound(*b).index(); }
|
||||
static void CastSo2S(FString* a, int b) { *a = soundEngine->GetSoundName(FSoundID::fromInt(b)); }
|
||||
|
|
|
|||
|
|
@ -724,6 +724,11 @@ struct AFuncDesc
|
|||
extern FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr; \
|
||||
MSVC_FSEG FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr GCC_FSEG = &VMField_##cls##_##scriptname;
|
||||
|
||||
#define DEFINE_FIELD_NAMED_UNSIZED(cls, name, scriptname) \
|
||||
static const FieldDesc VMField_##cls##_##scriptname = { #cls, #scriptname, (unsigned)myoffsetof(cls, name), ~0u, 0 }; \
|
||||
extern FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr; \
|
||||
MSVC_FSEG FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr GCC_FSEG = &VMField_##cls##_##scriptname;
|
||||
|
||||
#define DEFINE_FIELD_BIT(cls, name, scriptname, bitval) \
|
||||
static const FieldDesc VMField_##cls##_##scriptname = { #cls, #scriptname, (unsigned)myoffsetof(cls, name), (unsigned)sizeof(cls::name), bitval }; \
|
||||
extern FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr; \
|
||||
|
|
@ -792,6 +797,7 @@ class AActor;
|
|||
class PFunction;
|
||||
|
||||
VMFunction *FindVMFunction(PClass *cls, const char *name);
|
||||
VMFunction* FindVMFunction(const char* name);
|
||||
#define DECLARE_VMFUNC(cls, name) static VMFunction *name; if (name == nullptr) name = FindVMFunction(RUNTIME_CLASS(cls), #name);
|
||||
|
||||
FString FStringFormat(VM_ARGS, int offset = 0);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -2117,7 +2126,7 @@ static void DoCast(const VMRegisters ®, const VMFrame *f, int a, int b, int c
|
|||
|
||||
case CAST_S2Co:
|
||||
ASSERTD(a); ASSERTS(b);
|
||||
reg.d[a] = V_GetColor(reg.s[b]);
|
||||
reg.d[a] = V_GetColor(reg.s[b].GetChars());
|
||||
break;
|
||||
|
||||
case CAST_Co2S:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue