mirror of
https://github.com/ZDoom/gzdoom.git
synced 2025-06-04 19:10:59 +00:00
- scriptified Heretic's blaster.
- scriptified all Effect functions of Fastprojectile's children - implemented access to class meta data. - added a VM instruction to retrieve the class metadata, to eliminate the overhead of the function call that would otherwise be needed. - made GetClass() a builtin so that it can use the new instruction Important note about this commit: Scriptifying CFlameMissile::Effect revealed a problem with the virtual function interface: In order to work, this needs to be explicitly enabled for each single native class that may be used as a base for a scripted class. Needless to say, this will end up way too much work, as there are over 100 native classes, excluding those which will be scriptified. But in order to fix the problem this partially broken state needs to be committed first.
This commit is contained in:
parent
3f5bf88d69
commit
9ae272d753
23 changed files with 499 additions and 430 deletions
|
@ -5637,12 +5637,6 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classct
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (vsym->Flags & VARF_Static)
|
||||
{
|
||||
// todo. For now these cannot be defined so let's just exit.
|
||||
ScriptPosition.Message(MSG_ERROR, "Static members not implemented yet.");
|
||||
return nullptr;
|
||||
}
|
||||
auto x = isclass ? new FxClassMember(object, vsym, ScriptPosition) : new FxStructMember(object, vsym, ScriptPosition);
|
||||
object = nullptr;
|
||||
return x->Resolve(ctx);
|
||||
|
@ -5918,40 +5912,14 @@ FxExpression *FxClassDefaults::Resolve(FCompileContext& ctx)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
int BuiltinGetDefault(VMFrameStack *stack, VMValue *param, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret)
|
||||
{
|
||||
assert(numparam == 1);
|
||||
PARAM_POINTER_AT(0, obj, DObject);
|
||||
ACTION_RETURN_OBJECT(obj->GetClass()->Defaults);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
ExpEmit FxClassDefaults::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
EmitParameter(build, obj, ScriptPosition);
|
||||
PSymbol *sym = FindBuiltinFunction(NAME_BuiltinGetDefault, BuiltinGetDefault);
|
||||
|
||||
assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction)));
|
||||
assert(((PSymbolVMFunction *)sym)->Function != nullptr);
|
||||
auto callfunc = ((PSymbolVMFunction *)sym)->Function;
|
||||
int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K);
|
||||
build->Emit(opcode, build->GetConstantAddress(callfunc, ATAG_OBJECT), 1, 1);
|
||||
|
||||
if (EmitTail)
|
||||
{
|
||||
ExpEmit call;
|
||||
call.Final = true;
|
||||
return call;
|
||||
}
|
||||
|
||||
ExpEmit out(build, REGT_POINTER);
|
||||
build->Emit(OP_RESULT, 0, REGT_POINTER, out.RegNum);
|
||||
return out;
|
||||
ExpEmit ob = obj->Emit(build);
|
||||
ob.Free(build);
|
||||
ExpEmit meta(build, REGT_POINTER);
|
||||
build->Emit(OP_META, meta.RegNum, ob.RegNum);
|
||||
build->Emit(OP_LO, meta.RegNum, meta.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults)));
|
||||
return meta;
|
||||
|
||||
}
|
||||
|
||||
|
@ -6272,6 +6240,11 @@ FxStructMember::~FxStructMember()
|
|||
|
||||
bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable)
|
||||
{
|
||||
// Cannot take the address of metadata variables.
|
||||
if (membervar->Flags & VARF_Static)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
AddressRequested = true;
|
||||
if (writable != nullptr) *writable = (AddressWritable && !ctx.CheckReadOnly(membervar->Flags) &&
|
||||
(!classx->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) || !static_cast<PPointer*>(classx->ValueType)->IsConst));
|
||||
|
@ -6411,6 +6384,14 @@ ExpEmit FxStructMember::Emit(VMFunctionBuilder *build)
|
|||
obj = newobj;
|
||||
}
|
||||
|
||||
if (membervar->Flags & VARF_Static)
|
||||
{
|
||||
obj.Free(build);
|
||||
ExpEmit meta(build, REGT_POINTER);
|
||||
build->Emit(OP_META, meta.RegNum, obj.RegNum);
|
||||
obj = meta;
|
||||
}
|
||||
|
||||
if (AddressRequested)
|
||||
{
|
||||
if (membervar->Offset == 0)
|
||||
|
@ -6953,6 +6934,13 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
|
|||
}
|
||||
break;
|
||||
|
||||
case NAME_GetClass:
|
||||
if (CheckArgSize(NAME_GetClass, ArgList, 0, 0, ScriptPosition))
|
||||
{
|
||||
func = new FxGetClass(new FxSelf(ScriptPosition));
|
||||
}
|
||||
break;
|
||||
|
||||
case NAME_Random:
|
||||
// allow calling Random without arguments to default to (0, 255)
|
||||
if (ArgList.Size() == 0)
|
||||
|
@ -7132,6 +7120,12 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
// handle builtins: Vectors got 2: Length and Unit.
|
||||
if (MethodName == NAME_Length || MethodName == NAME_Unit)
|
||||
{
|
||||
if (ArgList.Size() > 0)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
auto x = new FxVectorBuiltin(Self, MethodName);
|
||||
Self = nullptr;
|
||||
delete this;
|
||||
|
@ -7144,6 +7138,17 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
auto ptype = static_cast<PPointer *>(Self->ValueType)->PointedType;
|
||||
if (ptype->IsKindOf(RUNTIME_CLASS(PStruct)))
|
||||
{
|
||||
if (ptype->IsKindOf(RUNTIME_CLASS(PClass)) && MethodName == NAME_GetClass)
|
||||
{
|
||||
if (ArgList.Size() > 0)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
auto x = new FxGetClass(Self);
|
||||
return x->Resolve(ctx);
|
||||
}
|
||||
cls = static_cast<PStruct *>(ptype);
|
||||
}
|
||||
else
|
||||
|
@ -7976,6 +7981,44 @@ ExpEmit FxVectorBuiltin::Emit(VMFunctionBuilder *build)
|
|||
return to;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxGetClass::FxGetClass(FxExpression *self)
|
||||
:FxExpression(EFX_GetClass, self->ScriptPosition)
|
||||
{
|
||||
Self = self;
|
||||
}
|
||||
|
||||
FxGetClass::~FxGetClass()
|
||||
{
|
||||
SAFE_DELETE(Self);
|
||||
}
|
||||
|
||||
FxExpression *FxGetClass::Resolve(FCompileContext &ctx)
|
||||
{
|
||||
SAFE_RESOLVE(Self, ctx);
|
||||
if (!Self->IsObject())
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "GetClass() requires an object");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
ValueType = NewClassPointer(static_cast<PClass*>(static_cast<PPointer*>(Self->ValueType)->PointedType));
|
||||
return this;
|
||||
}
|
||||
|
||||
ExpEmit FxGetClass::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
ExpEmit op = Self->Emit(build);
|
||||
op.Free(build);
|
||||
ExpEmit to(build, REGT_POINTER);
|
||||
build->Emit(OP_META, to.RegNum, op.RegNum);
|
||||
return to;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxSequence :: Resolve
|
||||
|
|
|
@ -286,6 +286,7 @@ enum EFxType
|
|||
EFX_StaticArrayVariable,
|
||||
EFX_CVar,
|
||||
EFX_NamedNode,
|
||||
EFX_GetClass,
|
||||
EFX_COUNT
|
||||
};
|
||||
|
||||
|
@ -320,6 +321,7 @@ public:
|
|||
bool IsPointer() const { return ValueType->GetRegType() == REGT_POINTER; }
|
||||
bool IsVector() const { return ValueType == TypeVector2 || ValueType == TypeVector3; };
|
||||
bool IsBoolCompat() const { return ValueType->GetRegCount() == 1 && (ValueType->GetRegType() == REGT_INT || ValueType->GetRegType() == REGT_FLOAT || ValueType->GetRegType() == REGT_POINTER); }
|
||||
bool IsObject() const { return ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && !ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)) && ValueType != TypeNullPtr && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PClass)); }
|
||||
|
||||
virtual ExpEmit Emit(VMFunctionBuilder *build);
|
||||
|
||||
|
@ -1520,6 +1522,24 @@ public:
|
|||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxFlopFunctionCall
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FxGetClass : public FxExpression
|
||||
{
|
||||
FxExpression *Self;
|
||||
|
||||
public:
|
||||
|
||||
FxGetClass(FxExpression *self);
|
||||
~FxGetClass();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxVMFunctionCall
|
||||
|
|
|
@ -75,6 +75,7 @@
|
|||
#define ASSERTF(x) assert((unsigned)(x) < f->NumRegF)
|
||||
#define ASSERTA(x) assert((unsigned)(x) < f->NumRegA)
|
||||
#define ASSERTS(x) assert((unsigned)(x) < f->NumRegS)
|
||||
#define ASSERTO(x) assert((unsigned)(x) < f->NumRegA && reg.atag[x] == ATAG_OBJECT)
|
||||
|
||||
#define ASSERTKD(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstD)
|
||||
#define ASSERTKF(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstF)
|
||||
|
|
|
@ -110,6 +110,12 @@ begin:
|
|||
reg.atag[a] = ATAG_GENERIC; // using ATAG_FRAMEPOINTER will cause endless asserts.
|
||||
NEXTOP;
|
||||
|
||||
OP(META):
|
||||
ASSERTA(a); ASSERTO(B);
|
||||
reg.a[a] = ((DObject*)reg.a[B])->GetClass(); // I wish this could be done without a special opcode but there's really no good way to guarantee initialization of the Class pointer...
|
||||
reg.atag[a] = ATAG_OBJECT;
|
||||
NEXTOP;
|
||||
|
||||
OP(LB):
|
||||
ASSERTD(a); ASSERTA(B); ASSERTKD(C);
|
||||
GETADDR(PB,KC,X_READ_NIL);
|
||||
|
|
|
@ -23,6 +23,7 @@ xx(LKF_R, lk, RFRII8, NOP, 0, 0), // load float constant indexed
|
|||
xx(LKS_R, lk, RSRII8, NOP, 0, 0), // load string constant indexed
|
||||
xx(LKP_R, lk, RPRII8, NOP, 0, 0), // load pointer constant indexed
|
||||
xx(LFP, lf, LFP, NOP, 0, 0), // load frame pointer
|
||||
xx(META, meta, RPRP, NOP, 0, 0), // load a class's meta class address
|
||||
|
||||
// Load from memory. rA = *(rB + rkC)
|
||||
xx(LB, lb, RIRPKI, LB_R, 4, REGT_INT), // load byte
|
||||
|
|
|
@ -1298,8 +1298,12 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
|
|||
|
||||
if (field->Flags & ZCC_Meta)
|
||||
{
|
||||
varflags |= VARF_ReadOnly; // metadata implies readonly
|
||||
// todo: this needs to go into the metaclass and needs some handling
|
||||
varflags |= VARF_Static|VARF_ReadOnly; // metadata implies readonly
|
||||
if (!(field->Flags & ZCC_Native))
|
||||
{
|
||||
// Non-native meta data is not implemented yet and requires some groundwork in the class copy code.
|
||||
Error(field, "Metadata member %s must be native", FName(field->Names->Name).GetChars());
|
||||
}
|
||||
}
|
||||
|
||||
if (field->Type->ArraySize != nullptr)
|
||||
|
@ -1320,7 +1324,8 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
|
|||
|
||||
if (varflags & VARF_Native)
|
||||
{
|
||||
fd = FindField(type, FName(name->Name).GetChars());
|
||||
auto querytype = (varflags & VARF_Static) ? type->GetClass() : type;
|
||||
fd = FindField(querytype, FName(name->Name).GetChars());
|
||||
if (fd == nullptr)
|
||||
{
|
||||
Error(field, "The member variable '%s.%s' has not been exported from the executable.", type->TypeName.GetChars(), FName(name->Name).GetChars());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue