Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Content.Tests/DMProject/Tests/Builtins/caller.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#define COMPARE(a, b) if(a != b) {CRASH("Assertion failed: expected [b] got [a]")}

/datum/meep/proc/bar()
COMPARE("[caller.caller.caller.name]", nameof(/proc/RunTest))

/datum/proc/foo()
var/datum/meep/M = new
COMPARE("[caller.name]", nameof(/proc/ihateithere))
M.bar()

/proc/ihateithere()
var/datum/D = new
COMPARE("[caller.name]", nameof(/proc/RunTest))
D.foo()

/proc/RunTest()
// RunTest()'s caller is null due to how unit tests are invoked
ihateithere()
2 changes: 1 addition & 1 deletion DMCompiler/DMStandard/Types/Callee.dm
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/callee
var/proc
var/args
var/caller as /callee|null
var/callee/caller as /callee|null

var/name as text|null
var/desc as text|null
Expand Down
3 changes: 3 additions & 0 deletions OpenDreamRuntime/DreamThread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ public abstract class ProcState : IDisposable {
public DreamObject? Usr;
public int ArgumentCount;
public abstract DreamProc? Proc { get; }
public ProcState? Caller { get; set; }

protected void Initialize(DreamThread thread, bool waitFor) {
Thread = thread;
Expand Down Expand Up @@ -205,6 +206,7 @@ public sealed class DreamThread(string name) {

private ProcState? _current;
private readonly Stack<ProcState> _stack = new();
public int StackDepth => _current is null ? 0 : (_stack.Count + 1); // including _current

// The amount of stack frames containing `WaitFor = false`
private int _syncCount;
Expand Down Expand Up @@ -393,6 +395,7 @@ public void PushProcState(ProcState state) {
_stack.Push(_current);
}

state.Caller = _current;
_current = state;
}

Expand Down
49 changes: 37 additions & 12 deletions OpenDreamRuntime/Objects/Types/DreamObjectCallee.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using DMCompiler.Bytecode;
using System.Diagnostics.CodeAnalysis;
using DMCompiler.Bytecode;
using OpenDreamRuntime.Procs;

namespace OpenDreamRuntime.Objects.Types;
Expand All @@ -7,35 +8,59 @@
public DMProcState? ProcState;
public long ProcStateId; // Used to ensure the proc state hasn't been reused for another proc

[MemberNotNullWhen(false, nameof(ProcState))]
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
public bool Expired => ProcState == null || ProcState.Id != ProcStateId;

protected override bool TryGetVar(string varName, out DreamValue value) {
if (ProcState == null || ProcState.Id != ProcStateId)
// TODO: This ProcState check doesn't match byond behavior?
if (Expired)
throw new Exception("This callee has expired");

switch (varName) {
case "proc":
value = new(ProcState.Proc);
value = ProcState.Proc is not null
? new(ProcState.Proc)
: DreamValue.Null;
return true;
case "args":
value = new(new ProcArgsList(ObjectTree.List.ObjectDefinition, ProcState));
return true;
case "caller":
// TODO
value = DreamValue.Null;
if(ProcState.Caller is DMProcState callerState) {
callerState.CalleeObject.IncRef();
value = new(callerState.CalleeObject);
}
else if(ProcState.Caller?.Caller is DMProcState superCallerState) { // init case
superCallerState.CalleeObject.IncRef();
value = new(superCallerState.CalleeObject);
}
else
value = DreamValue.Null;
return true;
case "name":
value = new(ProcState.Proc.VerbName);
value = ProcState.Proc is not null
? new(ProcState.Proc.VerbName)
: DreamValue.Null;
return true;
case "desc":
value = ProcState.Proc.VerbDesc != null ? new(ProcState.Proc.VerbDesc) : DreamValue.Null;
value = ProcState.Proc is not null
? new(ProcState.Proc.VerbDesc)

Check warning

Code scanning / InspectCode

Possible null reference argument for a parameter. Warning

Possible 'null' reference argument for parameter 'value' in 'OpenDreamRuntime.DreamValue.DreamValue'
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
: DreamValue.Null;
return true;
case "category":
value = ProcState.Proc.VerbCategory != null ? new(ProcState.Proc.VerbCategory) : DreamValue.Null;
value = ProcState.Proc is not null
? new(ProcState.Proc.VerbCategory)

Check warning

Code scanning / InspectCode

Possible null reference argument for a parameter. Warning

Possible 'null' reference argument for parameter 'value' in 'OpenDreamRuntime.DreamValue.DreamValue'
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
: DreamValue.Null;
return true;
case "file":
value = new(ProcState.Proc.GetSourceAtOffset(0).Source);
value = ProcState.Proc is not null
? new DreamValue(ProcState.Proc.GetSourceAtOffset(0).Source)
: DreamValue.Null;
return true;
case "line":
value = new(ProcState.Proc.GetSourceAtOffset(0).Line);
value = ProcState.Proc is not null
? new DreamValue(ProcState.Proc.GetSourceAtOffset(0).Line)
: DreamValue.Null;
return true;
case "src":
ProcState.Instance?.IncRef();
Expand All @@ -61,9 +86,9 @@
}

public override string GetDisplayName(StringFormatEncoder.FormatSuffix? suffix = null) {
if (ProcState == null || ProcState.Id != ProcStateId)
if (Expired)
return string.Empty;

return $"proc<{ProcState.Proc},0>"; // TODO: Call depth
return $"proc<{ProcState.Proc},{ProcState.Id}>"; // ID isn't accurate but does that really matter?
}
}
34 changes: 25 additions & 9 deletions OpenDreamRuntime/Procs/DMProc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,9 @@
public readonly IDreamValueEnumerator?[] Enumerators = new IDreamValueEnumerator?[16];

public int ProgramCounter => _pc;
public override DMProc Proc => _proc;
public override DMProc? Proc => _proc;

Check warning

Code scanning / InspectCode

Return type of a function can be made non-nullable Warning

Return type of 'Proc' can be made non-nullable
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

public DreamObjectCallee CalleeObject { get; set; } = default!;

#if TOOLS
public override (string SourceFile, int Line) TracyLocationId => _proc.GetSourceAtOffset(_pc+1);
Expand Down Expand Up @@ -440,6 +442,13 @@
SetArgument(i, arguments.GetArgument(i));
}

var callee = Proc.ObjectTree.CreateObject<DreamObjectCallee>(Proc.ObjectTree.Callee);

Check warning

Code scanning / InspectCode

Dereference of a possibly null reference. Warning

Dereference of a possibly null reference
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
callee.ProcState = this;
callee.ProcStateId = Id;
callee.IncRef();
CalleeObject = callee;


arguments.Dispose();
}

