mirror of
https://github.com/ZDoom/qzdoom.git
synced 2025-05-31 00:51:21 +00:00
- fixed: Readonly pointers never were flagged as such.
- fixed: Assignment from a readonly to a read-allowed pointer must be an error. - made GetDefaultByType a builtin so that it can do proper type assignment to the result, which for a function would be problematic in this case, even if automatic type deduction was implemented. Since this returns the class defaults which are not a real object, the result cannot be subjected to a type cast. - error out if a type cast of a readonly pointer is attempted. - fixed: FxBooleanNot could clobber a local variable because it used the source register to manipulate the result.
This commit is contained in:
parent
3dd323ac0d
commit
f409a24d2d
7 changed files with 125 additions and 13 deletions
|
@ -1688,7 +1688,7 @@ PPointer *NewPointer(PType *type, bool isconst)
|
||||||
PType *ptype = TypeTable.FindType(RUNTIME_CLASS(PPointer), (intptr_t)type, isconst ? 1 : 0, &bucket);
|
PType *ptype = TypeTable.FindType(RUNTIME_CLASS(PPointer), (intptr_t)type, isconst ? 1 : 0, &bucket);
|
||||||
if (ptype == NULL)
|
if (ptype == NULL)
|
||||||
{
|
{
|
||||||
ptype = new PPointer(type);
|
ptype = new PPointer(type, isconst);
|
||||||
TypeTable.AddType(ptype, RUNTIME_CLASS(PPointer), (intptr_t)type, isconst ? 1 : 0, bucket);
|
TypeTable.AddType(ptype, RUNTIME_CLASS(PPointer), (intptr_t)type, isconst ? 1 : 0, bucket);
|
||||||
}
|
}
|
||||||
return static_cast<PPointer *>(ptype);
|
return static_cast<PPointer *>(ptype);
|
||||||
|
|
|
@ -286,6 +286,7 @@ xx(Random2)
|
||||||
xx(RandomPick)
|
xx(RandomPick)
|
||||||
xx(FRandomPick)
|
xx(FRandomPick)
|
||||||
xx(GetClass)
|
xx(GetClass)
|
||||||
|
xx(GetDefaultByType)
|
||||||
xx(Exp)
|
xx(Exp)
|
||||||
xx(Log10)
|
xx(Log10)
|
||||||
xx(Ceil)
|
xx(Ceil)
|
||||||
|
|
|
@ -262,7 +262,7 @@ static bool AreCompatiblePointerTypes(PType *dest, PType *source, bool forcompar
|
||||||
auto fromtype = static_cast<PPointer *>(source);
|
auto fromtype = static_cast<PPointer *>(source);
|
||||||
auto totype = static_cast<PPointer *>(dest);
|
auto totype = static_cast<PPointer *>(dest);
|
||||||
if (fromtype == nullptr) return true;
|
if (fromtype == nullptr) return true;
|
||||||
if (!forcompare && totype->IsConst && !fromtype->IsConst) return false;
|
if (!forcompare && totype->IsConst != fromtype->IsConst) return false;
|
||||||
if (fromtype == totype) return true;
|
if (fromtype == totype) return true;
|
||||||
if (fromtype->PointedType->IsKindOf(RUNTIME_CLASS(PClass)) && totype->PointedType->IsKindOf(RUNTIME_CLASS(PClass)))
|
if (fromtype->PointedType->IsKindOf(RUNTIME_CLASS(PClass)) && totype->PointedType->IsKindOf(RUNTIME_CLASS(PClass)))
|
||||||
{
|
{
|
||||||
|
@ -1874,10 +1874,13 @@ ExpEmit FxUnaryNotBoolean::Emit(VMFunctionBuilder *build)
|
||||||
assert(Operand->ValueType == TypeBool);
|
assert(Operand->ValueType == TypeBool);
|
||||||
assert(ValueType == TypeBool || IsInteger()); // this may have been changed by an int cast.
|
assert(ValueType == TypeBool || IsInteger()); // this may have been changed by an int cast.
|
||||||
ExpEmit from = Operand->Emit(build);
|
ExpEmit from = Operand->Emit(build);
|
||||||
|
from.Free(build);
|
||||||
|
ExpEmit to(build, REGT_INT);
|
||||||
assert(!from.Konst);
|
assert(!from.Konst);
|
||||||
// boolean not is the same as XOR-ing the lowest bit
|
// boolean not is the same as XOR-ing the lowest bit
|
||||||
build->Emit(OP_XOR_RK, from.RegNum, from.RegNum, build->GetConstantInt(1));
|
|
||||||
return from;
|
build->Emit(OP_XOR_RK, to.RegNum, from.RegNum, build->GetConstantInt(1));
|
||||||
|
return to;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -4309,7 +4312,18 @@ FxExpression *FxDynamicCast::Resolve(FCompileContext& ctx)
|
||||||
{
|
{
|
||||||
CHECKRESOLVED();
|
CHECKRESOLVED();
|
||||||
SAFE_RESOLVE(expr, ctx);
|
SAFE_RESOLVE(expr, ctx);
|
||||||
|
if (expr->ExprType == EFX_GetDefaultByType)
|
||||||
|
{
|
||||||
|
int a = 0;
|
||||||
|
}
|
||||||
bool constflag = expr->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast<PPointer *>(expr->ValueType)->IsConst;
|
bool constflag = expr->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast<PPointer *>(expr->ValueType)->IsConst;
|
||||||
|
if (constflag)
|
||||||
|
{
|
||||||
|
// readonly pointers are normally only used for class defaults which lack type information to be cast properly, so we have to error out here.
|
||||||
|
ScriptPosition.Message(MSG_ERROR, "Cannot cast a readonly pointer");
|
||||||
|
delete this;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
expr = new FxTypeCast(expr, NewPointer(RUNTIME_CLASS(DObject), constflag), true, true);
|
expr = new FxTypeCast(expr, NewPointer(RUNTIME_CLASS(DObject), constflag), true, true);
|
||||||
expr = expr->Resolve(ctx);
|
expr = expr->Resolve(ctx);
|
||||||
if (expr == nullptr)
|
if (expr == nullptr)
|
||||||
|
@ -7012,6 +7026,14 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NAME_GetDefaultByType:
|
||||||
|
if (CheckArgSize(NAME_GetDefaultByType, ArgList, 1, 1, ScriptPosition))
|
||||||
|
{
|
||||||
|
func = new FxGetDefaultByType(ArgList[0]);
|
||||||
|
ArgList[0] = nullptr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case NAME_Random:
|
case NAME_Random:
|
||||||
// allow calling Random without arguments to default to (0, 255)
|
// allow calling Random without arguments to default to (0, 255)
|
||||||
if (ArgList.Size() == 0)
|
if (ArgList.Size() == 0)
|
||||||
|
@ -8147,6 +8169,78 @@ ExpEmit FxGetClass::Emit(VMFunctionBuilder *build)
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
|
FxGetDefaultByType::FxGetDefaultByType(FxExpression *self)
|
||||||
|
:FxExpression(EFX_GetDefaultByType, self->ScriptPosition)
|
||||||
|
{
|
||||||
|
Self = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
FxGetDefaultByType::~FxGetDefaultByType()
|
||||||
|
{
|
||||||
|
SAFE_DELETE(Self);
|
||||||
|
}
|
||||||
|
|
||||||
|
FxExpression *FxGetDefaultByType::Resolve(FCompileContext &ctx)
|
||||||
|
{
|
||||||
|
SAFE_RESOLVE(Self, ctx);
|
||||||
|
PClass *cls = nullptr;
|
||||||
|
|
||||||
|
if (Self->ValueType == TypeString || Self->ValueType == TypeName)
|
||||||
|
{
|
||||||
|
if (Self->isConstant())
|
||||||
|
{
|
||||||
|
cls = PClass::FindActor(static_cast<FxConstant *>(Self)->GetValue().GetName());
|
||||||
|
if (cls == nullptr)
|
||||||
|
{
|
||||||
|
ScriptPosition.Message(MSG_ERROR, "GetDefaultByType() requires an actor class type, but got %s", static_cast<FxConstant *>(Self)->GetValue().GetString().GetChars());
|
||||||
|
delete this;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
Self = new FxConstant(cls, NewClassPointer(cls), ScriptPosition);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// this is the ugly case. We do not know what we have and cannot do proper type casting.
|
||||||
|
// For now error out and let this case require explicit handling on the user side.
|
||||||
|
ScriptPosition.Message(MSG_ERROR, "GetDefaultByType() requires an actor class type", static_cast<FxConstant *>(Self)->GetValue().GetString().GetChars());
|
||||||
|
delete this;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto cp = dyn_cast<PClassPointer>(Self->ValueType);
|
||||||
|
if (cp == nullptr || !cp->ClassRestriction->IsDescendantOf(RUNTIME_CLASS(AActor)))
|
||||||
|
{
|
||||||
|
ScriptPosition.Message(MSG_ERROR, "GetDefaultByType() requires an actor class type");
|
||||||
|
delete this;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
cls = cp->ClassRestriction;
|
||||||
|
}
|
||||||
|
ValueType = NewPointer(cls, true);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpEmit FxGetDefaultByType::Emit(VMFunctionBuilder *build)
|
||||||
|
{
|
||||||
|
ExpEmit op = Self->Emit(build);
|
||||||
|
op.Free(build);
|
||||||
|
ExpEmit to(build, REGT_POINTER);
|
||||||
|
if (op.Konst)
|
||||||
|
{
|
||||||
|
build->Emit(OP_LKP, to.RegNum, op.RegNum);
|
||||||
|
op = to;
|
||||||
|
}
|
||||||
|
build->Emit(OP_LO, to.RegNum, op.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults)));
|
||||||
|
return to;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
FxColorLiteral::FxColorLiteral(FArgumentList &args, FScriptPosition &sc)
|
FxColorLiteral::FxColorLiteral(FArgumentList &args, FScriptPosition &sc)
|
||||||
:FxExpression(EFX_ColorLiteral, sc)
|
:FxExpression(EFX_ColorLiteral, sc)
|
||||||
{
|
{
|
||||||
|
|
|
@ -289,6 +289,7 @@ enum EFxType
|
||||||
EFX_NamedNode,
|
EFX_NamedNode,
|
||||||
EFX_GetClass,
|
EFX_GetClass,
|
||||||
EFX_ColorLiteral,
|
EFX_ColorLiteral,
|
||||||
|
EFX_GetDefaultByType,
|
||||||
EFX_COUNT
|
EFX_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1542,6 +1543,24 @@ public:
|
||||||
ExpEmit Emit(VMFunctionBuilder *build);
|
ExpEmit Emit(VMFunctionBuilder *build);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FxFlopFunctionCall
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
class FxGetDefaultByType : public FxExpression
|
||||||
|
{
|
||||||
|
FxExpression *Self;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
FxGetDefaultByType(FxExpression *self);
|
||||||
|
~FxGetDefaultByType();
|
||||||
|
FxExpression *Resolve(FCompileContext&);
|
||||||
|
ExpEmit Emit(VMFunctionBuilder *build);
|
||||||
|
};
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// FxColorLiteral
|
// FxColorLiteral
|
||||||
|
|
|
@ -424,7 +424,7 @@ int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMRetur
|
||||||
{
|
{
|
||||||
bool allocated = false;
|
bool allocated = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (func->Native)
|
if (func->Native)
|
||||||
{
|
{
|
||||||
return static_cast<VMNativeFunction *>(func)->NativeCall(this, params, func->DefaultArgs, numparams, results, numresults);
|
return static_cast<VMNativeFunction *>(func)->NativeCall(this, params, func->DefaultArgs, numparams, results, numresults);
|
||||||
|
|
|
@ -238,7 +238,7 @@ class Actor : Thinker native
|
||||||
VisiblePitch 0, 0;
|
VisiblePitch 0, 0;
|
||||||
DefaultStateUsage SUF_ACTOR|SUF_OVERLAY;
|
DefaultStateUsage SUF_ACTOR|SUF_OVERLAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
|
|
||||||
// 'parked' global functions.
|
// 'parked' global functions.
|
||||||
|
@ -268,7 +268,6 @@ class Actor : Thinker native
|
||||||
virtual native bool SpecialBlastHandling (Actor source, double strength);
|
virtual native bool SpecialBlastHandling (Actor source, double strength);
|
||||||
|
|
||||||
|
|
||||||
native static readonly<Actor> GetDefaultByType(class<Actor> cls);
|
|
||||||
native static double GetDefaultSpeed(class<Actor> type);
|
native static double GetDefaultSpeed(class<Actor> type);
|
||||||
native void RemoveFromHash();
|
native void RemoveFromHash();
|
||||||
native string GetTag(string defstr = "");
|
native string GetTag(string defstr = "");
|
||||||
|
|
|
@ -28,7 +28,7 @@ class PoisonBag : Actor
|
||||||
|
|
||||||
void A_PoisonBagInit()
|
void A_PoisonBagInit()
|
||||||
{
|
{
|
||||||
Actor mo = Spawn("PoisonCloud", (pos.xy, 28), ALLOW_REPLACE);
|
Actor mo = Spawn("PoisonCloud", pos + (0, 0, 28), ALLOW_REPLACE);
|
||||||
if (mo)
|
if (mo)
|
||||||
{
|
{
|
||||||
mo.target = target;
|
mo.target = target;
|
||||||
|
@ -184,8 +184,7 @@ class ArtiPoisonBag : Inventory
|
||||||
// If a subclass's specific icon is not defined, let it use the base class's.
|
// If a subclass's specific icon is not defined, let it use the base class's.
|
||||||
if (!Icon.isValid())
|
if (!Icon.isValid())
|
||||||
{
|
{
|
||||||
Inventory defbag = Inventory(GetDefaultByType("ArtiPoisonBag"));
|
Icon = GetDefaultByType("ArtiPoisonBag").Icon;
|
||||||
Icon = defbag.Icon;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -469,7 +468,7 @@ class PoisonCloud : Actor
|
||||||
special1 = 24+(random[PoisonCloud]() & 7);
|
special1 = 24+(random[PoisonCloud]() & 7);
|
||||||
special2 = 0;
|
special2 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
@ -482,7 +481,7 @@ class PoisonCloud : Actor
|
||||||
{
|
{
|
||||||
bool mate = (target != null && victim.player != target.player && victim.IsTeammate (target));
|
bool mate = (target != null && victim.player != target.player && victim.IsTeammate (target));
|
||||||
bool dopoison;
|
bool dopoison;
|
||||||
|
|
||||||
if (!mate)
|
if (!mate)
|
||||||
{
|
{
|
||||||
dopoison = victim.player.poisoncount < 4;
|
dopoison = victim.player.poisoncount < 4;
|
||||||
|
@ -494,7 +493,7 @@ class PoisonCloud : Actor
|
||||||
|
|
||||||
if (dopoison)
|
if (dopoison)
|
||||||
{
|
{
|
||||||
int damage = 15 + (random[PoisonCloud]()&15);
|
damage = 15 + (random[PoisonCloud]() & 15);
|
||||||
if (mate)
|
if (mate)
|
||||||
{
|
{
|
||||||
damage = (int)(damage * level.teamdamage);
|
damage = (int)(damage * level.teamdamage);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue