- 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:
Christoph Oelckers 2016-11-27 15:50:58 +01:00
parent 3dd323ac0d
commit f409a24d2d
7 changed files with 125 additions and 13 deletions

View file

@ -1688,7 +1688,7 @@ PPointer *NewPointer(PType *type, bool isconst)
PType *ptype = TypeTable.FindType(RUNTIME_CLASS(PPointer), (intptr_t)type, isconst ? 1 : 0, &bucket);
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);
}
return static_cast<PPointer *>(ptype);

View file

@ -286,6 +286,7 @@ xx(Random2)
xx(RandomPick)
xx(FRandomPick)
xx(GetClass)
xx(GetDefaultByType)
xx(Exp)
xx(Log10)
xx(Ceil)

View file

@ -262,7 +262,7 @@ static bool AreCompatiblePointerTypes(PType *dest, PType *source, bool forcompar
auto fromtype = static_cast<PPointer *>(source);
auto totype = static_cast<PPointer *>(dest);
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->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(ValueType == TypeBool || IsInteger()); // this may have been changed by an int cast.
ExpEmit from = Operand->Emit(build);
from.Free(build);
ExpEmit to(build, REGT_INT);
assert(!from.Konst);
// 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();
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;
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 = expr->Resolve(ctx);
if (expr == nullptr)
@ -7012,6 +7026,14 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
}
break;
case NAME_GetDefaultByType:
if (CheckArgSize(NAME_GetDefaultByType, ArgList, 1, 1, ScriptPosition))
{
func = new FxGetDefaultByType(ArgList[0]);
ArgList[0] = nullptr;
}
break;
case NAME_Random:
// allow calling Random without arguments to default to (0, 255)
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)
:FxExpression(EFX_ColorLiteral, sc)
{

View file

@ -289,6 +289,7 @@ enum EFxType
EFX_NamedNode,
EFX_GetClass,
EFX_ColorLiteral,
EFX_GetDefaultByType,
EFX_COUNT
};
@ -1542,6 +1543,24 @@ public:
ExpEmit Emit(VMFunctionBuilder *build);
};
//==========================================================================
//
// FxFlopFunctionCall
//
//==========================================================================
class FxGetDefaultByType : public FxExpression
{
FxExpression *Self;
public:
FxGetDefaultByType(FxExpression *self);
~FxGetDefaultByType();
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build);
};
//==========================================================================
//
// FxColorLiteral

View file

@ -424,7 +424,7 @@ int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMRetur
{
bool allocated = false;
try
{
{
if (func->Native)
{
return static_cast<VMNativeFunction *>(func)->NativeCall(this, params, func->DefaultArgs, numparams, results, numresults);

View file

@ -238,7 +238,7 @@ class Actor : Thinker native
VisiblePitch 0, 0;
DefaultStateUsage SUF_ACTOR|SUF_OVERLAY;
}
// Functions
// 'parked' global functions.
@ -268,7 +268,6 @@ class Actor : Thinker native
virtual native bool SpecialBlastHandling (Actor source, double strength);
native static readonly<Actor> GetDefaultByType(class<Actor> cls);
native static double GetDefaultSpeed(class<Actor> type);
native void RemoveFromHash();
native string GetTag(string defstr = "");

View file

@ -28,7 +28,7 @@ class PoisonBag : Actor
void A_PoisonBagInit()
{
Actor mo = Spawn("PoisonCloud", (pos.xy, 28), ALLOW_REPLACE);
Actor mo = Spawn("PoisonCloud", pos + (0, 0, 28), ALLOW_REPLACE);
if (mo)
{
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 (!Icon.isValid())
{
Inventory defbag = Inventory(GetDefaultByType("ArtiPoisonBag"));
Icon = defbag.Icon;
Icon = GetDefaultByType("ArtiPoisonBag").Icon;
}
}
@ -469,7 +468,7 @@ class PoisonCloud : Actor
special1 = 24+(random[PoisonCloud]() & 7);
special2 = 0;
}
//===========================================================================
//
//
@ -482,7 +481,7 @@ class PoisonCloud : Actor
{
bool mate = (target != null && victim.player != target.player && victim.IsTeammate (target));
bool dopoison;
if (!mate)
{
dopoison = victim.player.poisoncount < 4;
@ -494,7 +493,7 @@ class PoisonCloud : Actor
if (dopoison)
{
int damage = 15 + (random[PoisonCloud]()&15);
damage = 15 + (random[PoisonCloud]() & 15);
if (mate)
{
damage = (int)(damage * level.teamdamage);