Expand Down Expand Up @@ -612,6 +621,9 @@
_pc = 0;
_proc = null!;

CalleeObject.DecRef();
CalleeObject = null!;

DreamValuePool.Return(_stack);
_stackIndex = 0;
_stack = null!;
Expand Down Expand Up @@ -932,16 +944,20 @@
DreamManager.WorldInstance.IncRef();
return new(DreamManager.WorldInstance);
case DMReference.Type.Callee: {
// TODO: BYOND seems to reuse the same object. At least, callee == callee
var callee = Proc.ObjectTree.CreateObject<DreamObjectCallee>(Proc.ObjectTree.Callee);

callee.ProcState = this;
callee.ProcStateId = Id;
return new(callee);
// BYOND seems to reuse the same object. At least, callee == callee
CalleeObject.IncRef();
return new(CalleeObject);
}
case DMReference.Type.Caller: {
// TODO
return DreamValue.Null;
// Note that the ref says that caller still returns a "/callee" object, just with the caller's info
if(Caller is not DMProcState dmCaller) {
if(Caller?.Caller is not DMProcState realDmCaller) // init case
return DreamValue.Null;
dmCaller = realDmCaller;
}

dmCaller.CalleeObject.IncRef();
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
return new(dmCaller.CalleeObject);
}
case DMReference.Type.Field: {
var owner = peek ? Peek() : Pop();
Expand Down
Loading