- implemented passing vectors as parameters. So far working for native functions.

- removed the bogus optional value from the first A_Jump argument. A quick test with an older ZDoom revealed that this was never working - and implementing it would make things a lot more complicated, especially error checking in the code generator.
- fixed: The check for insufficient parameters to a function call was missing.
This commit is contained in:
Christoph Oelckers 2016-10-29 13:10:27 +02:00
parent e94b4cc74e
commit 7209f9edd6
9 changed files with 78 additions and 37 deletions

View file

@ -732,3 +732,4 @@ xx(__decorate_internal_float__)
xx(DamageFunction)
xx(Length)
xx(Unit)
xx(A_Jump)

View file

@ -1148,7 +1148,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_BulletAttack)
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Jump)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT_DEF(maxchance);
PARAM_INT(maxchance);
paramnum++; // Increment paramnum to point at the first jump target
int count = numparam - paramnum;

View file

@ -532,6 +532,17 @@ void AActor::SetOrigin(double x, double y, double z, bool moving)
if (!moving) ClearInterpolation();
}
DEFINE_ACTION_FUNCTION(AActor, SetOrigin)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_FLOAT(x);
PARAM_FLOAT(y);
PARAM_FLOAT(z);
PARAM_BOOL(moving);
self->SetOrigin(x, y, z, moving);
return 0;
}
//===========================================================================
//
// FBlockNode - allows to link actors into multiple blocks in the blockmap

View file

@ -334,7 +334,7 @@ PPrototype *FxExpression::ReturnProto()
//
//==========================================================================
static void EmitParameter(VMFunctionBuilder *build, FxExpression *operand, const FScriptPosition &pos)
static int EmitParameter(VMFunctionBuilder *build, FxExpression *operand, const FScriptPosition &pos)
{
ExpEmit where = operand->Emit(build);
@ -342,6 +342,7 @@ static void EmitParameter(VMFunctionBuilder *build, FxExpression *operand, const
{
pos.Message(MSG_ERROR, "Attempted to pass a non-value");
build->Emit(OP_PARAM, 0, where.RegType, where.RegNum);
return 1;
}
else
{
@ -350,8 +351,17 @@ static void EmitParameter(VMFunctionBuilder *build, FxExpression *operand, const
{
regtype |= REGT_KONST;
}
else if (where.RegCount == 2)
{
regtype |= REGT_MULTIREG2;
}
else if (where.RegCount == 3)
{
regtype |= REGT_MULTIREG3;
}
build->Emit(OP_PARAM, 0, regtype, where.RegNum);
where.Free(build);
return where.RegCount;
}
}
@ -5873,6 +5883,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
delete this;
return nullptr;
}
for (unsigned i = 0; i < ArgList->Size(); i++)
{
// Varargs must all have the same type as the last typed argument. A_Jump is the only function using it.
@ -5888,6 +5899,18 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
failed |= (x == nullptr);
(*ArgList)[i] = x;
}
int numargs = ArgList->Size() + implicit;
if ((unsigned)numargs < argtypes.Size() && argtypes[numargs] != nullptr)
{
auto flags = Function->Variants[0].ArgFlags[numargs];
if (!(flags & VARF_Optional))
{
ScriptPosition.Message(MSG_ERROR, "Insufficient arguments in call to %s", Function->SymbolName.GetChars());
delete this;
return nullptr;
}
}
}
if (failed)
{
@ -5917,7 +5940,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
{
assert((build->IsActionFunc && build->Registers[REGT_POINTER].GetMostUsed() >= NAP) ||
(!build->IsActionFunc && build->Registers[REGT_POINTER].GetMostUsed() >= 1));
int count = (ArgList ? ArgList->Size() : 0);
int count = 0; (ArgList ? ArgList->Size() : 0);
if (count == 1)
{
@ -5928,6 +5951,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
}
}
count = 0;
// Emit code to pass implied parameters
if (Function->Variants[0].Flags & VARF_Method)
{
@ -5958,7 +5982,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
{
for (unsigned i = 0; i < ArgList->Size(); ++i)
{
EmitParameter(build, (*ArgList)[i], ScriptPosition);
count += EmitParameter(build, (*ArgList)[i], ScriptPosition);
}
}
// Get a constant register for this function

View file

@ -134,7 +134,9 @@ enum
REGT_TYPE = 3,
REGT_KONST = 4,
REGT_MULTIREG = 8, // (e.g. a vector)
REGT_MULTIREG2 = 8,
REGT_MULTIREG3 = 16, // (e.g. a vector)
REGT_MULTIREG = 24,
REGT_ADDROF = 32, // used with PARAM: pass address of this register
REGT_NIL = 128 // parameter was omitted

View file

@ -498,7 +498,11 @@ size_t VMFunctionBuilder::Emit(int opcode, int opa, int opb, int opc)
assert(opc >= 0 && opc <= 255);
if (opcode == OP_PARAM)
{
ParamChange(1);
int chg;
if (opb & REGT_MULTIREG2) chg = 2;
else if (opb&REGT_MULTIREG3) chg = 3;
else chg = 1;
ParamChange(chg);
}
else if (opcode == OP_CALL || opcode == OP_CALL_K || opcode == OP_TAIL || opcode == OP_TAIL_K)
{

View file

@ -578,8 +578,10 @@ static int print_reg(FILE *out, int col, int arg, int mode, int immshift, const
return col+printf_wrapper(out, "s%d", regnum);
case REGT_POINTER:
return col+printf_wrapper(out, "a%d", regnum);
case REGT_FLOAT | REGT_MULTIREG:
return col+printf_wrapper(out, "v%d", regnum);
case REGT_FLOAT | REGT_MULTIREG2:
return col+printf_wrapper(out, "v%d.2", regnum);
case REGT_FLOAT | REGT_MULTIREG3:
return col+printf_wrapper(out, "v%d.3", regnum);
case REGT_INT | REGT_KONST:
return col+print_reg(out, 0, regnum, MODE_KI, 0, func);
case REGT_FLOAT | REGT_KONST:

View file

@ -453,7 +453,7 @@ begin:
}
else
{
switch(b & (REGT_TYPE | REGT_KONST | REGT_ADDROF))
switch(b)
{
case REGT_INT:
assert(C < f->NumRegD);
@ -492,40 +492,31 @@ begin:
::new(param) VMValue(konsta[C].v, konstatag[C]);
break;
case REGT_FLOAT:
if (b & REGT_MULTIREG)
{
assert(C < f->NumRegF - 2);
assert(f->NumParam < sfunc->MaxParam - 1);
::new(param) VMValue(reg.f[C]);
::new(param+1) VMValue(reg.f[C+1]);
::new(param+2) VMValue(reg.f[C+2]);
f->NumParam += 2;
}
else
{
assert(C < f->NumRegF);
::new(param) VMValue(reg.f[C]);
}
assert(C < f->NumRegF);
::new(param) VMValue(reg.f[C]);
break;
case REGT_FLOAT | REGT_MULTIREG2:
assert(C < f->NumRegF - 1);
assert(f->NumParam < sfunc->MaxParam);
::new(param) VMValue(reg.f[C]);
::new(param + 1) VMValue(reg.f[C + 1]);
f->NumParam++;
break;
case REGT_FLOAT | REGT_MULTIREG3:
assert(C < f->NumRegF - 2);
assert(f->NumParam < sfunc->MaxParam - 1);
::new(param) VMValue(reg.f[C]);
::new(param + 1) VMValue(reg.f[C + 1]);
::new(param + 2) VMValue(reg.f[C + 2]);
f->NumParam += 2;
break;
case REGT_FLOAT | REGT_ADDROF:
assert(C < f->NumRegF);
::new(param) VMValue(&reg.f[C], ATAG_FREGISTER);
break;
case REGT_FLOAT | REGT_KONST:
if (b & REGT_MULTIREG)
{
assert(C < sfunc->NumKonstF - 2);
assert(f->NumParam < sfunc->MaxParam - 1);
::new(param) VMValue(konstf[C]);
::new(param+1) VMValue(konstf[C+1]);
::new(param+2) VMValue(konstf[C+2]);
f->NumParam += 2;
}
else
{
assert(C < sfunc->NumKonstF);
::new(param) VMValue(konstf[C]);
}
assert(C < sfunc->NumKonstF);
::new(param) VMValue(konstf[C]);
break;
default:
assert(0);

View file

@ -2017,6 +2017,7 @@ void ZCCCompiler::InitFunctions()
SetImplicitArgs(&args, &argflags, &argnames, c->Type(), varflags);
argdefaults.Resize(argnames.Size());
auto p = f->Params;
bool hasoptionals = false;
if (p != nullptr)
{
do
@ -2043,6 +2044,7 @@ void ZCCCompiler::InitFunctions()
else if (p->Default != nullptr)
{
flags |= VARF_Optional;
hasoptionals = true;
// The simplifier is not suited to convert the constant into something usable.
// All it does is reduce the expression to a constant but we still got to do proper type checking and conversion.
// It will also lose important type info about enums, once these get implemented
@ -2094,6 +2096,10 @@ void ZCCCompiler::InitFunctions()
}
if (x != nullptr) delete x;
}
else if (hasoptionals)
{
Error(p, "All arguments after the first optional one need also be optional.");
}
// TBD: disallow certain types? For now, let everything pass that isn't an array.
args.Push(type);
argflags.Push(flags);