diff --git a/DMCompiler/DM/Builders/DMProcBuilder.cs b/DMCompiler/DM/Builders/DMProcBuilder.cs
index 250ed587e0..ef305dcfe6 100644
--- a/DMCompiler/DM/Builders/DMProcBuilder.cs
+++ b/DMCompiler/DM/Builders/DMProcBuilder.cs
@@ -602,37 +602,30 @@ public void ProcessLoopAssignment(LValue lValue) {
}
}
- public void ProcessStatementForList(DMExpression list, DMExpression outputVar, DMComplexValueType? dmTypes, DMASTProcBlockInner body) {
+ public void ProcessStatementForList(DMExpression list, DMExpression outputVar, DMComplexValueType? typeCheck, DMASTProcBlockInner body) {
if (outputVar is not LValue lValue) {
compiler.Emit(WarningCode.BadExpression, outputVar.Location, "Invalid output var");
- lValue = null;
+ lValue = new BadLValue(outputVar.Location);
}
- // Depending on the var's type and possibly a given "as [types]", an implicit istype() check is performed
- DreamPath? implicitTypeCheck = null;
- if (dmTypes == null) {
- // No "as" means the var's type will be used
- implicitTypeCheck = lValue?.Path;
- } else if (dmTypes.Value.TypePath != null) {
- // "as /datum" will perform a check for /datum
- implicitTypeCheck = dmTypes.Value.TypePath;
- } else if (!dmTypes.Value.IsAnything) {
- // "as anything" performs no check. Other values are unimplemented.
- compiler.UnimplementedWarning(outputVar.Location,
- $"As type {dmTypes} in for loops is unimplemented. No type check will be performed.");
+ // Having no "as [types]" will use the var's type for the type filter
+ if (typeCheck == null && lValue.Path != null) {
+ typeCheck = lValue.Path;
}
+ bool performingImplicitIsType = false;
list.EmitPushValue(ExprContext);
- if (implicitTypeCheck != null) {
- if (compiler.DMObjectTree.TryGetTypeId(implicitTypeCheck.Value, out var filterTypeId)) {
+ if (typeCheck?.TypePath is { } typeCheckPath) { // We have a specific type to filter for
+ if (compiler.DMObjectTree.TryGetTypeId(typeCheckPath, out var filterTypeId)) {
// Create an enumerator that will do the implicit istype() for us
- proc.CreateFilteredListEnumerator(filterTypeId, implicitTypeCheck.Value);
+ proc.CreateFilteredListEnumerator(filterTypeId, typeCheckPath);
} else {
compiler.Emit(WarningCode.ItemDoesntExist, outputVar.Location,
- $"Cannot filter enumeration by type {implicitTypeCheck.Value}, it does not exist");
+ $"Cannot filter enumeration by type {typeCheckPath}, it does not exist");
proc.CreateListEnumerator();
}
- } else {
+ } else { // Either no type filter or we're using the slower "as [types]"
+ performingImplicitIsType = !(typeCheck is null || typeCheck.Value.IsAnything);
proc.CreateListEnumerator();
}
@@ -643,8 +636,43 @@ public void ProcessStatementForList(DMExpression list, DMExpression outputVar, D
{
proc.MarkLoopContinue(loopLabel);
- if (lValue != null) {
- ProcessLoopAssignment(lValue);
+ ProcessLoopAssignment(lValue);
+
+ // "as mob|etc" will insert code equivalent to "if(!(istype(X, mob) || istype(X, etc))) continue;"
+ // It would be ideal if the type filtering could be done by the interpreter, like it does when the var has a type
+ // But the code currently isn't structured in a way that it could be done nicely
+ if (performingImplicitIsType) {
+ var afterTypeCheckIf = proc.NewLabelName();
+ var afterTypeCheckExpr = proc.NewLabelName();
+
+ void CheckType(DMValueType type, DreamPath path, ref bool doOr) {
+ if (!typeCheck!.Value.Type.HasFlag(type))
+ return;
+ if (!compiler.DMObjectTree.TryGetTypeId(path, out var typeId))
+ return;
+
+ if (doOr)
+ proc.BooleanOr(afterTypeCheckExpr);
+ doOr = true;
+
+ lValue.EmitPushValue(ExprContext);
+ proc.PushType(typeId);
+ proc.IsType();
+ }
+
+ bool doOr = false; // Only insert BooleanOr after the first type
+ CheckType(DMValueType.Area, DreamPath.Area, ref doOr);
+ CheckType(DMValueType.Turf, DreamPath.Turf, ref doOr);
+ CheckType(DMValueType.Obj, DreamPath.Obj, ref doOr);
+ CheckType(DMValueType.Mob, DreamPath.Mob, ref doOr);
+ proc.AddLabel(afterTypeCheckExpr);
+ if (doOr) {
+ proc.Not();
+ proc.JumpIfFalse(afterTypeCheckIf);
+ proc.Continue();
+ }
+
+ proc.AddLabel(afterTypeCheckIf);
}
ProcessBlockInner(body);
diff --git a/DMCompiler/DM/DMProc.cs b/DMCompiler/DM/DMProc.cs
index 670442f16b..73600868a1 100644
--- a/DMCompiler/DM/DMProc.cs
+++ b/DMCompiler/DM/DMProc.cs
@@ -579,11 +579,6 @@ public void Pop() {
WriteOpcode(DreamProcOpcode.Pop);
}
- public void PopReference(DMReference reference) {
- WriteOpcode(DreamProcOpcode.PopReference);
- WriteReference(reference, false);
- }
-
public void BooleanOr(string endLabel) {
WriteOpcode(DreamProcOpcode.BooleanOr);
WriteLabel(endLabel);
diff --git a/DMCompiler/DM/Expressions/LValue.cs b/DMCompiler/DM/Expressions/LValue.cs
index d5fd05f468..afae2566f9 100644
--- a/DMCompiler/DM/Expressions/LValue.cs
+++ b/DMCompiler/DM/Expressions/LValue.cs
@@ -31,6 +31,19 @@ public virtual void EmitPushInitial(ExpressionContext ctx) {
}
}
+///
+/// Used when there was an error regarding L-Values
+///
+/// Emit an error code before creating!
+internal sealed class BadLValue(Location location) : LValue(location, null) {
+ public override void EmitPushValue(ExpressionContext ctx) {
+ // It's normal to have this expression exist when there are errors in the code
+ // But in the runtime we say it's a compiler bug because the compiler should never have output it
+ ctx.Proc.PushString("Encountered a bad LValue (compiler bug!)");
+ ctx.Proc.Throw();
+ }
+}
+
// global
internal class Global(Location location) : LValue(location, null) {
public override DMReference EmitReference(ExpressionContext ctx, string endLabel,
diff --git a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs
index 777574d9dc..9e9ef6afaa 100644
--- a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs
+++ b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs
@@ -89,7 +89,7 @@ public static ProcStatus CreateAssociativeList(DMProcState state) {
return ProcStatus.Continue;
}
- private static IDreamValueEnumerator GetContentsEnumerator(DreamObjectTree objectTree, AtomManager atomManager, DreamValue value, TreeEntry? filterType) {
+ private static IDreamValueEnumerator GetContentsEnumerator(AtomManager atomManager, DreamValue value, TreeEntry? filterType) {
if (!value.TryGetValueAsDreamList(out var list)) {
if (value.TryGetValueAsDreamObject(out var dreamObject)) {
if (dreamObject == null)
@@ -121,7 +121,7 @@ private static IDreamValueEnumerator GetContentsEnumerator(DreamObjectTree objec
public static ProcStatus CreateListEnumerator(DMProcState state) {
var enumeratorId = state.ReadInt();
- var enumerator = GetContentsEnumerator(state.Proc.ObjectTree, state.Proc.AtomManager, state.Pop(), null);
+ var enumerator = GetContentsEnumerator(state.Proc.AtomManager, state.Pop(), null);
state.Enumerators[enumeratorId] = enumerator;
return ProcStatus.Continue;
@@ -131,7 +131,7 @@ public static ProcStatus CreateFilteredListEnumerator(DMProcState state) {
var enumeratorId = state.ReadInt();
var filterTypeId = state.ReadInt();
var filterType = state.Proc.ObjectTree.GetTreeEntry(filterTypeId);
- var enumerator = GetContentsEnumerator(state.Proc.ObjectTree, state.Proc.AtomManager, state.Pop(), filterType);
+ var enumerator = GetContentsEnumerator(state.Proc.AtomManager, state.Pop(), filterType);
state.Enumerators[enumeratorId] = enumerator;
return ProcStatus.Continue;
@@ -281,29 +281,21 @@ public static ProcStatus EnumerateNoAssign(DMProcState state) {
///
/// Helper function of to handle text macros that are "suffix" (coming after the noun) pronouns
///
+ ///
+ ///
+ ///
/// This should be in MALE,FEMALE,PLURAL,NEUTER order.
- private static void HandleSuffixPronoun(ref StringBuilder formattedString, ReadOnlySpan interps, int prevInterpIndex, string[] pronouns)
- {
- DreamObject? dreamObject;
+ private static void HandleSuffixPronoun(ref StringBuilder formattedString, ReadOnlySpan interps, int prevInterpIndex, string[] pronouns) {
if (prevInterpIndex == -1 || prevInterpIndex >= interps.Length) // We should probably be throwing here
- {
return;
- }
- interps[prevInterpIndex].TryGetValueAsDreamObject(out dreamObject);
- if (dreamObject == null)
- {
+ if (!interps[prevInterpIndex].TryGetValueAsDreamObject(out var dreamObject))
return;
- }
- bool hasGender = dreamObject.TryGetVariable("gender", out var objectGender); // NOTE: in DM, this has to be a native property.
- if (!hasGender)
- {
+ if (!dreamObject.TryGetVariable("gender", out var objectGender)) // NOTE: in DM, this has to be a native property.
return;
- }
if (!objectGender.TryGetValueAsString(out var genderStr))
return;
- switch(genderStr)
- {
+ switch(genderStr) {
case "male":
formattedString.Append(pronouns[0]);
return;
@@ -319,18 +311,17 @@ private static void HandleSuffixPronoun(ref StringBuilder formattedString, ReadO
default:
return;
}
-
}
private static void ToRoman(ref StringBuilder formattedString, ReadOnlySpan interps, int nextInterpIndex, bool upperCase) {
char[] arr;
if(upperCase) {
- arr = new char[] { 'M', 'D', 'C', 'L', 'X', 'V', 'I' };
+ arr = new[] { 'M', 'D', 'C', 'L', 'X', 'V', 'I' };
} else {
- arr = new char[] { 'm', 'd', 'c', 'l', 'x', 'v', 'i' };
+ arr = new[] { 'm', 'd', 'c', 'l', 'x', 'v', 'i' };
}
- int[] numArr = new int[] { 1000, 500, 100, 50, 10, 5, 1 };
+ int[] numArr = new[] { 1000, 500, 100, 50, 10, 5, 1 };
if(!interps[nextInterpIndex].TryGetValueAsFloat(out float value)) {
return;
@@ -363,6 +354,7 @@ private static void ToRoman(ref StringBuilder formattedString, ReadOnlySpan(out var dreamObject)) {
bool hasName = dreamObject.TryGetVariable("name", out var objectName);
if (!hasName) continue;
string nameStr = objectName.Stringify();
- if (!DreamObject.StringIsProper(nameStr))
- {
+ if (!DreamObject.StringIsProper(nameStr)) {
formattedString.Append(formatType == StringFormatEncoder.FormatSuffix.UpperDefiniteArticle ? "The " : "the ");
}
}
+
continue;
}
case StringFormatEncoder.FormatSuffix.UpperIndefiniteArticle:
@@ -477,40 +467,38 @@ public static ProcStatus FormatString(DMProcState state) {
}
//Suffix macros
case StringFormatEncoder.FormatSuffix.UpperSubjectPronoun:
- HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new string[] { "He", "She", "They", "Tt" });
+ HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new[] { "He", "She", "They", "Tt" });
break;
case StringFormatEncoder.FormatSuffix.LowerSubjectPronoun:
- HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new string[] { "he", "she", "they", "it" });
+ HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new[] { "he", "she", "they", "it" });
break;
case StringFormatEncoder.FormatSuffix.UpperPossessiveAdjective:
- HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new string[] { "His", "Her", "Their", "Its" });
+ HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new[] { "His", "Her", "Their", "Its" });
break;
case StringFormatEncoder.FormatSuffix.LowerPossessiveAdjective:
- HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new string[] { "his", "her", "their", "its" });
+ HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new[] { "his", "her", "their", "its" });
break;
case StringFormatEncoder.FormatSuffix.ObjectPronoun:
- HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new string[] { "him", "her", "them", "it" });
+ HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new[] { "him", "her", "them", "it" });
break;
case StringFormatEncoder.FormatSuffix.ReflexivePronoun:
- HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new string[] { "himself", "herself", "themself", "itself" });
+ HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new[] { "himself", "herself", "themself", "itself" });
break;
case StringFormatEncoder.FormatSuffix.UpperPossessivePronoun:
- HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new string[] { "His", "Hers", "Theirs", "Its" });
+ HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new[] { "His", "Hers", "Theirs", "Its" });
break;
case StringFormatEncoder.FormatSuffix.LowerPossessivePronoun:
- HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new string[] { "his", "hers", "theirs", "its" });
+ HandleSuffixPronoun(ref formattedString, interps, prevInterpIndex, new[] { "his", "hers", "theirs", "its" });
break;
case StringFormatEncoder.FormatSuffix.PluralSuffix:
- if (interps[prevInterpIndex].TryGetValueAsFloat(out var pluralNumber) && pluralNumber == 1)
- {
+ if (interps[prevInterpIndex].TryGetValueAsFloat(out var pluralNumber) && pluralNumber.Equals(1f))
continue;
- }
+
formattedString.Append("s");
continue;
case StringFormatEncoder.FormatSuffix.OrdinalIndicator:
var interp = interps[prevInterpIndex];
if (interp.TryGetValueAsInteger(out var ordinalNumber)) {
-
// For some mystical reason byond converts \th to integers
// This is slightly hacky but the only reliable way I know how to replace the number
// Need to call stringy to make sure its the right length to cut
@@ -539,7 +527,7 @@ public static ProcStatus FormatString(DMProcState state) {
formattedString.Append("0th");
}
} else if (interp.TryGetValueAsDreamObject(out var interpObj)) {
- var typeStr = interpObj.ObjectDefinition.Type.ToString();
+ var typeStr = interpObj.ObjectDefinition.Type;
var lastIdx = formattedString.ToString().LastIndexOf(typeStr);
if (lastIdx != -1) { // Can this even fail?
formattedString.Remove(lastIdx, typeStr.Length);
@@ -550,6 +538,7 @@ public static ProcStatus FormatString(DMProcState state) {
// we support this behavior for some non-floats but not all, so just append 0th anyways for now
formattedString.Append("0th");
}
+
continue;
case StringFormatEncoder.FormatSuffix.LowerRoman:
postPrefix = formatType;
@@ -582,7 +571,7 @@ public static ProcStatus Initial(DMProcState state) {
return ProcStatus.Continue;
}
- if (!key.TryGetValueAsString(out string property)) {
+ if (!key.TryGetValueAsString(out string? property)) {
throw new Exception("Invalid var for initial() call: " + key);
}
@@ -707,9 +696,11 @@ public static ProcStatus PushGlobalVars(DMProcState state) {
state.Push(new DreamValue(new DreamGlobalVars(state.Proc.ObjectTree.List.ObjectDefinition)));
return ProcStatus.Continue;
}
+
#endregion Values
#region Math
+
public static ProcStatus Add(DMProcState state) {
DreamValue second = state.Pop();
DreamValue first = state.Pop();
@@ -732,6 +723,7 @@ public static ProcStatus Add(DMProcState state) {
if (second.Type == DreamValue.DreamValueType.Float) {
output = new DreamValue(firstFloat + second.MustGetValueAsFloat());
}
+
break;
}
case DreamValue.DreamValueType.String when second.Type == DreamValue.DreamValueType.String:
@@ -1048,6 +1040,7 @@ public static ProcStatus BooleanOr(DMProcState state) {
state.Push(a);
state.Jump(jumpPosition);
}
+
return ProcStatus.Continue;
}
@@ -1217,6 +1210,7 @@ public static ProcStatus Multiply(DMProcState state) {
} else {
throw new Exception($"Invalid multiply operation on {first} and {second}");
}
+
return ProcStatus.Continue;
}
@@ -1310,9 +1304,11 @@ public static ProcStatus Subtract(DMProcState state) {
return ProcStatus.Continue;
}
+
#endregion Math
#region Comparisons
+
public static ProcStatus CompareEquals(DMProcState state) {
DreamValue second = state.Pop();
DreamValue first = state.Pop();
@@ -1457,6 +1453,7 @@ private static DreamValue TypecheckHelper(DreamValue typeValue, DreamValue value
#endregion Comparisons
#region Flow
+
public static ProcStatus Call(DMProcState state) {
DreamReference procRef = state.ReadReference();
var argumentInfo = state.ReadProcArguments();
@@ -1865,9 +1862,11 @@ public static ProcStatus ReturnReferenceValue(DMProcState state) {
state.SetReturn(state.GetReferenceValue(reference));
return ProcStatus.Returned;
}
+
#endregion Flow
#region Builtins
+
public static ProcStatus GetStep(DMProcState state) {
var d = state.Pop();
var l = state.Pop();
@@ -2253,10 +2252,10 @@ public static ProcStatus PickUnweighted(DMProcState state) {
}
public static ProcStatus Prob(DMProcState state) {
- DreamValue P = state.Pop();
+ DreamValue probability = state.Pop();
- if (P.TryGetValueAsFloat(out float probability)) {
- int result = (state.DreamManager.Random.Prob(probability / 100)) ? 1 : 0;
+ if (probability.TryGetValueAsFloat(out float probabilityValue)) {
+ int result = (state.DreamManager.Random.Prob(probabilityValue / 100)) ? 1 : 0;
state.Push(new DreamValue(result));
} else {
@@ -2276,7 +2275,7 @@ public static ProcStatus IsSaved(DMProcState state) {
return ProcStatus.Continue;
}
- if (!key.TryGetValueAsString(out string property)) {
+ if (!key.TryGetValueAsString(out string? property)) {
throw new Exception($"Invalid var for issaved() call: {key}");
}
@@ -2304,9 +2303,11 @@ public static ProcStatus IsSaved(DMProcState state) {
return ProcStatus.Continue;
}
+
#endregion Builtins
#region Others
+
private static void PerformOutput(DreamValue a, DreamValue b) {
if (a.TryGetValueAsDreamResource(out var resource)) {
resource.Output(b);
@@ -2650,9 +2651,11 @@ public static ProcStatus DereferenceCall(DMProcState state) {
return state.Call(proc, instance, arguments);
}
+
#endregion Others
#region Helpers
+
[SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")]
private static bool IsEqual(DreamValue first, DreamValue second) {
// null should only ever be equal to null
@@ -2756,9 +2759,10 @@ private static bool IsGreaterThan(DreamValue first, DreamValue second) {
default: {
if (first.IsNull) {
if (second.Type == DreamValue.DreamValueType.Float) return 0 > second.MustGetValueAsFloat();
- if (second.TryGetValueAsString(out var s)) return false;
+ if (second.TryGetValueAsString(out _)) return false;
if (second.IsNull) return false;
}
+
throw new Exception("Invalid greater than comparison on " + first + " and " + second);
}
}
@@ -2778,6 +2782,7 @@ private static bool IsLessThan(DreamValue first, DreamValue second) {
if (second.TryGetValueAsString(out var s)) return s != "";
if (second.IsNull) return false;
}
+
throw new Exception("Invalid less than comparison between " + first + " and " + second);
}
}
@@ -2841,6 +2846,7 @@ private static DreamValue ModulusModulusValues(DreamValue first, DreamValue seco
fraction -= MathF.Truncate(fraction);
return new DreamValue(fraction * secondFloat);
}
+
throw new Exception("Invalid modulusmodulus operation on " + first + " and " + second);
}
@@ -2913,16 +2919,18 @@ private static DreamValue CalculateGradient(List gradientValues, Dre
float normalized = (index - leftBound) / (rightBound - leftBound);
// Cheap way to make sure the gradient works at the extremes (eg 1 and 0)
- if (!left.HasValue || (right.HasValue && normalized == 1) || (right.HasValue && normalized == 0)) {
+ if (!left.HasValue || (right.HasValue && normalized.Equals(1f)) || (right.HasValue && normalized == 0)) {
if (right?.AByte == 255) {
- return new DreamValue(right?.ToHexNoAlpha().ToLower() ?? "#00000000");
+ return new DreamValue(right.Value.ToHexNoAlpha().ToLower());
}
+
return new DreamValue(right?.ToHex().ToLower() ?? "#00000000");
} else if (!right.HasValue) {
- if (left?.AByte == 255) {
- return new DreamValue(left?.ToHexNoAlpha().ToLower() ?? "#00000000");
+ if (left.Value.AByte == 255) {
+ return new DreamValue(left.Value.ToHexNoAlpha().ToLower());
}
- return new DreamValue(left?.ToHex().ToLower() ?? "#00000000");
+
+ return new DreamValue(left.Value.ToHex().ToLower());
} else if (!left.HasValue && !right.HasValue) {
throw new InvalidOperationException("Failed to find any colors");
}
@@ -3027,7 +3035,6 @@ public static ProcStatus PushNStrings(DMProcState state) {
state.Push(new DreamValue(str));
}
-
return ProcStatus.Continue;
}
@@ -3094,7 +3101,7 @@ public static ProcStatus SwitchOnFloat(DMProcState state) {
int casePosition = state.ReadInt();
var test = state.Pop();
if (test.TryGetValueAsFloat(out var value)) {
- if (testValue == value) {
+ if (testValue.Equals(value)) {
state.Jump(casePosition);
} else {
state.Push(test);
@@ -3123,7 +3130,6 @@ public static ProcStatus SwitchOnString(DMProcState state) {
return ProcStatus.Continue;
}
-
public static ProcStatus PushNOfStringFloat(DMProcState state) {
int count = state.ReadInt();
diff --git a/OpenDreamRuntime/Procs/DMProc.cs b/OpenDreamRuntime/Procs/DMProc.cs
index f0913cd35b..89e64a1b16 100644
--- a/OpenDreamRuntime/Procs/DMProc.cs
+++ b/OpenDreamRuntime/Procs/DMProc.cs
@@ -13,406 +13,408 @@
using OpenDreamShared.Dream;
using Robust.Shared.Utility;
-namespace OpenDreamRuntime.Procs {
- public sealed class DMProc : DreamProc {
- public readonly byte[] Bytecode;
-
- public readonly bool IsNullProc;
- public IReadOnlyList LocalNames { get; }
- public readonly List SourceInfo;
-
- public readonly AtomManager AtomManager;
- public readonly DreamManager DreamManager;
- public readonly ProcScheduler ProcScheduler;
- public readonly IDreamMapManager DreamMapManager;
- public readonly IDreamDebugManager DreamDebugManager;
- public readonly DreamResourceManager DreamResourceManager;
- public readonly DreamObjectTree ObjectTree;
- public readonly ServerVerbSystem VerbSystem;
-
- private readonly int _maxStackSize;
-
- public DMProc(int id, TreeEntry owningType, ProcDefinitionJson json, string? name, DreamManager dreamManager, AtomManager atomManager, IDreamMapManager dreamMapManager, IDreamDebugManager dreamDebugManager, DreamResourceManager dreamResourceManager, DreamObjectTree objectTree, ProcScheduler procScheduler, ServerVerbSystem verbSystem)
- : base(id, owningType, name ?? json.Name, null, json.Attributes, GetArgumentNames(json), GetArgumentTypes(json), json.VerbSrc, json.VerbName, json.VerbCategory, json.VerbDesc, json.Invisibility, json.IsVerb) {
- Bytecode = json.Bytecode ?? [];
- LocalNames = json.Locals ?? [];
- SourceInfo = json.SourceInfo;
- _maxStackSize = json.MaxStackSize;
- IsNullProc = CheckIfNullProc();
-
- AtomManager = atomManager;
- DreamManager = dreamManager;
- ProcScheduler = procScheduler;
- DreamMapManager = dreamMapManager;
- DreamDebugManager = dreamDebugManager;
- DreamResourceManager = dreamResourceManager;
- ObjectTree = objectTree;
- VerbSystem = verbSystem;
- }
-
- public (string Source, int Line) GetSourceAtOffset(int offset) {
- SourceInfoJson current = SourceInfo[0];
- string source = ObjectTree.Strings[current.File!.Value];
+namespace OpenDreamRuntime.Procs;
+
+public sealed class DMProc : DreamProc {
+ public readonly byte[] Bytecode;
+
+ public readonly bool IsNullProc;
+ public IReadOnlyList LocalNames { get; }
+ public readonly List SourceInfo;
+
+ public readonly AtomManager AtomManager;
+ public readonly DreamManager DreamManager;
+ public readonly ProcScheduler ProcScheduler;
+ public readonly IDreamMapManager DreamMapManager;
+ public readonly IDreamDebugManager DreamDebugManager;
+ public readonly DreamResourceManager DreamResourceManager;
+ public readonly DreamObjectTree ObjectTree;
+ public readonly ServerVerbSystem VerbSystem;
+
+ private readonly int _maxStackSize;
+
+ public DMProc(int id, TreeEntry owningType, ProcDefinitionJson json, string? name, DreamManager dreamManager, AtomManager atomManager, IDreamMapManager dreamMapManager, IDreamDebugManager dreamDebugManager, DreamResourceManager dreamResourceManager, DreamObjectTree objectTree, ProcScheduler procScheduler, ServerVerbSystem verbSystem)
+ : base(id, owningType, name ?? json.Name, null, json.Attributes, GetArgumentNames(json), GetArgumentTypes(json), json.VerbSrc, json.VerbName, json.VerbCategory, json.VerbDesc, json.Invisibility, json.IsVerb) {
+ Bytecode = json.Bytecode ?? [];
+ LocalNames = json.Locals ?? [];
+ SourceInfo = json.SourceInfo;
+ _maxStackSize = json.MaxStackSize;
+ IsNullProc = CheckIfNullProc();
+
+ AtomManager = atomManager;
+ DreamManager = dreamManager;
+ ProcScheduler = procScheduler;
+ DreamMapManager = dreamMapManager;
+ DreamDebugManager = dreamDebugManager;
+ DreamResourceManager = dreamResourceManager;
+ ObjectTree = objectTree;
+ VerbSystem = verbSystem;
+ }
- int i = 0;
- do {
- var next = SourceInfo[i++];
- if (next.Offset > offset)
- break;
+ public (string Source, int Line) GetSourceAtOffset(int offset) {
+ SourceInfoJson current = SourceInfo[0];
+ string source = ObjectTree.Strings[current.File!.Value];
- current = next;
- if (current.File != null)
- source = ObjectTree.Strings[current.File.Value];
- } while (i < SourceInfo.Count);
+ int i = 0;
+ do {
+ var next = SourceInfo[i++];
+ if (next.Offset > offset)
+ break;
- return (source, current.Line);
- }
+ current = next;
+ if (current.File != null)
+ source = ObjectTree.Strings[current.File.Value];
+ } while (i < SourceInfo.Count);
- ///
- /// Checks if the given bytecode offset is the first on a line of the source code
- ///
- public bool IsOnLineChange(int offset) {
- foreach (var sourceInfo in SourceInfo) {
- if (sourceInfo.Offset == offset)
- return true;
- }
+ return (source, current.Line);
+ }
- return false;
+ ///
+ /// Checks if the given bytecode offset is the first on a line of the source code
+ ///
+ public bool IsOnLineChange(int offset) {
+ foreach (var sourceInfo in SourceInfo) {
+ if (sourceInfo.Offset == offset)
+ return true;
}
- public bool TryGetOffsetAtSource(string source, int line, out int offset) {
- string? currentSource = null;
+ return false;
+ }
- int i = 0;
- do {
- var current = SourceInfo[i++];
+ public bool TryGetOffsetAtSource(string source, int line, out int offset) {
+ string? currentSource = null;
- if (current.File != null)
- currentSource = ObjectTree.Strings[current.File.Value];
+ int i = 0;
+ do {
+ var current = SourceInfo[i++];
- if (currentSource == source && current.Line == line) {
- offset = current.Offset;
- return true;
- }
- } while (i < SourceInfo.Count);
+ if (current.File != null)
+ currentSource = ObjectTree.Strings[current.File.Value];
- offset = 0;
- return false;
- }
+ if (currentSource == source && current.Line == line) {
+ offset = current.Offset;
+ return true;
+ }
+ } while (i < SourceInfo.Count);
- public override ProcState CreateState(DreamThread thread, DreamObject? src, DreamObject? usr, DreamProcArguments arguments) {
- if (IsNullProc) {
- if (!NullProcState.Pool.TryPop(out var nullState)) {
- nullState = new NullProcState();
- }
+ offset = 0;
+ return false;
+ }
- nullState.Initialize(this);
- return nullState;
+ public override ProcState CreateState(DreamThread thread, DreamObject? src, DreamObject? usr, DreamProcArguments arguments) {
+ if (IsNullProc) {
+ if (!NullProcState.Pool.TryPop(out var nullState)) {
+ nullState = new NullProcState();
}
- if (!DMProcState.Pool.TryPop(out var state)) {
- state = new DMProcState();
- }
+ nullState.Initialize(this);
+ return nullState;
+ }
- state.Initialize(this, thread, _maxStackSize, src, usr, arguments);
- return state;
+ if (!DMProcState.Pool.TryPop(out var state)) {
+ state = new DMProcState();
}
- private bool CheckIfNullProc() {
- // We check for two possible patterns, entirely empty procs or pushing and returning self.
- if (Bytecode.Length == 0 || Bytecode is [(byte)DreamProcOpcode.PushReferenceValue, 0x01, (byte)DreamProcOpcode.Return])
- return true;
+ state.Initialize(this, thread, _maxStackSize, src, usr, arguments);
+ return state;
+ }
- return false;
- }
+ private bool CheckIfNullProc() {
+ // We check for two possible patterns, entirely empty procs or pushing and returning self.
+ if (Bytecode.Length == 0 || Bytecode is [(byte)DreamProcOpcode.PushReferenceValue, 0x01, (byte)DreamProcOpcode.Return])
+ return true;
- private static List? GetArgumentNames(ProcDefinitionJson json) {
- if (json.Arguments == null) {
- return new();
- } else {
- var argumentNames = new List(json.Arguments.Count);
- argumentNames.AddRange(json.Arguments.Select(a => a.Name).ToArray());
- return argumentNames;
- }
+ return false;
+ }
+
+ private static List GetArgumentNames(ProcDefinitionJson json) {
+ if (json.Arguments == null) {
+ return new();
+ } else {
+ var argumentNames = new List(json.Arguments.Count);
+ argumentNames.AddRange(json.Arguments.Select(a => a.Name).ToArray());
+ return argumentNames;
}
+ }
- private static List? GetArgumentTypes(ProcDefinitionJson json) {
- if (json.Arguments == null) {
- return null;
- } else {
- var argumentTypes = new List(json.Arguments.Count);
- argumentTypes.AddRange(json.Arguments.Select(a => (DreamValueType)a.Type));
- return argumentTypes;
- }
+ private static List? GetArgumentTypes(ProcDefinitionJson json) {
+ if (json.Arguments == null) {
+ return null;
+ } else {
+ var argumentTypes = new List(json.Arguments.Count);
+ argumentTypes.AddRange(json.Arguments.Select(a => (DreamValueType)a.Type));
+ return argumentTypes;
}
}
+}
- public sealed class NullProcState : ProcState {
- public static readonly Stack Pool = new();
+public sealed class NullProcState : ProcState {
+ public static readonly Stack Pool = new();
- public override DreamProc? Proc => _proc;
+ public override DreamProc? Proc => _proc;
- private DreamProc? _proc;
+ private DreamProc? _proc;
- public override ProcStatus Resume() {
- return ProcStatus.Returned; // do nothing heehoo
- }
+ public override ProcStatus Resume() {
+ return ProcStatus.Returned; // do nothing heehoo
+ }
- public override void AppendStackFrame(StringBuilder builder) {
- throw new NotImplementedException();
- }
+ public override void AppendStackFrame(StringBuilder builder) {
+ throw new NotImplementedException();
+ }
- public void Initialize(DMProc proc) {
- _proc = proc;
- }
+ public void Initialize(DMProc proc) {
+ _proc = proc;
+ }
- public override void Dispose() {
- base.Dispose();
- _proc = null;
- Pool.Push(this);
+ public override void Dispose() {
+ base.Dispose();
+ _proc = null;
+ Pool.Push(this);
+ }
+}
+
+public sealed class DMProcState : ProcState {
+ private delegate ProcStatus OpcodeHandler(DMProcState state);
+
+ public static readonly Stack Pool = new();
+
+ private static readonly ArrayPool DreamValuePool = ArrayPool.Create();
+
+ #region Opcode Handlers
+
+ //Human-readable friendly version, which will be converted to a more efficient lookup at runtime.
+ private static readonly Dictionary OpcodeHandlers = new() {
+ {DreamProcOpcode.BitShiftLeft, DMOpcodeHandlers.BitShiftLeft},
+ {DreamProcOpcode.PushType, DMOpcodeHandlers.PushType},
+ {DreamProcOpcode.PushString, DMOpcodeHandlers.PushString},
+ {DreamProcOpcode.FormatString, DMOpcodeHandlers.FormatString},
+ {DreamProcOpcode.SwitchCaseRange, DMOpcodeHandlers.SwitchCaseRange},
+ {DreamProcOpcode.PushReferenceValue, DMOpcodeHandlers.PushReferenceValue},
+ {DreamProcOpcode.Add, DMOpcodeHandlers.Add},
+ {DreamProcOpcode.Assign, DMOpcodeHandlers.Assign},
+ {DreamProcOpcode.Call, DMOpcodeHandlers.Call},
+ {DreamProcOpcode.MultiplyReference, DMOpcodeHandlers.MultiplyReference},
+ {DreamProcOpcode.JumpIfFalse, DMOpcodeHandlers.JumpIfFalse},
+ {DreamProcOpcode.Jump, DMOpcodeHandlers.Jump},
+ {DreamProcOpcode.CompareEquals, DMOpcodeHandlers.CompareEquals},
+ {DreamProcOpcode.Return, DMOpcodeHandlers.Return},
+ {DreamProcOpcode.PushNull, DMOpcodeHandlers.PushNull},
+ {DreamProcOpcode.Subtract, DMOpcodeHandlers.Subtract},
+ {DreamProcOpcode.CompareLessThan, DMOpcodeHandlers.CompareLessThan},
+ {DreamProcOpcode.CompareGreaterThan, DMOpcodeHandlers.CompareGreaterThan},
+ {DreamProcOpcode.BooleanAnd, DMOpcodeHandlers.BooleanAnd},
+ {DreamProcOpcode.BooleanNot, DMOpcodeHandlers.BooleanNot},
+ {DreamProcOpcode.DivideReference, DMOpcodeHandlers.DivideReference},
+ {DreamProcOpcode.Negate, DMOpcodeHandlers.Negate},
+ {DreamProcOpcode.Modulus, DMOpcodeHandlers.Modulus},
+ {DreamProcOpcode.Append, DMOpcodeHandlers.Append},
+ {DreamProcOpcode.AppendNoPush, DMOpcodeHandlers.AppendNoPush},
+ {DreamProcOpcode.CreateRangeEnumerator, DMOpcodeHandlers.CreateRangeEnumerator},
+ {DreamProcOpcode.Input, DMOpcodeHandlers.Input},
+ {DreamProcOpcode.CompareLessThanOrEqual, DMOpcodeHandlers.CompareLessThanOrEqual},
+ {DreamProcOpcode.CreateAssociativeList, DMOpcodeHandlers.CreateAssociativeList},
+ {DreamProcOpcode.Remove, DMOpcodeHandlers.Remove},
+ {DreamProcOpcode.DeleteObject, DMOpcodeHandlers.DeleteObject},
+ {DreamProcOpcode.PushResource, DMOpcodeHandlers.PushResource},
+ {DreamProcOpcode.CreateList, DMOpcodeHandlers.CreateList},
+ {DreamProcOpcode.CallStatement, DMOpcodeHandlers.CallStatement},
+ {DreamProcOpcode.BitAnd, DMOpcodeHandlers.BitAnd},
+ {DreamProcOpcode.CompareNotEquals, DMOpcodeHandlers.CompareNotEquals},
+ {DreamProcOpcode.PushProc, DMOpcodeHandlers.PushProc},
+ {DreamProcOpcode.Divide, DMOpcodeHandlers.Divide},
+ {DreamProcOpcode.Multiply, DMOpcodeHandlers.Multiply},
+ {DreamProcOpcode.BitXorReference, DMOpcodeHandlers.BitXorReference},
+ {DreamProcOpcode.BitXor, DMOpcodeHandlers.BitXor},
+ {DreamProcOpcode.BitOr, DMOpcodeHandlers.BitOr},
+ {DreamProcOpcode.BitNot, DMOpcodeHandlers.BitNot},
+ {DreamProcOpcode.Combine, DMOpcodeHandlers.Combine},
+ {DreamProcOpcode.CreateObject, DMOpcodeHandlers.CreateObject},
+ {DreamProcOpcode.BooleanOr, DMOpcodeHandlers.BooleanOr},
+ {DreamProcOpcode.CreateMultidimensionalList, DMOpcodeHandlers.CreateMultidimensionalList},
+ {DreamProcOpcode.CompareGreaterThanOrEqual, DMOpcodeHandlers.CompareGreaterThanOrEqual},
+ {DreamProcOpcode.SwitchCase, DMOpcodeHandlers.SwitchCase},
+ {DreamProcOpcode.Mask, DMOpcodeHandlers.Mask},
+ {DreamProcOpcode.Error, DMOpcodeHandlers.Error},
+ {DreamProcOpcode.IsInList, DMOpcodeHandlers.IsInList},
+ {DreamProcOpcode.PushFloat, DMOpcodeHandlers.PushFloat},
+ {DreamProcOpcode.ModulusReference, DMOpcodeHandlers.ModulusReference},
+ {DreamProcOpcode.CreateListEnumerator, DMOpcodeHandlers.CreateListEnumerator},
+ {DreamProcOpcode.Enumerate, DMOpcodeHandlers.Enumerate},
+ {DreamProcOpcode.DestroyEnumerator, DMOpcodeHandlers.DestroyEnumerator},
+ {DreamProcOpcode.Browse, DMOpcodeHandlers.Browse},
+ {DreamProcOpcode.BrowseResource, DMOpcodeHandlers.BrowseResource},
+ {DreamProcOpcode.OutputControl, DMOpcodeHandlers.OutputControl},
+ {DreamProcOpcode.BitShiftRight, DMOpcodeHandlers.BitShiftRight},
+ {DreamProcOpcode.CreateFilteredListEnumerator, DMOpcodeHandlers.CreateFilteredListEnumerator},
+ {DreamProcOpcode.Power, DMOpcodeHandlers.Power},
+ {DreamProcOpcode.Prompt, DMOpcodeHandlers.Prompt},
+ {DreamProcOpcode.Link, DMOpcodeHandlers.Link},
+ {DreamProcOpcode.Ftp, DMOpcodeHandlers.Ftp},
+ {DreamProcOpcode.Initial, DMOpcodeHandlers.Initial},
+ {DreamProcOpcode.AsType, DMOpcodeHandlers.AsType},
+ {DreamProcOpcode.IsType, DMOpcodeHandlers.IsType},
+ {DreamProcOpcode.LocateCoord, DMOpcodeHandlers.LocateCoord},
+ {DreamProcOpcode.Locate, DMOpcodeHandlers.Locate},
+ {DreamProcOpcode.IsNull, DMOpcodeHandlers.IsNull},
+ {DreamProcOpcode.Spawn, DMOpcodeHandlers.Spawn},
+ {DreamProcOpcode.OutputReference, DMOpcodeHandlers.OutputReference},
+ {DreamProcOpcode.Output, DMOpcodeHandlers.Output},
+ {DreamProcOpcode.Pop, DMOpcodeHandlers.Pop},
+ {DreamProcOpcode.Prob, DMOpcodeHandlers.Prob},
+ {DreamProcOpcode.IsSaved, DMOpcodeHandlers.IsSaved},
+ {DreamProcOpcode.PickUnweighted, DMOpcodeHandlers.PickUnweighted},
+ {DreamProcOpcode.PickWeighted, DMOpcodeHandlers.PickWeighted},
+ {DreamProcOpcode.Increment, DMOpcodeHandlers.Increment},
+ {DreamProcOpcode.Decrement, DMOpcodeHandlers.Decrement},
+ {DreamProcOpcode.CompareEquivalent, DMOpcodeHandlers.CompareEquivalent},
+ {DreamProcOpcode.CompareNotEquivalent, DMOpcodeHandlers.CompareNotEquivalent},
+ {DreamProcOpcode.Throw, DMOpcodeHandlers.Throw},
+ {DreamProcOpcode.IsInRange, DMOpcodeHandlers.IsInRange},
+ {DreamProcOpcode.MassConcatenation, DMOpcodeHandlers.MassConcatenation},
+ {DreamProcOpcode.CreateTypeEnumerator, DMOpcodeHandlers.CreateTypeEnumerator},
+ {DreamProcOpcode.PushGlobalVars, DMOpcodeHandlers.PushGlobalVars},
+ {DreamProcOpcode.ModulusModulus, DMOpcodeHandlers.ModulusModulus},
+ {DreamProcOpcode.ModulusModulusReference, DMOpcodeHandlers.ModulusModulusReference},
+ {DreamProcOpcode.AssignInto, DMOpcodeHandlers.AssignInto},
+ {DreamProcOpcode.JumpIfNull, DMOpcodeHandlers.JumpIfNull},
+ {DreamProcOpcode.JumpIfNullNoPop, DMOpcodeHandlers.JumpIfNullNoPop},
+ {DreamProcOpcode.JumpIfTrueReference, DMOpcodeHandlers.JumpIfTrueReference},
+ {DreamProcOpcode.JumpIfFalseReference, DMOpcodeHandlers.JumpIfFalseReference},
+ {DreamProcOpcode.DereferenceField, DMOpcodeHandlers.DereferenceField},
+ {DreamProcOpcode.DereferenceIndex, DMOpcodeHandlers.DereferenceIndex},
+ {DreamProcOpcode.IndexRefWithString, DMOpcodeHandlers.IndexRefWithString},
+ {DreamProcOpcode.DereferenceCall, DMOpcodeHandlers.DereferenceCall},
+ {DreamProcOpcode.PopReference, DMOpcodeHandlers.PopReference},
+ {DreamProcOpcode.BitShiftLeftReference,DMOpcodeHandlers.BitShiftLeftReference},
+ {DreamProcOpcode.BitShiftRightReference, DMOpcodeHandlers.BitShiftRightReference},
+ {DreamProcOpcode.Try, DMOpcodeHandlers.Try},
+ {DreamProcOpcode.TryNoValue, DMOpcodeHandlers.TryNoValue},
+ {DreamProcOpcode.EndTry, DMOpcodeHandlers.EndTry},
+ {DreamProcOpcode.Gradient, DMOpcodeHandlers.Gradient},
+ {DreamProcOpcode.Sin, DMOpcodeHandlers.Sin},
+ {DreamProcOpcode.Cos, DMOpcodeHandlers.Cos},
+ {DreamProcOpcode.Tan, DMOpcodeHandlers.Tan},
+ {DreamProcOpcode.ArcSin, DMOpcodeHandlers.ArcSin},
+ {DreamProcOpcode.ArcCos, DMOpcodeHandlers.ArcCos},
+ {DreamProcOpcode.ArcTan, DMOpcodeHandlers.ArcTan},
+ {DreamProcOpcode.ArcTan2, DMOpcodeHandlers.ArcTan2},
+ {DreamProcOpcode.Sqrt, DMOpcodeHandlers.Sqrt},
+ {DreamProcOpcode.Log, DMOpcodeHandlers.Log},
+ {DreamProcOpcode.LogE, DMOpcodeHandlers.LogE},
+ {DreamProcOpcode.Abs, DMOpcodeHandlers.Abs},
+ {DreamProcOpcode.EnumerateNoAssign, DMOpcodeHandlers.EnumerateNoAssign},
+ {DreamProcOpcode.GetStep, DMOpcodeHandlers.GetStep},
+ {DreamProcOpcode.Length, DMOpcodeHandlers.Length},
+ {DreamProcOpcode.GetDir, DMOpcodeHandlers.GetDir},
+ {DreamProcOpcode.DebuggerBreakpoint, DMOpcodeHandlers.DebuggerBreakpoint},
+ {DreamProcOpcode.Rgb, DMOpcodeHandlers.Rgb},
+ // Peephole optimizer opcode handlers
+ {DreamProcOpcode.NullRef, DMOpcodeHandlers.NullRef},
+ {DreamProcOpcode.AssignNoPush, DMOpcodeHandlers.AssignNoPush},
+ {DreamProcOpcode.PushRefAndDereferenceField, DMOpcodeHandlers.PushReferenceAndDereferenceField},
+ {DreamProcOpcode.PushNRefs, DMOpcodeHandlers.PushNRefs},
+ {DreamProcOpcode.PushNFloats, DMOpcodeHandlers.PushNFloats},
+ {DreamProcOpcode.PushNStrings, DMOpcodeHandlers.PushNStrings},
+ {DreamProcOpcode.PushNResources, DMOpcodeHandlers.PushNResources},
+ {DreamProcOpcode.PushStringFloat, DMOpcodeHandlers.PushStringFloat},
+ {DreamProcOpcode.SwitchOnFloat, DMOpcodeHandlers.SwitchOnFloat},
+ {DreamProcOpcode.SwitchOnString, DMOpcodeHandlers.SwitchOnString},
+ {DreamProcOpcode.JumpIfReferenceFalse, DMOpcodeHandlers.JumpIfReferenceFalse},
+ {DreamProcOpcode.PushNOfStringFloats, DMOpcodeHandlers.PushNOfStringFloat},
+ {DreamProcOpcode.CreateListNFloats, DMOpcodeHandlers.CreateListNFloats},
+ {DreamProcOpcode.CreateListNStrings, DMOpcodeHandlers.CreateListNStrings},
+ {DreamProcOpcode.CreateListNRefs, DMOpcodeHandlers.CreateListNRefs},
+ {DreamProcOpcode.CreateListNResources, DMOpcodeHandlers.CreateListNResources},
+ {DreamProcOpcode.IsTypeDirect, DMOpcodeHandlers.IsTypeDirect},
+ {DreamProcOpcode.ReturnReferenceValue, DMOpcodeHandlers.ReturnReferenceValue},
+ {DreamProcOpcode.ReturnFloat, DMOpcodeHandlers.ReturnFloat}
+ };
+
+ public static readonly unsafe delegate*[] OpcodeHandlersTable;
+
+ #endregion
+
+ public DreamManager DreamManager => _proc.DreamManager;
+ public ProcScheduler ProcScheduler => _proc.ProcScheduler;
+ public IDreamDebugManager DebugManager => _proc.DreamDebugManager;
+
+ /// This stores our 'src' value. May be null!
+ public DreamObject? Instance;
+
+ public DreamObject? Usr;
+ public int ArgumentCount;
+ private readonly Stack _catchPosition = new();
+ private readonly Stack _catchVarIndex = new();
+ private const int NoTryCatchVar = -1;
+ public readonly IDreamValueEnumerator?[] Enumerators = new IDreamValueEnumerator?[16];
+
+ public int ProgramCounter => _pc;
+ private int _pc;
+
+ private bool _firstResume = true;
+
+ // Contains both arguments (at index 0) and local vars (at index ArgumentCount)
+ private DreamValue[] _localVariables = default!;
+
+ private DMProc _proc = default!;
+ public override DMProc Proc => _proc;
+
+ /// Static initializer for maintainer friendly OpcodeHandlers to performance friendly _opcodeHandlers
+ static unsafe DMProcState() {
+ int maxOpcode = (int)OpcodeHandlers.Keys.Max();
+
+ OpcodeHandlersTable = new delegate*[256];
+ foreach (var (dpo, handler) in OpcodeHandlers) {
+ OpcodeHandlersTable[(int) dpo] = (delegate*) handler.Method.MethodHandle.GetFunctionPointer();
+ }
+
+ var invalid = DMOpcodeHandlers.Invalid;
+ var invalidPtr = (delegate*)invalid.Method.MethodHandle.GetFunctionPointer();
+
+ OpcodeHandlersTable[0] = invalidPtr;
+ for (int i = maxOpcode + 1; i < 256; i++) {
+ OpcodeHandlersTable[i] = invalidPtr;
}
}
- public sealed class DMProcState : ProcState {
- private delegate ProcStatus OpcodeHandler(DMProcState state);
-
- public static readonly Stack Pool = new();
-
- private static readonly ArrayPool _dreamValuePool = ArrayPool.Create();
-
- #region Opcode Handlers
-
- //Human readable friendly version, which will be converted to a more efficient lookup at runtime.
- private static readonly Dictionary _opcodeHandlers = new() {
- {DreamProcOpcode.BitShiftLeft, DMOpcodeHandlers.BitShiftLeft},
- {DreamProcOpcode.PushType, DMOpcodeHandlers.PushType},
- {DreamProcOpcode.PushString, DMOpcodeHandlers.PushString},
- {DreamProcOpcode.FormatString, DMOpcodeHandlers.FormatString},
- {DreamProcOpcode.SwitchCaseRange, DMOpcodeHandlers.SwitchCaseRange},
- {DreamProcOpcode.PushReferenceValue, DMOpcodeHandlers.PushReferenceValue},
- {DreamProcOpcode.Add, DMOpcodeHandlers.Add},
- {DreamProcOpcode.Assign, DMOpcodeHandlers.Assign},
- {DreamProcOpcode.Call, DMOpcodeHandlers.Call},
- {DreamProcOpcode.MultiplyReference, DMOpcodeHandlers.MultiplyReference},
- {DreamProcOpcode.JumpIfFalse, DMOpcodeHandlers.JumpIfFalse},
- {DreamProcOpcode.Jump, DMOpcodeHandlers.Jump},
- {DreamProcOpcode.CompareEquals, DMOpcodeHandlers.CompareEquals},
- {DreamProcOpcode.Return, DMOpcodeHandlers.Return},
- {DreamProcOpcode.PushNull, DMOpcodeHandlers.PushNull},
- {DreamProcOpcode.Subtract, DMOpcodeHandlers.Subtract},
- {DreamProcOpcode.CompareLessThan, DMOpcodeHandlers.CompareLessThan},
- {DreamProcOpcode.CompareGreaterThan, DMOpcodeHandlers.CompareGreaterThan},
- {DreamProcOpcode.BooleanAnd, DMOpcodeHandlers.BooleanAnd},
- {DreamProcOpcode.BooleanNot, DMOpcodeHandlers.BooleanNot},
- {DreamProcOpcode.DivideReference, DMOpcodeHandlers.DivideReference},
- {DreamProcOpcode.Negate, DMOpcodeHandlers.Negate},
- {DreamProcOpcode.Modulus, DMOpcodeHandlers.Modulus},
- {DreamProcOpcode.Append, DMOpcodeHandlers.Append},
- {DreamProcOpcode.AppendNoPush, DMOpcodeHandlers.AppendNoPush},
- {DreamProcOpcode.CreateRangeEnumerator, DMOpcodeHandlers.CreateRangeEnumerator},
- {DreamProcOpcode.Input, DMOpcodeHandlers.Input},
- {DreamProcOpcode.CompareLessThanOrEqual, DMOpcodeHandlers.CompareLessThanOrEqual},
- {DreamProcOpcode.CreateAssociativeList, DMOpcodeHandlers.CreateAssociativeList},
- {DreamProcOpcode.Remove, DMOpcodeHandlers.Remove},
- {DreamProcOpcode.DeleteObject, DMOpcodeHandlers.DeleteObject},
- {DreamProcOpcode.PushResource, DMOpcodeHandlers.PushResource},
- {DreamProcOpcode.CreateList, DMOpcodeHandlers.CreateList},
- {DreamProcOpcode.CallStatement, DMOpcodeHandlers.CallStatement},
- {DreamProcOpcode.BitAnd, DMOpcodeHandlers.BitAnd},
- {DreamProcOpcode.CompareNotEquals, DMOpcodeHandlers.CompareNotEquals},
- {DreamProcOpcode.PushProc, DMOpcodeHandlers.PushProc},
- {DreamProcOpcode.Divide, DMOpcodeHandlers.Divide},
- {DreamProcOpcode.Multiply, DMOpcodeHandlers.Multiply},
- {DreamProcOpcode.BitXorReference, DMOpcodeHandlers.BitXorReference},
- {DreamProcOpcode.BitXor, DMOpcodeHandlers.BitXor},
- {DreamProcOpcode.BitOr, DMOpcodeHandlers.BitOr},
- {DreamProcOpcode.BitNot, DMOpcodeHandlers.BitNot},
- {DreamProcOpcode.Combine, DMOpcodeHandlers.Combine},
- {DreamProcOpcode.CreateObject, DMOpcodeHandlers.CreateObject},
- {DreamProcOpcode.BooleanOr, DMOpcodeHandlers.BooleanOr},
- {DreamProcOpcode.CreateMultidimensionalList, DMOpcodeHandlers.CreateMultidimensionalList},
- {DreamProcOpcode.CompareGreaterThanOrEqual, DMOpcodeHandlers.CompareGreaterThanOrEqual},
- {DreamProcOpcode.SwitchCase, DMOpcodeHandlers.SwitchCase},
- {DreamProcOpcode.Mask, DMOpcodeHandlers.Mask},
- {DreamProcOpcode.Error, DMOpcodeHandlers.Error},
- {DreamProcOpcode.IsInList, DMOpcodeHandlers.IsInList},
- {DreamProcOpcode.PushFloat, DMOpcodeHandlers.PushFloat},
- {DreamProcOpcode.ModulusReference, DMOpcodeHandlers.ModulusReference},
- {DreamProcOpcode.CreateListEnumerator, DMOpcodeHandlers.CreateListEnumerator},
- {DreamProcOpcode.Enumerate, DMOpcodeHandlers.Enumerate},
- {DreamProcOpcode.DestroyEnumerator, DMOpcodeHandlers.DestroyEnumerator},
- {DreamProcOpcode.Browse, DMOpcodeHandlers.Browse},
- {DreamProcOpcode.BrowseResource, DMOpcodeHandlers.BrowseResource},
- {DreamProcOpcode.OutputControl, DMOpcodeHandlers.OutputControl},
- {DreamProcOpcode.BitShiftRight, DMOpcodeHandlers.BitShiftRight},
- {DreamProcOpcode.CreateFilteredListEnumerator, DMOpcodeHandlers.CreateFilteredListEnumerator},
- {DreamProcOpcode.Power, DMOpcodeHandlers.Power},
- {DreamProcOpcode.Prompt, DMOpcodeHandlers.Prompt},
- {DreamProcOpcode.Link, DMOpcodeHandlers.Link},
- {DreamProcOpcode.Ftp, DMOpcodeHandlers.Ftp},
- {DreamProcOpcode.Initial, DMOpcodeHandlers.Initial},
- {DreamProcOpcode.AsType, DMOpcodeHandlers.AsType},
- {DreamProcOpcode.IsType, DMOpcodeHandlers.IsType},
- {DreamProcOpcode.LocateCoord, DMOpcodeHandlers.LocateCoord},
- {DreamProcOpcode.Locate, DMOpcodeHandlers.Locate},
- {DreamProcOpcode.IsNull, DMOpcodeHandlers.IsNull},
- {DreamProcOpcode.Spawn, DMOpcodeHandlers.Spawn},
- {DreamProcOpcode.OutputReference, DMOpcodeHandlers.OutputReference},
- {DreamProcOpcode.Output, DMOpcodeHandlers.Output},
- {DreamProcOpcode.Pop, DMOpcodeHandlers.Pop},
- {DreamProcOpcode.Prob, DMOpcodeHandlers.Prob},
- {DreamProcOpcode.IsSaved, DMOpcodeHandlers.IsSaved},
- {DreamProcOpcode.PickUnweighted, DMOpcodeHandlers.PickUnweighted},
- {DreamProcOpcode.PickWeighted, DMOpcodeHandlers.PickWeighted},
- {DreamProcOpcode.Increment, DMOpcodeHandlers.Increment},
- {DreamProcOpcode.Decrement, DMOpcodeHandlers.Decrement},
- {DreamProcOpcode.CompareEquivalent, DMOpcodeHandlers.CompareEquivalent},
- {DreamProcOpcode.CompareNotEquivalent, DMOpcodeHandlers.CompareNotEquivalent},
- {DreamProcOpcode.Throw, DMOpcodeHandlers.Throw},
- {DreamProcOpcode.IsInRange, DMOpcodeHandlers.IsInRange},
- {DreamProcOpcode.MassConcatenation, DMOpcodeHandlers.MassConcatenation},
- {DreamProcOpcode.CreateTypeEnumerator, DMOpcodeHandlers.CreateTypeEnumerator},
- {DreamProcOpcode.PushGlobalVars, DMOpcodeHandlers.PushGlobalVars},
- {DreamProcOpcode.ModulusModulus, DMOpcodeHandlers.ModulusModulus},
- {DreamProcOpcode.ModulusModulusReference, DMOpcodeHandlers.ModulusModulusReference},
- {DreamProcOpcode.AssignInto, DMOpcodeHandlers.AssignInto},
- {DreamProcOpcode.JumpIfNull, DMOpcodeHandlers.JumpIfNull},
- {DreamProcOpcode.JumpIfNullNoPop, DMOpcodeHandlers.JumpIfNullNoPop},
- {DreamProcOpcode.JumpIfTrueReference, DMOpcodeHandlers.JumpIfTrueReference},
- {DreamProcOpcode.JumpIfFalseReference, DMOpcodeHandlers.JumpIfFalseReference},
- {DreamProcOpcode.DereferenceField, DMOpcodeHandlers.DereferenceField},
- {DreamProcOpcode.DereferenceIndex, DMOpcodeHandlers.DereferenceIndex},
- {DreamProcOpcode.IndexRefWithString, DMOpcodeHandlers.IndexRefWithString},
- {DreamProcOpcode.DereferenceCall, DMOpcodeHandlers.DereferenceCall},
- {DreamProcOpcode.PopReference, DMOpcodeHandlers.PopReference},
- {DreamProcOpcode.BitShiftLeftReference,DMOpcodeHandlers.BitShiftLeftReference},
- {DreamProcOpcode.BitShiftRightReference, DMOpcodeHandlers.BitShiftRightReference},
- {DreamProcOpcode.Try, DMOpcodeHandlers.Try},
- {DreamProcOpcode.TryNoValue, DMOpcodeHandlers.TryNoValue},
- {DreamProcOpcode.EndTry, DMOpcodeHandlers.EndTry},
- {DreamProcOpcode.Gradient, DMOpcodeHandlers.Gradient},
- {DreamProcOpcode.Sin, DMOpcodeHandlers.Sin},
- {DreamProcOpcode.Cos, DMOpcodeHandlers.Cos},
- {DreamProcOpcode.Tan, DMOpcodeHandlers.Tan},
- {DreamProcOpcode.ArcSin, DMOpcodeHandlers.ArcSin},
- {DreamProcOpcode.ArcCos, DMOpcodeHandlers.ArcCos},
- {DreamProcOpcode.ArcTan, DMOpcodeHandlers.ArcTan},
- {DreamProcOpcode.ArcTan2, DMOpcodeHandlers.ArcTan2},
- {DreamProcOpcode.Sqrt, DMOpcodeHandlers.Sqrt},
- {DreamProcOpcode.Log, DMOpcodeHandlers.Log},
- {DreamProcOpcode.LogE, DMOpcodeHandlers.LogE},
- {DreamProcOpcode.Abs, DMOpcodeHandlers.Abs},
- {DreamProcOpcode.EnumerateNoAssign, DMOpcodeHandlers.EnumerateNoAssign},
- {DreamProcOpcode.GetStep, DMOpcodeHandlers.GetStep},
- {DreamProcOpcode.Length, DMOpcodeHandlers.Length},
- {DreamProcOpcode.GetDir, DMOpcodeHandlers.GetDir},
- {DreamProcOpcode.DebuggerBreakpoint, DMOpcodeHandlers.DebuggerBreakpoint},
- {DreamProcOpcode.Rgb, DMOpcodeHandlers.Rgb},
- // Peephole optimizer opcode handlers
- {DreamProcOpcode.NullRef, DMOpcodeHandlers.NullRef},
- {DreamProcOpcode.AssignNoPush, DMOpcodeHandlers.AssignNoPush},
- {DreamProcOpcode.PushRefAndDereferenceField, DMOpcodeHandlers.PushReferenceAndDereferenceField},
- {DreamProcOpcode.PushNRefs, DMOpcodeHandlers.PushNRefs},
- {DreamProcOpcode.PushNFloats, DMOpcodeHandlers.PushNFloats},
- {DreamProcOpcode.PushNStrings, DMOpcodeHandlers.PushNStrings},
- {DreamProcOpcode.PushNResources, DMOpcodeHandlers.PushNResources},
- {DreamProcOpcode.PushStringFloat, DMOpcodeHandlers.PushStringFloat},
- {DreamProcOpcode.SwitchOnFloat, DMOpcodeHandlers.SwitchOnFloat},
- {DreamProcOpcode.SwitchOnString, DMOpcodeHandlers.SwitchOnString},
- {DreamProcOpcode.JumpIfReferenceFalse, DMOpcodeHandlers.JumpIfReferenceFalse},
- {DreamProcOpcode.PushNOfStringFloats, DMOpcodeHandlers.PushNOfStringFloat},
- {DreamProcOpcode.CreateListNFloats, DMOpcodeHandlers.CreateListNFloats},
- {DreamProcOpcode.CreateListNStrings, DMOpcodeHandlers.CreateListNStrings},
- {DreamProcOpcode.CreateListNRefs, DMOpcodeHandlers.CreateListNRefs},
- {DreamProcOpcode.CreateListNResources, DMOpcodeHandlers.CreateListNResources},
- {DreamProcOpcode.IsTypeDirect, DMOpcodeHandlers.IsTypeDirect},
- {DreamProcOpcode.ReturnReferenceValue, DMOpcodeHandlers.ReturnReferenceValue},
- {DreamProcOpcode.ReturnFloat, DMOpcodeHandlers.ReturnFloat}
- };
-
- public static readonly unsafe delegate*[] OpcodeHandlers;
-
- #endregion
-
- public DreamManager DreamManager => _proc.DreamManager;
- public ProcScheduler ProcScheduler => _proc.ProcScheduler;
- public IDreamDebugManager DebugManager => _proc.DreamDebugManager;
-
- /// This stores our 'src' value. May be null!
- public DreamObject? Instance;
- public DreamObject? Usr;
- public int ArgumentCount;
- private readonly Stack _catchPosition = new();
- private readonly Stack _catchVarIndex = new();
- public const int NoTryCatchVar = -1;
- public IDreamValueEnumerator?[] Enumerators = new IDreamValueEnumerator?[16];
-
- private int _pc = 0;
- public int ProgramCounter => _pc;
-
- private bool _firstResume = true;
-
- // Contains both arguments (at index 0) and local vars (at index ArgumentCount)
- private DreamValue[] _localVariables;
-
- private DMProc _proc;
- public override DMProc Proc => _proc;
-
- /// Static initializer for maintainer friendly OpcodeHandlers to performance friendly _opcodeHandlers
- static unsafe DMProcState() {
- int maxOpcode = (int)_opcodeHandlers.Keys.Max();
-
- OpcodeHandlers = new delegate*[256];
- foreach (var (dpo, handler) in _opcodeHandlers) {
- OpcodeHandlers[(int) dpo] = (delegate*) handler.Method.MethodHandle.GetFunctionPointer();
- }
+ public DMProcState() { }
+
+ private DMProcState(DMProcState other, DreamThread thread) {
+ base.Initialize(thread, other.WaitFor);
+ _proc = other._proc;
+ Instance = other.Instance;
+ Usr = other.Usr;
+ ArgumentCount = other.ArgumentCount;
+ _pc = other._pc;
+ _firstResume = false;
+ Result = other.Result;
+
+ _stack = DreamValuePool.Rent(other._stack.Length);
+ _localVariables = DreamValuePool.Rent(other._localVariables.Length);
+ Array.Copy(other._localVariables, _localVariables, other._localVariables.Length);
+ }
- var invalid = DMOpcodeHandlers.Invalid;
- var invalidPtr = (delegate*)invalid.Method.MethodHandle.GetFunctionPointer();
+ public void Initialize(DMProc proc, DreamThread thread, int maxStackSize, DreamObject? instance, DreamObject? usr, DreamProcArguments arguments) {
+ base.Initialize(thread, (proc.Attributes & ProcAttributes.DisableWaitfor) != ProcAttributes.DisableWaitfor);
+ _proc = proc;
+ Instance = instance;
+ Usr = usr;
+ ArgumentCount = Math.Max(arguments.Count, _proc.ArgumentNames?.Count ?? 0);
+ _localVariables = DreamValuePool.Rent(256);
+ _stack = DreamValuePool.Rent(maxStackSize);
+ _firstResume = true;
- OpcodeHandlers[0] = invalidPtr;
- for (int i = maxOpcode + 1; i < 256; i++) {
- OpcodeHandlers[i] = invalidPtr;
- }
- }
-
- public DMProcState() { }
-
- private DMProcState(DMProcState other, DreamThread thread) {
- base.Initialize(thread, other.WaitFor);
- _proc = other._proc;
- Instance = other.Instance;
- Usr = other.Usr;
- ArgumentCount = other.ArgumentCount;
- _pc = other._pc;
- _firstResume = false;
- Result = other.Result;
-
- _stack = _dreamValuePool.Rent(other._stack.Length);
- _localVariables = _dreamValuePool.Rent(other._localVariables.Length);
- Array.Copy(other._localVariables, _localVariables, other._localVariables.Length);
+ for (int i = 0; i < ArgumentCount; i++) {
+ _localVariables[i] = arguments.GetArgument(i);
}
+ }
- public void Initialize(DMProc proc, DreamThread thread, int maxStackSize, DreamObject? instance, DreamObject? usr, DreamProcArguments arguments) {
- base.Initialize(thread, (proc.Attributes & ProcAttributes.DisableWaitfor) != ProcAttributes.DisableWaitfor);
- _proc = proc;
- Instance = instance;
- Usr = usr;
- ArgumentCount = Math.Max(arguments.Count, _proc.ArgumentNames?.Count ?? 0);
- _localVariables = _dreamValuePool.Rent(256);
- _stack = _dreamValuePool.Rent(maxStackSize);
- _firstResume = true;
-
- for (int i = 0; i < ArgumentCount; i++) {
- _localVariables[i] = arguments.GetArgument(i);
- }
+ public override unsafe ProcStatus Resume() {
+ if (Instance?.Deleted == true) {
+ return ProcStatus.Returned;
}
- public override unsafe ProcStatus Resume() {
- if (Instance?.Deleted == true) {
- return ProcStatus.Returned;
- }
-
#if TOOLS
if (_firstResume) {
DebugManager.HandleFirstResume(this);
@@ -420,732 +422,734 @@ public override unsafe ProcStatus Resume() {
}
#endif
- var procBytecode = _proc.Bytecode;
+ var procBytecode = _proc.Bytecode;
- if (procBytecode.Length == 0)
- return ProcStatus.Returned;
+ if (procBytecode.Length == 0)
+ return ProcStatus.Returned;
- fixed (delegate** handlers = &OpcodeHandlers[0]) {
- fixed (byte* bytecode = &procBytecode[0]) {
- var l = procBytecode.Length; // The length never changes so we stick it in a register.
+ fixed (delegate** handlers = &OpcodeHandlersTable[0]) {
+ fixed (byte* bytecode = &procBytecode[0]) {
+ var l = procBytecode.Length; // The length never changes so we stick it in a register.
- while (_pc < l) {
+ while (_pc < l) {
#if TOOLS
DebugManager.HandleInstruction(this);
#endif
- int opcode = bytecode[_pc];
- _pc += 1;
+ int opcode = bytecode[_pc];
+ _pc += 1;
- var handler = handlers[opcode];
- var status = handler(this);
+ var handler = handlers[opcode];
+ var status = handler(this);
- if (status != ProcStatus.Continue) {
- return status;
- }
+ if (status != ProcStatus.Continue) {
+ return status;
}
}
}
-
- return ProcStatus.Returned;
}
- public override void ReturnedInto(DreamValue value) {
- Push(value);
- }
-
- public override void AppendStackFrame(StringBuilder builder) {
- if (Proc.OwningType != Proc.ObjectTree.Root) {
- builder.Append(Proc.OwningType);
- builder.Append('/');
- }
+ return ProcStatus.Returned;
+ }
- builder.Append(Proc.Name);
+ public override void ReturnedInto(DreamValue value) {
+ Push(value);
+ }
- // Subtract 1 because _pc may have been advanced to the next line
- var location = Proc.GetSourceAtOffset(_pc - 1);
- builder.Append(' ');
- builder.Append(location.Source);
- builder.Append(':');
- builder.Append(location.Line);
+ public override void AppendStackFrame(StringBuilder builder) {
+ if (Proc.OwningType != Proc.ObjectTree.Root) {
+ builder.Append(Proc.OwningType);
+ builder.Append('/');
}
- public (string, int) GetCurrentSource() {
- return Proc.GetSourceAtOffset(_pc - 1);
- }
+ builder.Append(Proc.Name);
- public void Jump(int position) {
- _pc = position;
- }
+ // Subtract 1 because _pc may have been advanced to the next line
+ var location = Proc.GetSourceAtOffset(_pc - 1);
+ builder.Append(' ');
+ builder.Append(location.Source);
+ builder.Append(':');
+ builder.Append(location.Line);
+ }
- public void SetReturn(DreamValue value) {
- Result = value;
- }
+ public (string, int) GetCurrentSource() {
+ return Proc.GetSourceAtOffset(_pc - 1);
+ }
- public ProcStatus Call(DreamProc proc, DreamObject? src, DreamProcArguments arguments) {
- if (proc is NativeProc p) {
- // Skip a whole song and dance.
- Push(p.Call(Thread, src, Usr, arguments));
- return ProcStatus.Continue;
- }
+ public void Jump(int position) {
+ _pc = position;
+ }
+
+ public void SetReturn(DreamValue value) {
+ Result = value;
+ }
- var state = proc.CreateState(Thread, src, Usr, arguments);
- Thread.PushProcState(state);
- if (proc is AsyncNativeProc) // Hack to ensure sleeping native procs will return our value in a no-waitfor context
- state.Result = Result;
- return ProcStatus.Called;
+ public ProcStatus Call(DreamProc proc, DreamObject? src, DreamProcArguments arguments) {
+ if (proc is NativeProc p) {
+ // Skip a whole song and dance.
+ Push(p.Call(Thread, src, Usr, arguments));
+ return ProcStatus.Continue;
}
- public DreamThread Spawn() {
- var thread = new DreamThread(Proc.ToString());
+ var state = proc.CreateState(Thread, src, Usr, arguments);
+ Thread.PushProcState(state);
+ if (proc is AsyncNativeProc) // Hack to ensure sleeping native procs will return our value in a no-waitfor context
+ state.Result = Result;
+ return ProcStatus.Called;
+ }
- var state = new DMProcState(this, thread);
- thread.PushProcState(state);
+ public DreamThread Spawn() {
+ var thread = new DreamThread(Proc.ToString());
- return thread;
- }
+ var state = new DMProcState(this, thread);
+ thread.PushProcState(state);
- public void StartTryBlock(int catchPosition, int catchVarIndex = NoTryCatchVar) {
- if (catchVarIndex != NoTryCatchVar)
- catchVarIndex += ArgumentCount; // We're given a local var index so we need to account for our arguments
-
- _catchPosition.Push(catchPosition);
- _catchVarIndex.Push(catchVarIndex);
- }
+ return thread;
+ }
- public void EndTryBlock() {
- _catchPosition.Pop();
- _catchVarIndex.Pop();
- }
+ public void StartTryBlock(int catchPosition, int catchVarIndex = NoTryCatchVar) {
+ if (catchVarIndex != NoTryCatchVar)
+ catchVarIndex += ArgumentCount; // We're given a local var index so we need to account for our arguments
- public override bool IsCatching() => _catchPosition.Count > 0;
+ _catchPosition.Push(catchPosition);
+ _catchVarIndex.Push(catchVarIndex);
+ }
- public override void CatchException(Exception exception) {
- if (!IsCatching())
- base.CatchException(exception);
+ public void EndTryBlock() {
+ _catchPosition.Pop();
+ _catchVarIndex.Pop();
+ }
- Jump(_catchPosition.Pop());
- var varIdx = _catchVarIndex.Pop();
- if (varIdx != NoTryCatchVar) {
- DreamValue value;
+ public override bool IsCatching() => _catchPosition.Count > 0;
- if (exception is DMThrowException throwException)
- value = throwException.Value;
- else
- value = new DreamValue(exception.Message); // TODO: Probably need to create an /exception
+ public override void CatchException(Exception exception) {
+ if (!IsCatching())
+ base.CatchException(exception);
- _localVariables[varIdx] = value;
- }
- }
+ Jump(_catchPosition.Pop());
+ var varIdx = _catchVarIndex.Pop();
+ if (varIdx != NoTryCatchVar) {
+ DreamValue value;
- public override void Dispose() {
- base.Dispose();
+ if (exception is DMThrowException throwException)
+ value = throwException.Value;
+ else
+ value = new DreamValue(exception.Message); // TODO: Probably need to create an /exception
- Instance = null;
- Usr = null;
- ArgumentCount = 0;
- Array.Clear(Enumerators);
- _pc = 0;
- _proc = null;
+ _localVariables[varIdx] = value;
+ }
+ }
- _dreamValuePool.Return(_stack);
- _stackIndex = 0;
- _stack = null;
+ public override void Dispose() {
+ base.Dispose();
- _dreamValuePool.Return(_localVariables, true);
- _localVariables = null;
+ Instance = null;
+ Usr = null;
+ ArgumentCount = 0;
+ Array.Clear(Enumerators);
+ _pc = 0;
+ _proc = null!;
- _catchPosition.Clear();
- _catchVarIndex.Clear();
+ DreamValuePool.Return(_stack);
+ _stackIndex = 0;
+ _stack = null!;
- Pool.Push(this);
- }
+ DreamValuePool.Return(_localVariables, true);
+ _localVariables = null!;
- public ReadOnlySpan GetArguments() {
- return _localVariables.AsSpan(0, ArgumentCount);
- }
+ _catchPosition.Clear();
+ _catchVarIndex.Clear();
- public void SetArgument(int id, DreamValue value) {
- if (id < 0 || id >= ArgumentCount)
- throw new IndexOutOfRangeException($"Given argument id ({id}) was out of range");
+ Pool.Push(this);
+ }
- _localVariables[id] = value;
- }
+ public ReadOnlySpan GetArguments() {
+ return _localVariables.AsSpan(0, ArgumentCount);
+ }
- #region Stack
- private DreamValue[] _stack;
- private int _stackIndex = 0;
- public ReadOnlyMemory DebugStack() => _stack.AsMemory(0, _stackIndex);
+ public void SetArgument(int id, DreamValue value) {
+ if (id < 0 || id >= ArgumentCount)
+ throw new IndexOutOfRangeException($"Given argument id ({id}) was out of range");
- public void Push(DreamValue value) {
- _stack[_stackIndex] = value;
- // ++ sucks for the compiler
- _stackIndex += 1;
- }
+ _localVariables[id] = value;
+ }
- public DreamValue Pop() {
- // -- sucks for the compiler
- _stackIndex -= 1;
- return _stack[_stackIndex];
- }
+ #region Stack
- public void PopDrop() {
- DebugTools.Assert(_stackIndex > 0, "Attempted to PopDrop with a stack index of (or below?) 0");
- _stackIndex -= 1;
- }
+ private DreamValue[] _stack = default!;
+ private int _stackIndex;
+ public ReadOnlyMemory DebugStack() => _stack.AsMemory(0, _stackIndex);
- ///
- /// Pops multiple values off the stack
- ///
- /// Amount of values to pop
- /// A ReadOnlySpan of the popped values, in FIFO order
- public ReadOnlySpan PopCount(int count) {
- _stackIndex -= count;
+ public void Push(DreamValue value) {
+ _stack[_stackIndex] = value;
+ // ++ sucks for the compiler
+ _stackIndex += 1;
+ }
- return _stack.AsSpan(_stackIndex, count);
- }
+ public DreamValue Pop() {
+ // -- sucks for the compiler
+ _stackIndex -= 1;
+ return _stack[_stackIndex];
+ }
- public DreamValue Peek() {
- return _stack[_stackIndex - 1];
- }
+ public void PopDrop() {
+ DebugTools.Assert(_stackIndex > 0, "Attempted to PopDrop with a stack index of (or below?) 0");
+ _stackIndex -= 1;
+ }
- ///
- /// Pops arguments off the stack and returns them in DreamProcArguments
- ///
- /// The target proc we're calling. If null, named args or arglist() cannot be used.
- /// The source of the arguments
- /// The amount of items the arguments have on the stack
- /// The arguments in a DreamProcArguments struct
- public DreamProcArguments PopProcArguments(DreamProc? proc, DMCallArgumentsType argumentsType, int argumentStackSize) {
- var values = PopCount(argumentStackSize);
-
- return CreateProcArguments(values, proc, argumentsType, argumentStackSize);
- }
- #endregion
-
- #region Operands
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public int ReadByte() {
- var r = _proc.Bytecode[_pc];
- _pc += 1;
- return r;
- }
+ ///
+ /// Pops multiple values off the stack
+ ///
+ /// Amount of values to pop
+ /// A ReadOnlySpan of the popped values, in FIFO order
+ public ReadOnlySpan PopCount(int count) {
+ _stackIndex -= count;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public int ReadInt() {
- int value = BitConverter.ToInt32(_proc.Bytecode, _pc);
- _pc += 4;
+ return _stack.AsSpan(_stackIndex, count);
+ }
- return value;
- }
+ public DreamValue Peek() {
+ return _stack[_stackIndex - 1];
+ }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public float ReadFloat() {
- float value = BitConverter.ToSingle(_proc.Bytecode, _pc);
- _pc += 4;
+ ///
+ /// Pops arguments off the stack and returns them in DreamProcArguments
+ ///
+ /// The target proc we're calling. If null, named args or arglist() cannot be used.
+ /// The source of the arguments
+ /// The amount of items the arguments have on the stack
+ /// The arguments in a DreamProcArguments struct
+ public DreamProcArguments PopProcArguments(DreamProc? proc, DMCallArgumentsType argumentsType, int argumentStackSize) {
+ var values = PopCount(argumentStackSize);
+
+ return CreateProcArguments(values, proc, argumentsType, argumentStackSize);
+ }
- return value;
- }
+ #endregion
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public string ReadString() {
- int stringId = ReadInt();
+ #region Operands
- return ResolveString(stringId);
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int ReadByte() {
+ var r = _proc.Bytecode[_pc];
+ _pc += 1;
+ return r;
+ }
- public string ResolveString(int stringId) {
- return Proc.ObjectTree.Strings[stringId];
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int ReadInt() {
+ int value = BitConverter.ToInt32(_proc.Bytecode, _pc);
+ _pc += 4;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public DreamReference ReadReference() {
- DMReference.Type refType = (DMReference.Type)ReadByte();
-
- switch (refType) {
- case DMReference.Type.Src:
- case DMReference.Type.Self:
- case DMReference.Type.Usr:
- case DMReference.Type.Args:
- case DMReference.Type.World:
- case DMReference.Type.SuperProc:
- case DMReference.Type.ListIndex:
- return new DreamReference(refType, 0);
- case DMReference.Type.Argument:
- case DMReference.Type.Local:
- return new DreamReference(refType, ReadByte());
- case DMReference.Type.Global:
- case DMReference.Type.GlobalProc:
- case DMReference.Type.Field:
- case DMReference.Type.SrcField:
- case DMReference.Type.SrcProc:
- return new DreamReference(refType, ReadInt());
- default: {
- ThrowInvalidReferenceType(refType);
- return default;
- }
- }
- }
+ return value;
+ }
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowInvalidReferenceType(DMReference.Type type) {
- throw new Exception($"Invalid reference type {type}");
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public float ReadFloat() {
+ float value = BitConverter.ToSingle(_proc.Bytecode, _pc);
+ _pc += 4;
- public (DMCallArgumentsType Type, int StackSize) ReadProcArguments() {
- return ((DMCallArgumentsType) ReadByte(), ReadInt());
- }
+ return value;
+ }
- #endregion
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public string ReadString() {
+ int stringId = ReadInt();
- #region References
+ return ResolveString(stringId);
+ }
- ///
- /// Takes a DMReference with a type and returns the value being indexed
- /// as well as what it's being indexed with.
- ///
- /// A ListIndex DMReference
- public void GetIndexReferenceValues(DreamReference reference, out DreamValue index, out DreamValue indexing, bool peek = false) {
- if (reference.Type != DMReference.Type.ListIndex)
- ThrowReferenceNotListIndex();
+ public string ResolveString(int stringId) {
+ return Proc.ObjectTree.Strings[stringId];
+ }
- index = _stack[_stackIndex - 1];
- indexing = _stack[_stackIndex - 2];
- if (!peek)
- _stackIndex -= 2;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public DreamReference ReadReference() {
+ DMReference.Type refType = (DMReference.Type)ReadByte();
+
+ switch (refType) {
+ case DMReference.Type.Src:
+ case DMReference.Type.Self:
+ case DMReference.Type.Usr:
+ case DMReference.Type.Args:
+ case DMReference.Type.World:
+ case DMReference.Type.SuperProc:
+ case DMReference.Type.ListIndex:
+ return new DreamReference(refType, 0);
+ case DMReference.Type.Argument:
+ case DMReference.Type.Local:
+ return new DreamReference(refType, ReadByte());
+ case DMReference.Type.Global:
+ case DMReference.Type.GlobalProc:
+ case DMReference.Type.Field:
+ case DMReference.Type.SrcField:
+ case DMReference.Type.SrcProc:
+ return new DreamReference(refType, ReadInt());
+ default: {
+ ThrowInvalidReferenceType(refType);
+ return default;
+ }
}
+ }
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowReferenceNotListIndex() {
- throw new ArgumentException("Reference was not a ListIndex type");
- }
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowInvalidReferenceType(DMReference.Type type) {
+ throw new Exception($"Invalid reference type {type}");
+ }
- public void AssignReference(DreamReference reference, DreamValue value) {
- switch (reference.Type) {
- case DMReference.Type.Self: Result = value; break;
- case DMReference.Type.Argument: SetArgument(reference.Value, value); break;
- case DMReference.Type.Local: _localVariables[ArgumentCount + reference.Value] = value; break;
- case DMReference.Type.SrcField: Instance.SetVariable(ResolveString(reference.Value), value); break;
- case DMReference.Type.Global: DreamManager.Globals[reference.Value] = value; break;
- case DMReference.Type.Src:
- //TODO: src can be assigned to non-DreamObject values
- if (!value.TryGetValueAsDreamObject(out Instance)) {
- ThrowCannotAssignSrcTo(value);
- }
+ public (DMCallArgumentsType Type, int StackSize) ReadProcArguments() {
+ return ((DMCallArgumentsType) ReadByte(), ReadInt());
+ }
- break;
- case DMReference.Type.Usr:
- //TODO: usr can be assigned to non-DreamObject values
- if (!value.TryGetValueAsDreamObject(out Usr)) {
- ThrowCannotAssignUsrTo(value);
- }
+ #endregion
+
+ #region References
+
+ ///
+ /// Takes a DMReference with a type and returns the value being indexed
+ /// as well as what it's being indexed with.
+ ///
+ /// A ListIndex DMReference
+ /// What index is being accessed
+ /// What is being indexed
+ /// Peek the stack instead of popping
+ public void GetIndexReferenceValues(DreamReference reference, out DreamValue index, out DreamValue indexing, bool peek = false) {
+ if (reference.Type != DMReference.Type.ListIndex)
+ ThrowReferenceNotListIndex();
+
+ index = _stack[_stackIndex - 1];
+ indexing = _stack[_stackIndex - 2];
+ if (!peek)
+ _stackIndex -= 2;
+ }
- break;
- case DMReference.Type.Field: {
- DreamValue owner = Pop();
- if (!owner.TryGetValueAsDreamObject(out var ownerObj) || ownerObj == null)
- ThrowCannotAssignFieldOn(reference, owner);
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowReferenceNotListIndex() {
+ throw new ArgumentException("Reference was not a ListIndex type");
+ }
- ownerObj!.SetVariable(ResolveString(reference.Value), value);
- break;
+ public void AssignReference(DreamReference reference, DreamValue value) {
+ switch (reference.Type) {
+ case DMReference.Type.Self: Result = value; break;
+ case DMReference.Type.Argument: SetArgument(reference.Value, value); break;
+ case DMReference.Type.Local: _localVariables[ArgumentCount + reference.Value] = value; break;
+ case DMReference.Type.SrcField: Instance.SetVariable(ResolveString(reference.Value), value); break;
+ case DMReference.Type.Global: DreamManager.Globals[reference.Value] = value; break;
+ case DMReference.Type.Src:
+ //TODO: src can be assigned to non-DreamObject values
+ if (!value.TryGetValueAsDreamObject(out Instance)) {
+ ThrowCannotAssignSrcTo(value);
}
- case DMReference.Type.ListIndex: {
- GetIndexReferenceValues(reference, out var index, out var indexing);
- if (indexing.TryGetValueAsDreamObject(out var dreamObject) && dreamObject != null) {
- dreamObject.OperatorIndexAssign(index, this, value);
- } else {
- ThrowCannotAssignListIndex(index, indexing);
- }
+ break;
+ case DMReference.Type.Usr:
+ //TODO: usr can be assigned to non-DreamObject values
+ if (!value.TryGetValueAsDreamObject(out Usr)) {
+ ThrowCannotAssignUsrTo(value);
+ }
+
+ break;
+ case DMReference.Type.Field: {
+ DreamValue owner = Pop();
+ if (!owner.TryGetValueAsDreamObject(out var ownerObj) || ownerObj == null)
+ ThrowCannotAssignFieldOn(reference, owner);
- break;
+ ownerObj!.SetVariable(ResolveString(reference.Value), value);
+ break;
+ }
+ case DMReference.Type.ListIndex: {
+ GetIndexReferenceValues(reference, out var index, out var indexing);
+
+ if (indexing.TryGetValueAsDreamObject(out var dreamObject) && dreamObject != null) {
+ dreamObject.OperatorIndexAssign(index, this, value);
+ } else {
+ ThrowCannotAssignListIndex(index, indexing);
}
- default:
- ThrowCannotAssignReferenceType(reference);
- break;
+
+ break;
}
+ default:
+ ThrowCannotAssignReferenceType(reference);
+ break;
}
+ }
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowCannotAssignReferenceType(DreamReference reference) {
- throw new Exception($"Cannot assign to reference type {reference.Type}");
- }
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowCannotAssignReferenceType(DreamReference reference) {
+ throw new Exception($"Cannot assign to reference type {reference.Type}");
+ }
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowCannotAssignListIndex(DreamValue index, DreamValue indexing) {
- throw new Exception($"Cannot assign to index {index} of {indexing}");
- }
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowCannotAssignListIndex(DreamValue index, DreamValue indexing) {
+ throw new Exception($"Cannot assign to index {index} of {indexing}");
+ }
- [MethodImpl(MethodImplOptions.NoInlining)]
- private void ThrowCannotAssignFieldOn(DreamReference reference, DreamValue owner) {
- throw new Exception($"Cannot assign field \"{ResolveString(reference.Value)}\" on {owner}");
- }
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private void ThrowCannotAssignFieldOn(DreamReference reference, DreamValue owner) {
+ throw new Exception($"Cannot assign field \"{ResolveString(reference.Value)}\" on {owner}");
+ }
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowCannotAssignSrcTo(DreamValue value) {
- throw new Exception($"Cannot assign src to {value}");
- }
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowCannotAssignSrcTo(DreamValue value) {
+ throw new Exception($"Cannot assign src to {value}");
+ }
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowCannotAssignUsrTo(DreamValue value) {
- throw new Exception($"Cannot assign usr to {value}");
- }
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowCannotAssignUsrTo(DreamValue value) {
+ throw new Exception($"Cannot assign usr to {value}");
+ }
- public DreamValue GetReferenceValue(DreamReference reference, bool peek = false) {
- switch (reference.Type) {
- case DMReference.Type.Src: return new(Instance);
- case DMReference.Type.Usr: return new(Usr);
- case DMReference.Type.Self: return Result;
- case DMReference.Type.Global: return DreamManager.Globals[reference.Value];
- case DMReference.Type.Argument: return _localVariables[reference.Value];
- case DMReference.Type.Local: return _localVariables[ArgumentCount + reference.Value];
- case DMReference.Type.Args: return new(new ProcArgsList(Proc.ObjectTree.List.ObjectDefinition, this));
- case DMReference.Type.World: return new(DreamManager.WorldInstance);
- case DMReference.Type.Field: {
- DreamValue owner = peek ? Peek() : Pop();
-
- return DereferenceField(owner, ResolveString(reference.Value));
- }
- case DMReference.Type.SrcField: {
- var fieldName = ResolveString(reference.Value);
- if (Instance == null)
- ThrowCannotGetFieldSrcGlobalProc(fieldName);
- if (!Instance!.TryGetVariable(fieldName, out var fieldValue))
- ThrowTypeHasNoField(fieldName);
-
- return fieldValue;
- }
- case DMReference.Type.ListIndex: {
- GetIndexReferenceValues(reference, out var index, out var indexing, peek);
+ public DreamValue GetReferenceValue(DreamReference reference, bool peek = false) {
+ switch (reference.Type) {
+ case DMReference.Type.Src: return new(Instance);
+ case DMReference.Type.Usr: return new(Usr);
+ case DMReference.Type.Self: return Result;
+ case DMReference.Type.Global: return DreamManager.Globals[reference.Value];
+ case DMReference.Type.Argument: return _localVariables[reference.Value];
+ case DMReference.Type.Local: return _localVariables[ArgumentCount + reference.Value];
+ case DMReference.Type.Args: return new(new ProcArgsList(Proc.ObjectTree.List.ObjectDefinition, this));
+ case DMReference.Type.World: return new(DreamManager.WorldInstance);
+ case DMReference.Type.Field: {
+ DreamValue owner = peek ? Peek() : Pop();
+
+ return DereferenceField(owner, ResolveString(reference.Value));
+ }
+ case DMReference.Type.SrcField: {
+ var fieldName = ResolveString(reference.Value);
+ if (Instance == null)
+ ThrowCannotGetFieldSrcGlobalProc(fieldName);
+ if (!Instance!.TryGetVariable(fieldName, out var fieldValue))
+ ThrowTypeHasNoField(fieldName);
- return GetIndex(indexing, index, this);
- }
- default:
- ThrowCannotGetValueOfReferenceType(reference);
- return DreamValue.Null;
+ return fieldValue;
}
- }
+ case DMReference.Type.ListIndex: {
+ GetIndexReferenceValues(reference, out var index, out var indexing, peek);
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowCannotGetValueOfReferenceType(DreamReference reference) {
- throw new Exception($"Cannot get value of reference type {reference.Type}");
+ return GetIndex(indexing, index, this);
+ }
+ default:
+ ThrowCannotGetValueOfReferenceType(reference);
+ return DreamValue.Null;
}
+ }
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowCannotGetFieldSrcGlobalProc(string fieldName) {
- throw new Exception($"Cannot get field src.{fieldName} in global proc");
- }
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowCannotGetValueOfReferenceType(DreamReference reference) {
+ throw new Exception($"Cannot get value of reference type {reference.Type}");
+ }
- [MethodImpl(MethodImplOptions.NoInlining)]
- private void ThrowTypeHasNoField(string fieldName) {
- throw new Exception($"Type {Instance!.ObjectDefinition.Type} has no field called \"{fieldName}\"");
- }
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowCannotGetFieldSrcGlobalProc(string fieldName) {
+ throw new Exception($"Cannot get field src.{fieldName} in global proc");
+ }
- public void PopReference(DreamReference reference) {
- switch (reference.Type) {
- case DMReference.Type.Src:
- case DMReference.Type.Usr:
- case DMReference.Type.Self:
- case DMReference.Type.Global:
- case DMReference.Type.GlobalProc:
- case DMReference.Type.Argument:
- case DMReference.Type.Local:
- case DMReference.Type.Args:
- case DMReference.Type.SrcField:
- return;
- case DMReference.Type.ListIndex:
- PopDrop();
-
- // Fallthrough to the below case ends up with more performant generated code
- goto case DMReference.Type.Field;
- case DMReference.Type.Field:
- PopDrop();
- return;
- default: ThrowPopInvalidType(reference.Type);
- return;
- }
- }
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private void ThrowTypeHasNoField(string fieldName) {
+ throw new Exception($"Type {Instance!.ObjectDefinition.Type} has no field called \"{fieldName}\"");
+ }
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowPopInvalidType(DMReference.Type type) {
- throw new Exception($"Cannot pop stack values of reference type {type}");
+ public void PopReference(DreamReference reference) {
+ switch (reference.Type) {
+ case DMReference.Type.Src:
+ case DMReference.Type.Usr:
+ case DMReference.Type.Self:
+ case DMReference.Type.Global:
+ case DMReference.Type.GlobalProc:
+ case DMReference.Type.Argument:
+ case DMReference.Type.Local:
+ case DMReference.Type.Args:
+ case DMReference.Type.SrcField:
+ return;
+ case DMReference.Type.ListIndex:
+ PopDrop();
+
+ // Fallthrough to the below case ends up with more performant generated code
+ goto case DMReference.Type.Field;
+ case DMReference.Type.Field:
+ PopDrop();
+ return;
+ default: ThrowPopInvalidType(reference.Type);
+ return;
}
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowPopInvalidType(DMReference.Type type) {
+ throw new Exception($"Cannot pop stack values of reference type {type}");
+ }
- public DreamValue DereferenceField(DreamValue owner, string field) {
- if (owner.TryGetValueAsDreamObject(out var ownerObj) && ownerObj != null) {
- if (!ownerObj.TryGetVariable(field, out var fieldValue))
- ThrowTypeHasNoField(field, ownerObj);
+ public DreamValue DereferenceField(DreamValue owner, string field) {
+ if (owner.TryGetValueAsDreamObject(out var ownerObj)) {
+ if (!ownerObj.TryGetVariable(field, out var fieldValue))
+ ThrowTypeHasNoField(field, ownerObj);
- return fieldValue;
- } else if (owner.TryGetValueAsProc(out var ownerProc)) {
- return ownerProc.GetField(field);
- } else if (owner.TryGetValueAsAppearance(out var appearance)) {
- if (!Proc.AtomManager.IsValidAppearanceVar(field))
- ThrowInvalidAppearanceVar(field);
-
- return Proc.AtomManager.GetAppearanceVar(appearance, field);
- } else if (owner.TryGetValueAsType(out var ownerType) && ownerType.ObjectDefinition.Variables.TryGetValue(field, out var val)) {
- return val; // equivalent to initial()
- }
+ return fieldValue;
+ } else if (owner.TryGetValueAsProc(out var ownerProc)) {
+ return ownerProc.GetField(field);
+ } else if (owner.TryGetValueAsAppearance(out var appearance)) {
+ if (!Proc.AtomManager.IsValidAppearanceVar(field))
+ ThrowInvalidAppearanceVar(field);
- ThrowCannotGetFieldFromOwner(owner, field);
- return DreamValue.Null;
+ return Proc.AtomManager.GetAppearanceVar(appearance, field);
+ } else if (owner.TryGetValueAsType(out var ownerType) && ownerType.ObjectDefinition.Variables.TryGetValue(field, out var val)) {
+ return val; // equivalent to initial()
}
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowCannotGetFieldFromOwner(DreamValue owner, string field) {
- throw new Exception($"Cannot get field \"{field}\" from {owner}");
- }
+ ThrowCannotGetFieldFromOwner(owner, field);
+ return DreamValue.Null;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowCannotGetFieldFromOwner(DreamValue owner, string field) {
+ throw new Exception($"Cannot get field \"{field}\" from {owner}");
+ }
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowInvalidAppearanceVar(string field) {
- throw new Exception($"Invalid appearance var \"{field}\"");
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowInvalidAppearanceVar(string field) {
+ throw new Exception($"Invalid appearance var \"{field}\"");
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowTypeHasNoField(string field, DreamObject ownerObj) {
+ throw new Exception($"Type {ownerObj.ObjectDefinition.Type} has no field called \"{field}\"");
+ }
+
+ public DreamValue GetIndex(DreamValue indexing, DreamValue index, DMProcState state) {
+ if (indexing.TryGetValueAsDreamList(out var listObj)) {
+ return listObj.GetValue(index);
}
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowTypeHasNoField(string field, DreamObject? ownerObj) {
- throw new Exception($"Type {ownerObj.ObjectDefinition.Type} has no field called \"{field}\"");
+ if (indexing.TryGetValueAsString(out string? strValue)) {
+ if (!index.TryGetValueAsInteger(out int strIndex))
+ ThrowAttemptedToIndexString(index);
+
+ char c = strValue[strIndex - 1];
+ return new DreamValue(Convert.ToString(c));
}
- public DreamValue GetIndex(DreamValue indexing, DreamValue index, DMProcState state) {
- if (indexing.TryGetValueAsDreamList(out var listObj)) {
- return listObj.GetValue(index);
+ if (indexing.TryGetValueAsDreamObject(out var dreamObject)) {
+ if (dreamObject != null) {
+ return dreamObject.OperatorIndex(index, state);
}
+ }
- if (indexing.TryGetValueAsString(out string? strValue)) {
- if (!index.TryGetValueAsInteger(out int strIndex))
- ThrowAttemptedToIndexString(index);
+ ThrowCannotGetIndex(indexing, index);
+ return default;
+ }
- char c = strValue[strIndex - 1];
- return new DreamValue(Convert.ToString(c));
- }
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowCannotGetIndex(DreamValue indexing, DreamValue index) {
+ throw new Exception($"Cannot get index {index} of {indexing}");
+ }
- if (indexing.TryGetValueAsDreamObject(out var dreamObject)) {
- if (dreamObject != null) {
- return dreamObject.OperatorIndex(index, state);
- }
- }
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowAttemptedToIndexString(DreamValue index) {
+ throw new Exception($"Attempted to index string with {index}");
+ }
- ThrowCannotGetIndex(indexing, index);
- return default;
- }
+ #endregion References
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowCannotGetIndex(DreamValue indexing, DreamValue index) {
- throw new Exception($"Cannot get index {index} of {indexing}");
+ public IEnumerable<(string, DreamValue)> DebugArguments() {
+ int i = 0;
+ if (_proc.ArgumentNames != null) {
+ while (i < _proc.ArgumentNames.Count) {
+ yield return (_proc.ArgumentNames[i], _localVariables[i]);
+ ++i;
+ }
}
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowAttemptedToIndexString(DreamValue index) {
- throw new Exception($"Attempted to index string with {index}");
+ // If the caller supplied excess positional arguments, they have no
+ // name, but the debugger should report them anyways.
+ while (i < ArgumentCount) {
+ yield return (i.ToString(), _localVariables[i]);
+ ++i;
}
+ }
- #endregion References
-
- public IEnumerable<(string, DreamValue)> DebugArguments() {
- int i = 0;
- if (_proc.ArgumentNames != null) {
- while (i < _proc.ArgumentNames.Count) {
- yield return (_proc.ArgumentNames[i], _localVariables[i]);
- ++i;
- }
+ public IEnumerable<(string, DreamValue)> DebugLocals() {
+ string[] names = new string[_localVariables.Length - ArgumentCount];
+ int count = 0;
+ foreach (var info in _proc.LocalNames) {
+ if (info.Offset > _pc) {
+ break;
}
- // If the caller supplied excess positional arguments, they have no
- // name, but the debugger should report them anyways.
- while (i < ArgumentCount) {
- yield return (i.ToString(), _localVariables[i]);
- ++i;
+
+ if (info.Remove is { } remove) {
+ count -= remove;
}
- }
- public IEnumerable<(string, DreamValue)> DebugLocals() {
- if (_proc.LocalNames is null) {
- yield break;
+ if (info.Add is { } add) {
+ names[count++] = add;
}
+ }
- string[] names = new string[_localVariables.Length - ArgumentCount];
- int count = 0;
- foreach (var info in _proc.LocalNames) {
- if (info.Offset > _pc) {
- break;
- }
+ int i = 0, j = ArgumentCount;
+ while (i < count && j < _localVariables.Length) {
+ yield return (names[i], _localVariables[j]);
+ ++i;
+ ++j;
+ }
+ // _localVariables.Length is pool-allocated so its length may go up
+ // to some round power of two or similar without anything actually
+ // being there, so just stop after the named locals.
+ }
- if (info.Remove is { } remove) {
- count -= remove;
- }
+ public DreamProcArguments CreateProcArguments(ReadOnlySpan values, DreamProc? proc, DMCallArgumentsType argumentsType, int argumentStackSize) {
+ switch (argumentsType) {
+ case DMCallArgumentsType.None:
+ return new DreamProcArguments();
+ case DMCallArgumentsType.FromStack:
+ return new DreamProcArguments(values);
+ case DMCallArgumentsType.FromProcArguments:
+ return new DreamProcArguments(GetArguments());
+ case DMCallArgumentsType.FromStackKeyed: {
+ if (argumentStackSize % 2 != 0)
+ throw new ArgumentException("Argument stack size must be even", nameof(argumentStackSize));
+ if (proc == null)
+ throw new Exception("Cannot use named arguments here");
+
+ // new /mutable_appearance(...) always uses /image/New()'s arguments, despite any overrides
+ if (proc.OwningType == Proc.ObjectTree.MutableAppearance && proc.Name == "New")
+ proc = Proc.DreamManager.ImageConstructor;
+
+ var argumentCount = argumentStackSize / 2;
+ var arguments = new DreamValue[Math.Max(argumentCount, proc.ArgumentNames.Count)];
+ var skippingArg = false;
+ var isImageConstructor = proc == Proc.DreamManager.ImageConstructor ||
+ proc == Proc.DreamManager.ImageFactoryProc;
+
+ Array.Fill(arguments, DreamValue.Null);
+ for (int i = 0; i < argumentCount; i++) {
+ var key = values[i*2];
+ var value = values[i*2+1];
+
+ if (key.IsNull) {
+ // image() or new /image() will skip the loc arg if the second arg is a string
+ // Really don't like this but it's BYOND behavior
+ // Note that the way we're doing it leads to different argument placement when there are no named args
+ // Hopefully nothing depends on that though
+ // TODO: We aim to do sanity improvements in the future, yea? Big one here
+ if (isImageConstructor && i == 1 && value.Type == DreamValue.DreamValueType.String)
+ skippingArg = true;
+
+ arguments[skippingArg ? i + 1 : i] = value;
+ } else {
+ string argumentName = key.MustGetValueAsString();
+ int argumentIndex = proc.ArgumentNames.IndexOf(argumentName);
+ if (argumentIndex == -1)
+ throw new Exception($"{proc} has no argument named {argumentName}");
- if (info.Add is { } add) {
- names[count++] = add;
+ arguments[argumentIndex] = value;
+ }
}
- }
- int i = 0, j = ArgumentCount;
- while (i < count && j < _localVariables.Length) {
- yield return (names[i], _localVariables[j]);
- ++i;
- ++j;
+ return new DreamProcArguments(arguments);
}
- // _localVariables.Length is pool-allocated so its length may go up
- // to some round power of two or similar without anything actually
- // being there, so just stop after the named locals.
- }
-
- public DreamProcArguments CreateProcArguments(ReadOnlySpan values, DreamProc? proc, DMCallArgumentsType argumentsType, int argumentStackSize) {
- switch (argumentsType) {
- case DMCallArgumentsType.None:
- return new DreamProcArguments();
- case DMCallArgumentsType.FromStack:
- return new DreamProcArguments(values);
- case DMCallArgumentsType.FromProcArguments:
- return new DreamProcArguments(GetArguments());
- case DMCallArgumentsType.FromStackKeyed: {
- if (argumentStackSize % 2 != 0)
- throw new ArgumentException("Argument stack size must be even", nameof(argumentStackSize));
- if (proc == null)
- throw new Exception("Cannot use named arguments here");
-
- // new /mutable_appearance(...) always uses /image/New()'s arguments, despite any overrides
- if (proc.OwningType == Proc.ObjectTree.MutableAppearance && proc.Name == "New")
- proc = Proc.DreamManager.ImageConstructor;
-
- var argumentCount = argumentStackSize / 2;
- var arguments = new DreamValue[Math.Max(argumentCount, proc.ArgumentNames.Count)];
- var skippingArg = false;
- var isImageConstructor = proc == Proc.DreamManager.ImageConstructor ||
- proc == Proc.DreamManager.ImageFactoryProc;
-
- Array.Fill(arguments, DreamValue.Null);
- for (int i = 0; i < argumentCount; i++) {
- var key = values[i*2];
- var value = values[i*2+1];
-
- if (key.IsNull) {
- // image() or new /image() will skip the loc arg if the second arg is a string
- // Really don't like this but it's BYOND behavior
- // Note that the way we're doing it leads to different argument placement when there are no named args
- // Hopefully nothing depends on that though
- // TODO: We aim to do sanity improvements in the future, yea? Big one here
- if (isImageConstructor && i == 1 && value.Type == DreamValue.DreamValueType.String)
- skippingArg = true;
-
- arguments[skippingArg ? i + 1 : i] = value;
- } else {
- string argumentName = key.MustGetValueAsString();
- int argumentIndex = proc.ArgumentNames.IndexOf(argumentName);
- if (argumentIndex == -1)
- throw new Exception($"{proc} has no argument named {argumentName}");
-
- arguments[argumentIndex] = value;
- }
+ case DMCallArgumentsType.FromArgumentList: {
+ if (proc == null)
+ throw new Exception("Cannot use an arglist here");
+ if (!values[0].TryGetValueAsDreamList(out var argList))
+ return new DreamProcArguments(); // Using a non-list gives you no arguments
+
+ // new /mutable_appearance(...) always uses /image/New()'s arguments, despite any overrides
+ if (proc.OwningType == Proc.ObjectTree.MutableAppearance && proc.Name == "New")
+ proc = Proc.DreamManager.ImageConstructor;
+
+ var listValues = argList.GetValues();
+ var arguments = new DreamValue[Math.Max(listValues.Count, proc.ArgumentNames.Count)];
+ var skippingArg = false;
+ var isImageConstructor = proc == Proc.DreamManager.ImageConstructor ||
+ proc == Proc.DreamManager.ImageFactoryProc;
+
+ Array.Fill(arguments, DreamValue.Null);
+ for (int i = 0; i < listValues.Count; i++) {
+ var value = listValues[i];
+
+ if (argList.ContainsKey(value)) { //Named argument
+ if (!value.TryGetValueAsString(out var argumentName))
+ throw new Exception("List contains a non-string key, and cannot be used as an arglist");
+
+ int argumentIndex = proc.ArgumentNames.IndexOf(argumentName);
+ if (argumentIndex == -1)
+ throw new Exception($"{proc} has no argument named {argumentName}");
+
+ arguments[argumentIndex] = argList.GetValue(value);
+ } else { //Ordered argument
+ // image() or new /image() will skip the loc arg if the second arg is a string
+ // Really don't like this but it's BYOND behavior
+ // Note that the way we're doing it leads to different argument placement when there are no named args
+ // Hopefully nothing depends on that though
+ if (isImageConstructor && i == 1 && value.Type == DreamValue.DreamValueType.String)
+ skippingArg = true;
+
+ // TODO: Verify ordered args precede all named args
+ arguments[skippingArg ? i + 1 : i] = value;
}
-
- return new DreamProcArguments(arguments);
}
- case DMCallArgumentsType.FromArgumentList: {
- if (proc == null)
- throw new Exception("Cannot use an arglist here");
- if (!values[0].TryGetValueAsDreamList(out var argList))
- return new DreamProcArguments(); // Using a non-list gives you no arguments
-
- // new /mutable_appearance(...) always uses /image/New()'s arguments, despite any overrides
- if (proc.OwningType == Proc.ObjectTree.MutableAppearance && proc.Name == "New")
- proc = Proc.DreamManager.ImageConstructor;
-
- var listValues = argList.GetValues();
- var arguments = new DreamValue[Math.Max(listValues.Count, proc.ArgumentNames.Count)];
- var skippingArg = false;
- var isImageConstructor = proc == Proc.DreamManager.ImageConstructor ||
- proc == Proc.DreamManager.ImageFactoryProc;
-
- Array.Fill(arguments, DreamValue.Null);
- for (int i = 0; i < listValues.Count; i++) {
- var value = listValues[i];
-
- if (argList.ContainsKey(value)) { //Named argument
- if (!value.TryGetValueAsString(out var argumentName))
- throw new Exception("List contains a non-string key, and cannot be used as an arglist");
-
- int argumentIndex = proc.ArgumentNames.IndexOf(argumentName);
- if (argumentIndex == -1)
- throw new Exception($"{proc} has no argument named {argumentName}");
-
- arguments[argumentIndex] = argList.GetValue(value);
- } else { //Ordered argument
- // image() or new /image() will skip the loc arg if the second arg is a string
- // Really don't like this but it's BYOND behavior
- // Note that the way we're doing it leads to different argument placement when there are no named args
- // Hopefully nothing depends on that though
- if (isImageConstructor && i == 1 && value.Type == DreamValue.DreamValueType.String)
- skippingArg = true;
-
- // TODO: Verify ordered args precede all named args
- arguments[skippingArg ? i + 1 : i] = value;
- }
- }
- return new DreamProcArguments(arguments);
- }
- default:
- throw new Exception($"Invalid arguments type {argumentsType}");
+ return new DreamProcArguments(arguments);
}
+ default:
+ throw new Exception($"Invalid arguments type {argumentsType}");
}
+ }
- public (DreamValue[]?, Dictionary?) CollectProcArguments(ReadOnlySpan values, DMCallArgumentsType argumentsType, int argumentStackSize) {
- switch (argumentsType) {
- case DMCallArgumentsType.None:
- return (Array.Empty(), null);
- case DMCallArgumentsType.FromStack:
- return (values.ToArray(), null);
- case DMCallArgumentsType.FromProcArguments:
- return (GetArguments().ToArray(), null);
- case DMCallArgumentsType.FromStackKeyed: {
- if (argumentStackSize % 2 != 0)
- throw new ArgumentException("Argument stack size must be even", nameof(argumentStackSize));
-
- var argumentCount = argumentStackSize / 2;
- var arguments = new Dictionary(argumentCount);
-
- for (int i = 0; i < argumentCount; i++) {
- var key = values[i*2];
- var value = values[i*2+1];
-
- if (key.IsNull) {
- arguments[new(i + 1)] = value;
- } else {
- string argumentName = key.MustGetValueAsString();
-
- arguments[new(argumentName)] = value;
- }
- }
+ public (DreamValue[]?, Dictionary?) CollectProcArguments(ReadOnlySpan values, DMCallArgumentsType argumentsType, int argumentStackSize) {
+ switch (argumentsType) {
+ case DMCallArgumentsType.None:
+ return (Array.Empty(), null);
+ case DMCallArgumentsType.FromStack:
+ return (values.ToArray(), null);
+ case DMCallArgumentsType.FromProcArguments:
+ return (GetArguments().ToArray(), null);
+ case DMCallArgumentsType.FromStackKeyed: {
+ if (argumentStackSize % 2 != 0)
+ throw new ArgumentException("Argument stack size must be even", nameof(argumentStackSize));
+
+ var argumentCount = argumentStackSize / 2;
+ var arguments = new Dictionary(argumentCount);
+
+ for (int i = 0; i < argumentCount; i++) {
+ var key = values[i*2];
+ var value = values[i*2+1];
+
+ if (key.IsNull) {
+ arguments[new(i + 1)] = value;
+ } else {
+ string argumentName = key.MustGetValueAsString();
- return (null, arguments);
+ arguments[new(argumentName)] = value;
+ }
}
- case DMCallArgumentsType.FromArgumentList: {
- if (!values[0].TryGetValueAsDreamList(out var argList))
- return (Array.Empty(), null); // Using a non-list gives you no arguments
- var listValues = argList.GetValues();
- var arguments = new Dictionary();
+ return (null, arguments);
+ }
+ case DMCallArgumentsType.FromArgumentList: {
+ if (!values[0].TryGetValueAsDreamList(out var argList))
+ return (Array.Empty(), null); // Using a non-list gives you no arguments
+
+ var listValues = argList.GetValues();
+ var arguments = new Dictionary();
- for (int i = 0; i < listValues.Count; i++) {
- var value = listValues[i];
+ for (int i = 0; i < listValues.Count; i++) {
+ var value = listValues[i];
- if (argList.ContainsKey(value)) { //Named argument
- if (!value.TryGetValueAsString(out var argumentName))
- throw new Exception("List contains a non-string key, and cannot be used as an arglist");
+ if (argList.ContainsKey(value)) { //Named argument
+ if (!value.TryGetValueAsString(out var argumentName))
+ throw new Exception("List contains a non-string key, and cannot be used as an arglist");
- arguments[new(argumentName)] = argList.GetValue(value);
- } else { //Ordered argument
- // TODO: Verify ordered args precede all named args
- arguments[new(i + 1)] = value;
- }
+ arguments[new(argumentName)] = argList.GetValue(value);
+ } else { //Ordered argument
+ // TODO: Verify ordered args precede all named args
+ arguments[new(i + 1)] = value;
}
-
- return (null, arguments);
}
- default:
- throw new Exception($"Invalid arguments type {argumentsType}");
+
+ return (null, arguments);
}
+ default:
+ throw new Exception($"Invalid arguments type {argumentsType}");
}
}
}
diff --git a/OpenDreamRuntime/Procs/DebugAdapter/DreamDebugManager.cs b/OpenDreamRuntime/Procs/DebugAdapter/DreamDebugManager.cs
index 56339a2316..8f747f90bf 100644
--- a/OpenDreamRuntime/Procs/DebugAdapter/DreamDebugManager.cs
+++ b/OpenDreamRuntime/Procs/DebugAdapter/DreamDebugManager.cs
@@ -238,7 +238,7 @@ public ProcStatus HandleBreakpoint(DMProcState state) {
// Execute the original opcode
unsafe {
- return DMProcState.OpcodeHandlers[breakpoint.OriginalOpcode](state);
+ return DMProcState.OpcodeHandlersTable[breakpoint.OriginalOpcode](state);
}
}
diff --git a/OpenDreamRuntime/Procs/DreamEnumerators.cs b/OpenDreamRuntime/Procs/DreamEnumerators.cs
index 6cd5583e31..b9da196424 100644
--- a/OpenDreamRuntime/Procs/DreamEnumerators.cs
+++ b/OpenDreamRuntime/Procs/DreamEnumerators.cs
@@ -11,21 +11,13 @@ public interface IDreamValueEnumerator {
/// Enumerates a range of numbers with a given step
/// for (var/i in 1 to 10 step 2)
///
-internal sealed class DreamValueRangeEnumerator : IDreamValueEnumerator {
- private float _current;
- private readonly float _end;
- private readonly float _step;
-
- public DreamValueRangeEnumerator(float rangeStart, float rangeEnd, float step) {
- _current = rangeStart - step;
- _end = rangeEnd;
- _step = step;
- }
+internal sealed class DreamValueRangeEnumerator(float rangeStart, float rangeEnd, float step) : IDreamValueEnumerator {
+ private float _current = rangeStart - step;
public bool Enumerate(DMProcState state, DreamReference? reference) {
- _current += _step;
+ _current += step;
- bool successful = (_step > 0) ? _current <= _end : _current >= _end;
+ bool successful = (step > 0) ? _current <= rangeEnd : _current >= rangeEnd;
if (successful && reference != null) // Only assign if it was successful
state.AssignReference(reference.Value, new DreamValue(_current));
@@ -36,14 +28,8 @@ public bool Enumerate(DMProcState state, DreamReference? reference) {
///
/// Enumerates over an IEnumerable of DreamObjects, possibly filtering for a certain type
///
-internal sealed class DreamObjectEnumerator : IDreamValueEnumerator {
- private readonly IEnumerator _dreamObjectEnumerator;
- private readonly TreeEntry? _filterType;
-
- public DreamObjectEnumerator(IEnumerable dreamObjects, TreeEntry? filterType = null) {
- _dreamObjectEnumerator = dreamObjects.GetEnumerator();
- _filterType = filterType;
- }
+internal sealed class DreamObjectEnumerator(IEnumerable dreamObjects, TreeEntry? filterType = null) : IDreamValueEnumerator {
+ private readonly IEnumerator _dreamObjectEnumerator = dreamObjects.GetEnumerator();
public bool Enumerate(DMProcState state, DreamReference? reference) {
bool success = _dreamObjectEnumerator.MoveNext();
@@ -51,8 +37,8 @@ public bool Enumerate(DMProcState state, DreamReference? reference) {
while(success && _dreamObjectEnumerator.Current.Deleted) //skip over deleted
success = _dreamObjectEnumerator.MoveNext();
- if (_filterType != null) {
- while (success && (_dreamObjectEnumerator.Current.Deleted || !_dreamObjectEnumerator.Current.IsSubtypeOf(_filterType))) {
+ if (filterType != null) {
+ while (success && (_dreamObjectEnumerator.Current.Deleted || !_dreamObjectEnumerator.Current.IsSubtypeOf(filterType))) {
success = _dreamObjectEnumerator.MoveNext();
}
}
@@ -68,20 +54,15 @@ public bool Enumerate(DMProcState state, DreamReference? reference) {
/// Enumerates over an array of DreamValues
/// for (var/i in list(1, 2, 3))
///
-internal sealed class DreamValueArrayEnumerator : IDreamValueEnumerator {
- private readonly DreamValue[] _dreamValueArray;
+internal sealed class DreamValueArrayEnumerator(DreamValue[] dreamValueArray) : IDreamValueEnumerator {
private int _current = -1;
- public DreamValueArrayEnumerator(DreamValue[] dreamValueArray) {
- _dreamValueArray = dreamValueArray;
- }
-
public bool Enumerate(DMProcState state, DreamReference? reference) {
_current++;
- bool success = _current < _dreamValueArray.Length;
+ bool success = _current < dreamValueArray.Length;
if (reference != null)
- state.AssignReference(reference.Value, success ? _dreamValueArray[_current] : DreamValue.Null); // Assign regardless of success
+ state.AssignReference(reference.Value, success ? dreamValueArray[_current] : DreamValue.Null); // Assign regardless of success
return success;
}
}
@@ -90,27 +71,20 @@ public bool Enumerate(DMProcState state, DreamReference? reference) {
/// Enumerates over an array of DreamValues, filtering for a certain type
/// for (var/obj/item/I in contents)
///
-internal sealed class FilteredDreamValueArrayEnumerator : IDreamValueEnumerator {
- private readonly DreamValue[] _dreamValueArray;
- private readonly TreeEntry _filterType;
+internal sealed class FilteredDreamValueArrayEnumerator(DreamValue[] dreamValueArray, TreeEntry filterType) : IDreamValueEnumerator {
private int _current = -1;
- public FilteredDreamValueArrayEnumerator(DreamValue[] dreamValueArray, TreeEntry filterType) {
- _dreamValueArray = dreamValueArray;
- _filterType = filterType;
- }
-
public bool Enumerate(DMProcState state, DreamReference? reference) {
do {
_current++;
- if (_current >= _dreamValueArray.Length) {
+ if (_current >= dreamValueArray.Length) {
if (reference != null)
state.AssignReference(reference.Value, DreamValue.Null);
return false;
}
- DreamValue value = _dreamValueArray[_current];
- if (value.TryGetValueAsDreamObject(out var dreamObject) && (dreamObject?.IsSubtypeOf(_filterType) ?? false)) {
+ DreamValue value = dreamValueArray[_current];
+ if (value.TryGetValueAsDreamObject(out var dreamObject) && (dreamObject?.IsSubtypeOf(filterType) ?? false)) {
if (reference != null)
state.AssignReference(reference.Value, value);
return true;
@@ -123,12 +97,8 @@ public bool Enumerate(DMProcState state, DreamReference? reference) {
/// Enumerates over all atoms in the world, possibly filtering for a certain type
/// for (var/obj/item/I in world)
///
-internal sealed class WorldContentsEnumerator : IDreamValueEnumerator {
- private readonly IEnumerator _enumerator;
-
- public WorldContentsEnumerator(AtomManager atomManager, TreeEntry? filterType) {
- _enumerator = atomManager.EnumerateAtoms(filterType).GetEnumerator();
- }
+internal sealed class WorldContentsEnumerator(AtomManager atomManager, TreeEntry? filterType) : IDreamValueEnumerator {
+ private readonly IEnumerator _enumerator = atomManager.EnumerateAtoms(filterType).GetEnumerator();
public bool Enumerate(DMProcState state, DreamReference? reference) {
bool success = _enumerator.MoveNext();