mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-24 21:11:39 +00:00
- implemented processing of multiple return values in script functions.
This commit is contained in:
parent
232b64d332
commit
ade9e4c3da
4 changed files with 80 additions and 27 deletions
|
@ -9956,37 +9956,76 @@ ExpEmit FxJumpStatement::Emit(VMFunctionBuilder *build)
|
|||
//==========================================================================
|
||||
|
||||
FxReturnStatement::FxReturnStatement(FxExpression *value, const FScriptPosition &pos)
|
||||
: FxExpression(EFX_ReturnStatement, pos), Value(value)
|
||||
: FxExpression(EFX_ReturnStatement, pos)
|
||||
{
|
||||
if (value != nullptr) Args.Push(value);
|
||||
ValueType = TypeVoid;
|
||||
}
|
||||
|
||||
FxReturnStatement::FxReturnStatement(FArgumentList &values, const FScriptPosition &pos)
|
||||
: FxExpression(EFX_ReturnStatement, pos)
|
||||
{
|
||||
Args = std::move(values);
|
||||
ValueType = TypeVoid;
|
||||
}
|
||||
|
||||
FxReturnStatement::~FxReturnStatement()
|
||||
{
|
||||
SAFE_DELETE(Value);
|
||||
}
|
||||
|
||||
FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx)
|
||||
{
|
||||
bool fail = false;
|
||||
CHECKRESOLVED();
|
||||
SAFE_RESOLVE_OPT(Value, ctx);
|
||||
for (auto &Value : Args)
|
||||
{
|
||||
SAFE_RESOLVE_OPT(Value, ctx);
|
||||
fail |= (Value == nullptr);
|
||||
}
|
||||
if (fail)
|
||||
{
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PPrototype *retproto;
|
||||
if (Value == nullptr)
|
||||
if (Args.Size() == 0)
|
||||
{
|
||||
TArray<PType *> none(0);
|
||||
retproto = NewPrototype(none, none);
|
||||
}
|
||||
else
|
||||
else if (Args.Size() == 1)
|
||||
{
|
||||
// If we already know the real return type we need at least try to cast the value to its proper type (unless in an anonymous function.)
|
||||
if (ctx.ReturnProto != nullptr && ctx.ReturnProto->ReturnTypes.Size() > 0 && ctx.Function->SymbolName != NAME_None)
|
||||
{
|
||||
Value = new FxTypeCast(Value, ctx.ReturnProto->ReturnTypes[0], false, false);
|
||||
Value = Value->Resolve(ctx);
|
||||
ABORT(Value);
|
||||
Args[0] = new FxTypeCast(Args[0], ctx.ReturnProto->ReturnTypes[0], false, false);
|
||||
Args[0] = Args[0]->Resolve(ctx);
|
||||
ABORT(Args[0]);
|
||||
}
|
||||
retproto = Value->ReturnProto();
|
||||
retproto = Args[0]->ReturnProto();
|
||||
}
|
||||
else if (ctx.ReturnProto != nullptr && ctx.ReturnProto->ReturnTypes.Size() == Args.Size())
|
||||
{
|
||||
for (unsigned i = 0; i < Args.Size(); i++)
|
||||
{
|
||||
auto &Value = Args[0];
|
||||
Value = new FxTypeCast(Value, ctx.ReturnProto->ReturnTypes[i], false, false);
|
||||
Value = Value->Resolve(ctx);
|
||||
if (Value == nullptr) fail = true;
|
||||
}
|
||||
if (fail)
|
||||
{
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
return this; // no point calling CheckReturn here.
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Incorrect number of return values. Got %u, but expected %u", Args.Size(), ctx.ReturnProto->ReturnTypes.Size());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ctx.CheckReturn(retproto, ScriptPosition);
|
||||
|
@ -9996,8 +10035,20 @@ FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx)
|
|||
|
||||
ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
TArray<ExpEmit> outs;
|
||||
|
||||
ExpEmit out(0, REGT_NIL);
|
||||
|
||||
// If there's structs to destroy here we need to emit all returns before destroying them.
|
||||
if (build->ConstructedStructs.Size())
|
||||
{
|
||||
for (auto ret : Args)
|
||||
{
|
||||
ExpEmit r = ret->Emit(build);
|
||||
outs.Push(r);
|
||||
}
|
||||
}
|
||||
|
||||
// call the destructors for all structs requiring one.
|
||||
// go in reverse order of construction
|
||||
for (int i = build->ConstructedStructs.Size() - 1; i >= 0; i--)
|
||||
|
@ -10013,19 +10064,19 @@ ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build)
|
|||
|
||||
// If we return nothing, use a regular RET opcode.
|
||||
// Otherwise just return the value we're given.
|
||||
if (Value == nullptr)
|
||||
if (Args.Size() == 0)
|
||||
{
|
||||
build->Emit(OP_RET, RET_FINAL, REGT_NIL, 0);
|
||||
}
|
||||
else
|
||||
else if (Args.Size() == 1)
|
||||
{
|
||||
out = Value->Emit(build);
|
||||
out = outs.Size() > 0? outs[0] : Args[0]->Emit(build);
|
||||
|
||||
// Check if it is a function call that simplified itself
|
||||
// into a tail call in which case we don't emit anything.
|
||||
if (!out.Final)
|
||||
{
|
||||
if (Value->ValueType == TypeVoid)
|
||||
if (Args[0]->ValueType == TypeVoid)
|
||||
{ // Nothing is returned.
|
||||
build->Emit(OP_RET, RET_FINAL, REGT_NIL, 0);
|
||||
}
|
||||
|
@ -10035,6 +10086,14 @@ ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build)
|
|||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned i = 0; i < Args.Size(); i++)
|
||||
{
|
||||
out = outs.Size() > 0 ? outs[i] : Args[i]->Emit(build);
|
||||
build->Emit(OP_RET, i < Args.Size() - 1 ? i : i+RET_FINAL, EncodeRegType(out), out.RegNum);
|
||||
}
|
||||
}
|
||||
|
||||
out.Final = true;
|
||||
return out;
|
||||
|
@ -10042,9 +10101,9 @@ ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build)
|
|||
|
||||
VMFunction *FxReturnStatement::GetDirectFunction()
|
||||
{
|
||||
if (Value != nullptr)
|
||||
if (Args.Size() == 1)
|
||||
{
|
||||
return Value->GetDirectFunction();
|
||||
return Args[0]->GetDirectFunction();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -1911,10 +1911,11 @@ public:
|
|||
|
||||
class FxReturnStatement : public FxExpression
|
||||
{
|
||||
FxExpression *Value;
|
||||
FArgumentList Args;
|
||||
|
||||
public:
|
||||
FxReturnStatement(FxExpression *value, const FScriptPosition &pos);
|
||||
FxReturnStatement(FArgumentList &args, const FScriptPosition &pos);
|
||||
~FxReturnStatement();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
|
|
|
@ -3314,16 +3314,9 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
|
|||
{
|
||||
return new FxReturnStatement(nullptr, *ast);
|
||||
}
|
||||
else if (args.Size() == 1)
|
||||
{
|
||||
auto arg = args[0];
|
||||
args[0] = nullptr;
|
||||
return new FxReturnStatement(arg, *ast);
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(ast, "Return with multiple values not implemented yet.");
|
||||
return new FxReturnStatement(nullptr, *ast);
|
||||
return new FxReturnStatement(args, *ast);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ class PlayerPawn : Actor native
|
|||
}
|
||||
|
||||
// This is for SBARINFO.
|
||||
int/*, int*/ GetEffectTicsForItem(class<Inventory> item)
|
||||
int, int GetEffectTicsForItem(class<Inventory> item)
|
||||
{
|
||||
let pg = (class<PowerupGiver>)(item);
|
||||
if (pg != null)
|
||||
|
@ -127,10 +127,10 @@ class PlayerPawn : Actor native
|
|||
{
|
||||
let maxtics = GetDefaultByType(powerupType).EffectTics;
|
||||
if (maxtics == 0) maxtics = powerup.default.EffectTics;
|
||||
return powerup.EffectTics/*, maxtics*/;
|
||||
return powerup.EffectTics, maxtics;
|
||||
}
|
||||
}
|
||||
return 0/*, 0*/;
|
||||
return 0, 0;
|
||||
}
|
||||
|
||||
native int GetMaxHealth();
|
||||
|
|
Loading…
Reference in a new issue