- actually evaluate the default parameters and store them in the VMFunction.

- disabled the assert in PType::GetRegType. This assert blocks any use to check for types that are incompatible with function parameters.
- pass the default parameter constants to the native functions. At the moment this is not used yet.
- use the function defaults to complete argument lists to script functions.
- fixed all default values that got flagged by the expression evaluator as non-constant. Most were state labels and colors which were defaulted to "". The proper value is null for states and 0 for colors.
- also replaced all "" defaults for names with "none".
This commit is contained in:
Christoph Oelckers 2016-10-27 01:30:34 +02:00
parent d32d52c0b9
commit 66b1f36e56
12 changed files with 152 additions and 62 deletions

View file

@ -96,7 +96,7 @@ FCompileContext::FCompileContext(PFunction *fnc, PPrototype *ret, bool fromdecor
if (fnc != nullptr) Class = fnc->OwningClass;
}
FCompileContext::FCompileContext(PClass *cls) : ReturnProto(nullptr), Function(nullptr), Class(cls), FromDecorate(true) // only used by DECORATE constants.
FCompileContext::FCompileContext(PClass *cls, bool fromdecorate) : ReturnProto(nullptr), Function(nullptr), Class(cls), FromDecorate(fromdecorate)
{
}
@ -3745,7 +3745,7 @@ FxExpression *FxRandom::Resolve(FCompileContext &ctx)
//
//==========================================================================
int DecoRandom(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret)
int DecoRandom(VMFrameStack *stack, VMValue *param, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret)
{
assert(numparam >= 1 && numparam <= 3);
FRandom *rng = reinterpret_cast<FRandom *>(param[0].a);
@ -3995,7 +3995,7 @@ FxFRandom::FxFRandom(FRandom *r, FxExpression *mi, FxExpression *ma, const FScri
//
//==========================================================================
int DecoFRandom(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret)
int DecoFRandom(VMFrameStack *stack, VMValue *param, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret)
{
assert(numparam == 1 || numparam == 3);
FRandom *rng = reinterpret_cast<FRandom *>(param[0].a);
@ -5255,7 +5255,7 @@ FxExpression *FxActionSpecialCall::Resolve(FCompileContext& ctx)
//
//==========================================================================
int DecoCallLineSpecial(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret)
int DecoCallLineSpecial(VMFrameStack *stack, VMValue *param, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret)
{
assert(numparam > 2 && numparam < 8);
assert(param[0].Type == REGT_INT);
@ -6702,7 +6702,7 @@ FxExpression *FxClassTypeCast::Resolve(FCompileContext &ctx)
//
//==========================================================================
int DecoNameToClass(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret)
int DecoNameToClass(VMFrameStack *stack, VMValue *param, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret)
{
assert(numparam == 2);
assert(numret == 1);
@ -6842,7 +6842,7 @@ static bool VerifyJumpTarget(AActor *stateowner, FStateParamInfo *stateinfo, int
return false;
}
static int DecoHandleRuntimeState(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret)
static int DecoHandleRuntimeState(VMFrameStack *stack, VMValue *param, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret)
{
PARAM_PROLOGUE;
PARAM_OBJECT(stateowner, AActor);
@ -7008,7 +7008,7 @@ static int DoFindState(VMFrameStack *stack, VMValue *param, int numparam, VMRetu
}
// Find a state with any number of dots in its name.
int BuiltinFindMultiNameState(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret)
int BuiltinFindMultiNameState(VMFrameStack *stack, VMValue *param, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret)
{
assert(numparam > 1);
assert(numret == 1);
@ -7024,7 +7024,7 @@ int BuiltinFindMultiNameState(VMFrameStack *stack, VMValue *param, int numparam,
}
// Find a state without any dots in its name.
int BuiltinFindSingleNameState(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret)
int BuiltinFindSingleNameState(VMFrameStack *stack, VMValue *param, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret)
{
assert(numparam == 2);
assert(numret == 1);

View file

@ -77,7 +77,7 @@ struct FCompileContext
TDeletingArray<FxLocalVariableDeclaration *> FunctionArgs;
FCompileContext(PFunction *func, PPrototype *ret, bool fromdecorate);
FCompileContext(PClass *cls); // only to be used to resolve constants!
FCompileContext(PClass *cls, bool fromdecorate); // only to be used to resolve constants!
PSymbol *FindInClass(FName identifier, PSymbolTable *&symt);
PSymbol *FindInSelfClass(FName identifier, PSymbolTable *&symt);

View file

@ -87,7 +87,7 @@ FxExpression *ParseExpression (FScanner &sc, PClassActor *cls, bool mustresolve)
if (mustresolve)
{
FCompileContext ctx(cls);
FCompileContext ctx(cls, true);
data = data->Resolve(ctx);
}

View file

@ -667,8 +667,8 @@ void InitThingdef()
symt.AddSymbol(new PField(NAME_MomX, TypeFloat64, VARF_Native|VARF_ReadOnly|VARF_Deprecated, myoffsetof(AActor, Vel.X)));
symt.AddSymbol(new PField(NAME_MomY, TypeFloat64, VARF_Native|VARF_ReadOnly|VARF_Deprecated, myoffsetof(AActor, Vel.Y)));
symt.AddSymbol(new PField(NAME_MomZ, TypeFloat64, VARF_Native|VARF_ReadOnly|VARF_Deprecated, myoffsetof(AActor, Vel.Z)));
symt.AddSymbol(new PField(NAME_ScaleX, TypeFloat64, VARF_Native|VARF_ReadOnly|VARF_Deprecated, myoffsetof(AActor, Scale.X)));
symt.AddSymbol(new PField(NAME_ScaleY, TypeFloat64, VARF_Native|VARF_ReadOnly|VARF_Deprecated, myoffsetof(AActor, Scale.Y)));
symt.AddSymbol(new PField(NAME_ScaleX, TypeFloat64, VARF_Native|VARF_Deprecated, myoffsetof(AActor, Scale.X)));
symt.AddSymbol(new PField(NAME_ScaleY, TypeFloat64, VARF_Native|VARF_Deprecated, myoffsetof(AActor, Scale.Y)));
symt.AddSymbol(new PField(NAME_Score, TypeSInt32, VARF_Native, myoffsetof(AActor, Score)));
symt.AddSymbol(new PField(NAME_Accuracy, TypeSInt32, VARF_Native, myoffsetof(AActor, accuracy)));
symt.AddSymbol(new PField(NAME_Stamina, TypeSInt32, VARF_Native, myoffsetof(AActor, stamina)));

View file

@ -173,20 +173,6 @@ enum EVMAbortException
X_BAD_SELF,
};
class VMFunction : public DObject
{
DECLARE_ABSTRACT_CLASS(VMFunction, DObject);
HAS_OBJECT_POINTERS;
public:
bool Native;
BYTE ImplicitArgs = 0; // either 0 for static, 1 for method or 3 for action
FName Name;
class PPrototype *Proto;
VMFunction(FName name = NAME_None) : Native(false), ImplicitArgs(0), Name(name), Proto(NULL) {}
};
enum EVMOpMode
{
MODE_ASHIFT = 0,
@ -642,6 +628,21 @@ do_double: if (inexact)
}
};
class VMFunction : public DObject
{
DECLARE_ABSTRACT_CLASS(VMFunction, DObject);
HAS_OBJECT_POINTERS;
public:
bool Native;
BYTE ImplicitArgs = 0; // either 0 for static, 1 for method or 3 for action
FName Name;
TArray<VMValue> Defaults;
class PPrototype *Proto;
VMFunction(FName name = NAME_None) : Native(false), ImplicitArgs(0), Name(name), Proto(NULL) {}
};
// VM frame layout:
// VMFrame header
// parameter stack - 16 byte boundary, 16 bytes each
@ -829,7 +830,7 @@ class VMNativeFunction : public VMFunction
{
DECLARE_CLASS(VMNativeFunction, VMFunction);
public:
typedef int (*NativeCallType)(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret);
typedef int (*NativeCallType)(VMFrameStack *stack, VMValue *param, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret);
VMNativeFunction() : NativeCall(NULL) { Native = true; }
VMNativeFunction(NativeCallType call) : NativeCall(call) { Native = true; }
@ -900,8 +901,8 @@ void VMDumpConstants(FILE *out, const VMScriptFunction *func);
void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction *func);
// Use this in the prototype for a native function.
#define VM_ARGS VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret
#define VM_ARGS_NAMES stack, param, numparam, ret, numret
#define VM_ARGS VMFrameStack *stack, VMValue *param, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret
#define VM_ARGS_NAMES stack, param, defaultparam, numparam, ret, numret
// Use these to collect the parameters in a native function.
// variable name <x> at position <p>
@ -924,6 +925,16 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction
// PARAM_INT_OPT(0,myint) { myint = 55; }
// Just make sure to fill it in when using these macros, because the compiler isn't likely
// to give useful error messages if you don't.
#define PARAM_EXISTS ((p) < numparam && param[p].Type != REGT_NIL)
#define ASSERTINT(p) assert((p).Type == REGT_INT)
#define ASSERTFLOAT(p) assert((p).Type == REGT_FLOAT)
#define ASSERTSTRING(p) assert((p).Type == REGT_STRING)
#define ASSERTOBJECT(p) assert((p).Type == REGT_POINTER && (p).atag == ATAG_OBJECT)
#define ASSERTPOINTER(p) assert((p).Type == REGT_POINTER && (p).atag == ATAG_GENERIC)
#define ASSERTSTATE(p) assert((p).Type == REGT_POINTER && ((p).atag == ATAG_GENERIC || (p).atag == ATAG_STATE))
#define PARAM_INT_DEF_AT(p,x) int x; if (PARAM_EXISTS) { ASSERTINT(param[p]); x = param[p].i; } else { ASSERTINT(defaultparam[p]); x = defaultparam[p].i; }
#define PARAM_INT_OPT_AT(p,x) int x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_INT); x = param[p].i; } else
#define PARAM_BOOL_OPT_AT(p,x) bool x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_INT); x = !!param[p].i; } else
#define PARAM_NAME_OPT_AT(p,x) FName x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_INT); x = ENamedName(param[p].i); } else
@ -954,6 +965,20 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction
#define PARAM_OBJECT(x,type) ++paramnum; PARAM_OBJECT_AT(paramnum,x,type)
#define PARAM_CLASS(x,base) ++paramnum; PARAM_CLASS_AT(paramnum,x,base)
#define PARAM_INT_DEF(x) ++paramnum; PARAM_INT_DEF_AT(paramnum,x)
#define PARAM_BOOL_DEF(x) ++paramnum; PARAM_BOOL_DEF_AT(paramnum,x)
#define PARAM_NAME_DEF(x) ++paramnum; PARAM_NAME_DEF_AT(paramnum,x)
#define PARAM_SOUND_DEF(x) ++paramnum; PARAM_SOUND_DEF_AT(paramnum,x)
#define PARAM_COLOR_DEF(x) ++paramnum; PARAM_COLOR_DEF_AT(paramnum,x)
#define PARAM_FLOAT_DEF(x) ++paramnum; PARAM_FLOAT_DEF_AT(paramnum,x)
#define PARAM_ANGLE_DEF(x) ++paramnum; PARAM_ANGLE_DEF_AT(paramnum,x)
#define PARAM_STRING_DEF(x) ++paramnum; PARAM_STRING_DEF_AT(paramnum,x)
#define PARAM_STATE_DEF(x) ++paramnum; PARAM_STATE_DEF_AT(paramnum,x)
#define PARAM_STATEINFO_DEF(x) ++paramnum; PARAM_STATEINFO_DEF_AT(paramnum,x)
#define PARAM_POINTER_DEF(x,type) ++paramnum; PARAM_POINTER_DEF_AT(paramnum,x,type)
#define PARAM_OBJECT_DEF(x,type) ++paramnum; PARAM_OBJECT_DEF_AT(paramnum,x,type)
#define PARAM_CLASS_DEF(x,base) ++paramnum; PARAM_CLASS_DEF_AT(paramnum,x,base)
#define PARAM_INT_OPT(x) ++paramnum; PARAM_INT_OPT_AT(paramnum,x)
#define PARAM_BOOL_OPT(x) ++paramnum; PARAM_BOOL_OPT_AT(paramnum,x)
#define PARAM_NAME_OPT(x) ++paramnum; PARAM_NAME_OPT_AT(paramnum,x)
@ -968,7 +993,7 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction
#define PARAM_OBJECT_OPT(x,type) ++paramnum; PARAM_OBJECT_OPT_AT(paramnum,x,type)
#define PARAM_CLASS_OPT(x,base) ++paramnum; PARAM_CLASS_OPT_AT(paramnum,x,base)
typedef int(*actionf_p)(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret);/*(VM_ARGS)*/
typedef int(*actionf_p)(VMFrameStack *stack, VMValue *param, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret);/*(VM_ARGS)*/
struct AFuncDesc
{

View file

@ -188,13 +188,14 @@ void VMFillParams(VMValue *params, VMFrame *callee, int numparam)
const VMRegisters calleereg(callee);
assert(calleefunc != NULL && !calleefunc->Native);
assert(numparam == calleefunc->NumArgs);
assert(numparam == calleefunc->NumArgs || ((int)calleefunc->Defaults.Size() == numparam));
assert(REGT_INT == 0 && REGT_FLOAT == 1 && REGT_STRING == 2 && REGT_POINTER == 3);
regd = regf = regs = rega = 0;
for (int i = 0; i < numparam; ++i)
for (int i = 0; i < calleefunc->NumArgs; ++i)
{
VMValue &p = params[i];
// get all actual parameters and fill the rest from the defaults.
VMValue &p = i < numparam? params[i] : calleefunc->Defaults[i];
if (p.Type < REGT_STRING)
{
if (p.Type == REGT_INT)

View file

@ -506,7 +506,7 @@ begin:
FillReturns(reg, f, returns, pc+1, C);
if (call->Native)
{
numret = static_cast<VMNativeFunction *>(call)->NativeCall(stack, reg.param + f->NumParam - B, B, returns, C);
numret = static_cast<VMNativeFunction *>(call)->NativeCall(stack, reg.param + f->NumParam - B, call->Defaults, B, returns, C);
}
else
{
@ -550,7 +550,7 @@ begin:
if (call->Native)
{
return static_cast<VMNativeFunction *>(call)->NativeCall(stack, reg.param + f->NumParam - B, B, ret, numret);
return static_cast<VMNativeFunction *>(call)->NativeCall(stack, reg.param + f->NumParam - B, call->Defaults, B, ret, numret);
}
else
{ // FIXME: Not a true tail call

View file

@ -415,7 +415,7 @@ int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMRetur
{
if (func->Native)
{
return static_cast<VMNativeFunction *>(func)->NativeCall(this, params, numparams, results, numresults);
return static_cast<VMNativeFunction *>(func)->NativeCall(this, params, func->Defaults, numparams, results, numresults);
}
else
{

View file

@ -1952,6 +1952,7 @@ void ZCCCompiler::InitFunctions()
TArray<PType *> rets(1);
TArray<PType *> args;
TArray<uint32_t> argflags;
TArray<VMValue> argdefaults;
TArray<FName> argnames;
for (auto c : Classes)
@ -1961,6 +1962,7 @@ void ZCCCompiler::InitFunctions()
rets.Clear();
args.Clear();
argflags.Clear();
bool hasdefault = false;
// For the time being, let's not allow overloading. This may be reconsidered later but really just adds an unnecessary amount of complexity here.
if (AddTreeNode(f->Name, f, &c->TreeNodes, false))
{
@ -2018,36 +2020,96 @@ void ZCCCompiler::InitFunctions()
}
}
SetImplicitArgs(&args, &argflags, &argnames, c->Type(), varflags);
argdefaults.Resize(argnames.Size());
auto p = f->Params;
if (p != nullptr)
{
do
{
VMValue vmval; // default is REGT_NIL which means 'no default value' here.
if (p->Type != nullptr)
{
auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false);
int flags = 0;
if (p->Flags & ZCC_In) flags |= VARF_In;
if (p->Flags & ZCC_Out) flags |= VARF_Out;
if (p->Default != nullptr)
if ((type->IsA(RUNTIME_CLASS(PStruct))) || (flags & VARF_Out))
{
auto val = Simplify(p->Default, &c->Type()->Symbols, true);
flags |= VARF_Optional;
if (val->Operation != PEX_ConstValue)
// 'out' parameters and all structs except vectors are passed by reference
if ((flags & VARF_Out) || (type != TypeVector2 && type != TypeVector3))
{
Error(c->cls, "Default parameter %s is not constant in %s", FName(p->Name).GetChars(), FName(f->Name).GetChars());
type = NewPointer(type);
}
// Todo: Store and handle the default value (native functions will discard it anyway but for scripted ones this should be done decently.)
}
if (type->GetRegType() == REGT_NIL && type != TypeVector2 && type != TypeVector3)
{
Error(p, "Invalid type %s for function parameter", type->DescriptiveName());
}
else if (p->Default != nullptr)
{
flags |= VARF_Optional;
// 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
// The code generator can do this properly for us.
FxExpression *x = new FxTypeCast(ConvertNode(p->Default), type, false);
FCompileContext ctx(c->Type(), false);
x = x->Resolve(ctx);
if (x != nullptr)
{
if (!x->isConstant())
{
Error(p, "Default parameter %s is not constant in %s", FName(p->Name).GetChars(), FName(f->Name).GetChars());
}
else if (x->ValueType != type)
{
Error(p, "Default parameter %s could not be converted to target type %s", FName(p->Name).GetChars(), c->Type()->TypeName.GetChars());
}
else
{
auto cnst = static_cast<FxConstant *>(x);
hasdefault = true;
switch (type->GetRegType())
{
case REGT_INT:
vmval = cnst->GetValue().GetInt();
break;
case REGT_FLOAT:
vmval = cnst->GetValue().GetFloat();
break;
case REGT_POINTER:
if (type->IsKindOf(RUNTIME_CLASS(PClassPointer)))
vmval = (DObject*)cnst->GetValue().GetPointer();
else
vmval = cnst->GetValue().GetPointer();
break;
case REGT_STRING:
vmval = cnst->GetValue().GetString();
break;
default:
assert(0 && "no valid type for constant");
break;
}
}
}
if (x != nullptr) delete x;
}
// TBD: disallow certain types? For now, let everything pass that isn't an array.
args.Push(type);
argflags.Push(flags);
}
else
{
args.Push(nullptr);
argflags.Push(0);
}
argdefaults.Push(vmval);
p = static_cast<decltype(p)>(p->SiblingNext);
} while (p != f->Params);
}
@ -2064,9 +2126,11 @@ void ZCCCompiler::InitFunctions()
sym->Variants[0].Implementation = FunctionBuildList.AddFunction(sym, code, FStringf("%s.%s", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()), false);
}
}
if (sym->Variants[0].Implementation != nullptr && hasdefault) // do not copy empty default lists, they only waste space and processing time.
{
sym->Variants[0].Implementation->Defaults = std::move(argdefaults);
}
// todo: Check inheritance.
// todo: Process function bodies.
}
}
}