From 6f903f4fab0c84fa1137911d52fe9532462dca12 Mon Sep 17 00:00:00 2001 From: Romayne <27376947+MeggalBozale@users.noreply.github.com> Date: Thu, 8 Aug 2024 22:47:45 -0400 Subject: [PATCH 1/9] Terrible starting code, but a start to an unfamiliar land --- DMCompiler/Bytecode/DreamProcOpcode.cs | 3 +- DMCompiler/DM/Builders/DMProcBuilder.cs | 5 ++-- DMCompiler/DM/DMProc.cs | 10 +++++++ .../Optimizer/AnnotatedByteCodeWriter.cs | 19 ++++++++++++ DMCompiler/Optimizer/AnnotatedBytecode.cs | 29 +++++++++++++++++++ OpenDreamRuntime/Procs/DMOpcodeHandlers.cs | 10 +++++++ OpenDreamRuntime/Procs/DMProc.cs | 1 + OpenDreamRuntime/Procs/ProcDecoder.cs | 7 +++++ 8 files changed, 81 insertions(+), 3 deletions(-) diff --git a/DMCompiler/Bytecode/DreamProcOpcode.cs b/DMCompiler/Bytecode/DreamProcOpcode.cs index e73e513fe8..811776e899 100644 --- a/DMCompiler/Bytecode/DreamProcOpcode.cs +++ b/DMCompiler/Bytecode/DreamProcOpcode.cs @@ -137,7 +137,8 @@ public enum DreamProcOpcode : byte { CreateFilteredListEnumerator = 0x41, [OpcodeMetadata(-1)] Power = 0x42, - //0x43, + [OpcodeMetadata(-1, OpcodeArgType.FilterId)] + CreateFilteredBaseTypesListEnumerator = 0x43, //0x44 [OpcodeMetadata(-3, OpcodeArgType.TypeId)] Prompt = 0x45, diff --git a/DMCompiler/DM/Builders/DMProcBuilder.cs b/DMCompiler/DM/Builders/DMProcBuilder.cs index a52cd9265f..1b11a27362 100644 --- a/DMCompiler/DM/Builders/DMProcBuilder.cs +++ b/DMCompiler/DM/Builders/DMProcBuilder.cs @@ -612,8 +612,9 @@ public void ProcessStatementForList(DMExpression list, DMExpression outputVar, D implicitTypeCheck = dmTypes.Value.TypePath; } else if (!dmTypes.Value.IsAnything) { // "as anything" performs no check. Other values are unimplemented. - DMCompiler.UnimplementedWarning(outputVar.Location, - $"As type {dmTypes} in for loops is unimplemented. No type check will be performed."); + proc.CreateFilteredBaseTypesListEnumerator(dmTypes.Value.Type); + //DMCompiler.UnimplementedWarning(outputVar.Location, + // $"As type {dmTypes} in for loops is unimplemented. No type check will be performed."); } list.EmitPushValue(dmObject, proc); diff --git a/DMCompiler/DM/DMProc.cs b/DMCompiler/DM/DMProc.cs index 8c1e5be54d..28136d594a 100644 --- a/DMCompiler/DM/DMProc.cs +++ b/DMCompiler/DM/DMProc.cs @@ -372,6 +372,12 @@ public void CreateFilteredListEnumerator(int filterTypeId, DreamPath filterType) WriteFilterID(filterTypeId, filterType); } + public void CreateFilteredBaseTypesListEnumerator(DMValueType filterType) { + WriteOpcode(DreamProcOpcode.CreateFilteredBaseTypesListEnumerator); + WriteEnumeratorId(_enumeratorIdCounter++); + WriteFilterTypeID(filterType); + } + public void CreateTypeEnumerator() { WriteOpcode(DreamProcOpcode.CreateTypeEnumerator); WriteEnumeratorId(_enumeratorIdCounter++); @@ -1115,6 +1121,10 @@ private void WriteFilterID(int filterId, DreamPath filter) { AnnotatedBytecode.WriteFilterId(filterId, filter, _writerLocation); } + private void WriteFilterTypeID(DMValueType filter) { + AnnotatedBytecode.WriteFilterTypeId(filter, _writerLocation); + } + private void WriteStackDelta(int delta) { AnnotatedBytecode.WriteStackDelta(delta, _writerLocation); } diff --git a/DMCompiler/Optimizer/AnnotatedByteCodeWriter.cs b/DMCompiler/Optimizer/AnnotatedByteCodeWriter.cs index 9009df1ecf..47a512b07a 100644 --- a/DMCompiler/Optimizer/AnnotatedByteCodeWriter.cs +++ b/DMCompiler/Optimizer/AnnotatedByteCodeWriter.cs @@ -152,6 +152,25 @@ public void WriteFilterId(int filterTypeId, DreamPath filterPath, Location locat _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeFilter(filterTypeId, filterPath, location)); } + /// + /// Write a filter. Filters are stored as reference IDs in the raw bytecode, which refer + /// to a string in the string table containing the datum path of the filter. + /// + /// The datum paths of the filter + /// The location of the filter in the source code + /// + public void WriteFilterTypeId(DMValueType filterPaths, Location location) { + _location = location; + + if (_requiredArgs.Count == 0 || _requiredArgs.Peek() != OpcodeArgType.FilterId) { + DMCompiler.ForcedError(location, "Expected filter argument"); + } + + _requiredArgs.Pop(); + + _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeTypeFilter(1, filterPaths, location)); + } + /// /// Write a list size, restricted to non-negative integers /// diff --git a/DMCompiler/Optimizer/AnnotatedBytecode.cs b/DMCompiler/Optimizer/AnnotatedBytecode.cs index 35dbd2fab0..4faac4159a 100644 --- a/DMCompiler/Optimizer/AnnotatedBytecode.cs +++ b/DMCompiler/Optimizer/AnnotatedBytecode.cs @@ -574,6 +574,35 @@ public Location GetLocation() { } } +internal sealed class AnnotatedBytecodeTypeFilter : IAnnotatedBytecode { + public DMValueType FilterPath; + + public int FilterTypeId; + public Location Location; + + public AnnotatedBytecodeTypeFilter(int filterTypeId, DMValueType filterPath, Location location) { + FilterTypeId = filterTypeId; + FilterPath = filterPath; + Location = location; + } + + public void AddArg(IAnnotatedBytecode arg) { + DMCompiler.ForcedError(Location, "Cannot add args to a filter"); + } + + public void SetLocation(IAnnotatedBytecode loc) { + Location = loc.GetLocation(); + } + + public void SetLocation(Location loc) { + Location = loc; + } + + public Location GetLocation() { + return Location; + } +} + internal sealed class AnnotatedBytecodeReference : IAnnotatedBytecode { public int Index; public Location Location; diff --git a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs index 34ec285840..af85f7e4dc 100644 --- a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs +++ b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs @@ -125,6 +125,16 @@ public static ProcStatus CreateFilteredListEnumerator(DMProcState state) { return ProcStatus.Continue; } + public static ProcStatus CreateFilteredBaseTypesListEnumerator(DMProcState state) { // this is also plagarized from the above. make sure thats ok? and remove this comment + var enumeratorId = state.ReadInt(); + var filterTypeId = state.ReadInt(); + var filterType = state.Proc.ObjectTree.GetTreeEntry(filterTypeId); // this REALLY probably has to change. seems to be a path rn + var enumerator = GetContentsEnumerator(state.Proc.ObjectTree, state.Proc.AtomManager, state.Pop(), filterType); + + state.Enumerators[enumeratorId] = enumerator; + return ProcStatus.Continue; + } + public static ProcStatus CreateTypeEnumerator(DMProcState state) { var enumeratorId = state.ReadInt(); var typeValue = state.Pop(); diff --git a/OpenDreamRuntime/Procs/DMProc.cs b/OpenDreamRuntime/Procs/DMProc.cs index 4b83a2477b..825a665418 100644 --- a/OpenDreamRuntime/Procs/DMProc.cs +++ b/OpenDreamRuntime/Procs/DMProc.cs @@ -242,6 +242,7 @@ public sealed class DMProcState : ProcState { {DreamProcOpcode.OutputControl, DMOpcodeHandlers.OutputControl}, {DreamProcOpcode.BitShiftRight, DMOpcodeHandlers.BitShiftRight}, {DreamProcOpcode.CreateFilteredListEnumerator, DMOpcodeHandlers.CreateFilteredListEnumerator}, + {DreamProcOpcode.CreateFilteredBaseTypesListEnumerator, DMOpcodeHandlers.CreateFilteredBaseTypesListEnumerator}, {DreamProcOpcode.Power, DMOpcodeHandlers.Power}, {DreamProcOpcode.Prompt, DMOpcodeHandlers.Prompt}, {DreamProcOpcode.Ftp, DMOpcodeHandlers.Ftp}, diff --git a/OpenDreamRuntime/Procs/ProcDecoder.cs b/OpenDreamRuntime/Procs/ProcDecoder.cs index f5725b082a..f9142b3905 100644 --- a/OpenDreamRuntime/Procs/ProcDecoder.cs +++ b/OpenDreamRuntime/Procs/ProcDecoder.cs @@ -162,6 +162,7 @@ public ITuple DecodeInstruction() { return (opcode, ReadInt(), ReadReference(), ReadInt()); case DreamProcOpcode.CreateFilteredListEnumerator: + case DreamProcOpcode.CreateFilteredBaseTypesListEnumerator: case DreamProcOpcode.EnumerateNoAssign: return (opcode, ReadInt(), ReadInt()); @@ -289,6 +290,12 @@ or DreamProcOpcode.JumpIfTrueReference text.Append(getTypePath(type)); break; + case (DreamProcOpcode.CreateFilteredBaseTypesListEnumerator, int enumeratorId, int type): + text.Append(enumeratorId); + text.Append(' '); + text.Append(getTypePath(type)); // TODO: maybe make sure this works / leave as thing for reviewer to talk abt + break; + case (DreamProcOpcode.CreateListNRefs or DreamProcOpcode.PushNRefs, DMReference[] refs): { foreach (var reference in refs) { From 276ac95427f9798cd5f024ee56edb9dfbdc96e6b Mon Sep 17 00:00:00 2001 From: Romayne <27376947+MeggalBozale@users.noreply.github.com> Date: Sun, 11 Aug 2024 20:58:24 -0400 Subject: [PATCH 2/9] Terrible starting code, but a start to an unfamiliar land --- DMCompiler/Bytecode/DreamProcOpcode.cs | 3 +- DMCompiler/DM/Builders/DMProcBuilder.cs | 6 ++-- DMCompiler/DM/DMProc.cs | 10 +++++++ .../Optimizer/AnnotatedByteCodeWriter.cs | 19 ++++++++++++ DMCompiler/Optimizer/AnnotatedBytecode.cs | 29 +++++++++++++++++++ OpenDreamRuntime/Procs/DMOpcodeHandlers.cs | 10 +++++++ OpenDreamRuntime/Procs/DMProc.cs | 1 + OpenDreamRuntime/Procs/ProcDecoder.cs | 7 +++++ 8 files changed, 82 insertions(+), 3 deletions(-) diff --git a/DMCompiler/Bytecode/DreamProcOpcode.cs b/DMCompiler/Bytecode/DreamProcOpcode.cs index e73e513fe8..59ab997b4b 100644 --- a/DMCompiler/Bytecode/DreamProcOpcode.cs +++ b/DMCompiler/Bytecode/DreamProcOpcode.cs @@ -137,7 +137,8 @@ public enum DreamProcOpcode : byte { CreateFilteredListEnumerator = 0x41, [OpcodeMetadata(-1)] Power = 0x42, - //0x43, + [OpcodeMetadata(-1, OpcodeArgType.EnumeratorId, OpcodeArgType.FilterId)] + CreateFilteredBaseTypesListEnumerator = 0x43, //0x44 [OpcodeMetadata(-3, OpcodeArgType.TypeId)] Prompt = 0x45, diff --git a/DMCompiler/DM/Builders/DMProcBuilder.cs b/DMCompiler/DM/Builders/DMProcBuilder.cs index a52cd9265f..640ab45802 100644 --- a/DMCompiler/DM/Builders/DMProcBuilder.cs +++ b/DMCompiler/DM/Builders/DMProcBuilder.cs @@ -612,8 +612,10 @@ public void ProcessStatementForList(DMExpression list, DMExpression outputVar, D implicitTypeCheck = dmTypes.Value.TypePath; } else if (!dmTypes.Value.IsAnything) { // "as anything" performs no check. Other values are unimplemented. - DMCompiler.UnimplementedWarning(outputVar.Location, - $"As type {dmTypes} in for loops is unimplemented. No type check will be performed."); + byte typeId = (byte) dmTypes.Value.Type; + proc.CreateFilteredBaseTypesListEnumerator(typeId, dmTypes.Value.Type); + // DMCompiler.UnimplementedWarning(outputVar.Location, + // $"As type {dmTypes} in for loops is unimplemented. No type check will be performed."); } list.EmitPushValue(dmObject, proc); diff --git a/DMCompiler/DM/DMProc.cs b/DMCompiler/DM/DMProc.cs index 8c1e5be54d..3a18443691 100644 --- a/DMCompiler/DM/DMProc.cs +++ b/DMCompiler/DM/DMProc.cs @@ -372,6 +372,12 @@ public void CreateFilteredListEnumerator(int filterTypeId, DreamPath filterType) WriteFilterID(filterTypeId, filterType); } + public void CreateFilteredBaseTypesListEnumerator(byte filterTypeId, DMValueType filterType) { + WriteOpcode(DreamProcOpcode.CreateFilteredBaseTypesListEnumerator); + WriteEnumeratorId(_enumeratorIdCounter++); + WriteFilterTypeID(filterTypeId, filterType); + } + public void CreateTypeEnumerator() { WriteOpcode(DreamProcOpcode.CreateTypeEnumerator); WriteEnumeratorId(_enumeratorIdCounter++); @@ -1115,6 +1121,10 @@ private void WriteFilterID(int filterId, DreamPath filter) { AnnotatedBytecode.WriteFilterId(filterId, filter, _writerLocation); } + private void WriteFilterTypeID(byte filterId, DMValueType filter) { + AnnotatedBytecode.WriteFilterTypeId(filterId, filter, _writerLocation); + } + private void WriteStackDelta(int delta) { AnnotatedBytecode.WriteStackDelta(delta, _writerLocation); } diff --git a/DMCompiler/Optimizer/AnnotatedByteCodeWriter.cs b/DMCompiler/Optimizer/AnnotatedByteCodeWriter.cs index 9009df1ecf..d3ca5f9645 100644 --- a/DMCompiler/Optimizer/AnnotatedByteCodeWriter.cs +++ b/DMCompiler/Optimizer/AnnotatedByteCodeWriter.cs @@ -152,6 +152,25 @@ public void WriteFilterId(int filterTypeId, DreamPath filterPath, Location locat _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeFilter(filterTypeId, filterPath, location)); } + /// + /// Write a filter. Filters are stored as reference IDs in the raw bytecode, which refer + /// to a string in the string table containing the datum path of the filter. + /// + /// The datum paths of the filter + /// The location of the filter in the source code + /// + public void WriteFilterTypeId(byte filterTypeId, DMValueType filterPaths, Location location) { + _location = location; + + if (_requiredArgs.Count == 0 || _requiredArgs.Peek() != OpcodeArgType.FilterId) { + DMCompiler.ForcedError(location, "Expected filter argument"); + } + + _requiredArgs.Pop(); + + _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeTypeFilter(filterTypeId, filterPaths, location)); + } + /// /// Write a list size, restricted to non-negative integers /// diff --git a/DMCompiler/Optimizer/AnnotatedBytecode.cs b/DMCompiler/Optimizer/AnnotatedBytecode.cs index 35dbd2fab0..4faac4159a 100644 --- a/DMCompiler/Optimizer/AnnotatedBytecode.cs +++ b/DMCompiler/Optimizer/AnnotatedBytecode.cs @@ -574,6 +574,35 @@ public Location GetLocation() { } } +internal sealed class AnnotatedBytecodeTypeFilter : IAnnotatedBytecode { + public DMValueType FilterPath; + + public int FilterTypeId; + public Location Location; + + public AnnotatedBytecodeTypeFilter(int filterTypeId, DMValueType filterPath, Location location) { + FilterTypeId = filterTypeId; + FilterPath = filterPath; + Location = location; + } + + public void AddArg(IAnnotatedBytecode arg) { + DMCompiler.ForcedError(Location, "Cannot add args to a filter"); + } + + public void SetLocation(IAnnotatedBytecode loc) { + Location = loc.GetLocation(); + } + + public void SetLocation(Location loc) { + Location = loc; + } + + public Location GetLocation() { + return Location; + } +} + internal sealed class AnnotatedBytecodeReference : IAnnotatedBytecode { public int Index; public Location Location; diff --git a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs index 34ec285840..af85f7e4dc 100644 --- a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs +++ b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs @@ -125,6 +125,16 @@ public static ProcStatus CreateFilteredListEnumerator(DMProcState state) { return ProcStatus.Continue; } + public static ProcStatus CreateFilteredBaseTypesListEnumerator(DMProcState state) { // this is also plagarized from the above. make sure thats ok? and remove this comment + var enumeratorId = state.ReadInt(); + var filterTypeId = state.ReadInt(); + var filterType = state.Proc.ObjectTree.GetTreeEntry(filterTypeId); // this REALLY probably has to change. seems to be a path rn + var enumerator = GetContentsEnumerator(state.Proc.ObjectTree, state.Proc.AtomManager, state.Pop(), filterType); + + state.Enumerators[enumeratorId] = enumerator; + return ProcStatus.Continue; + } + public static ProcStatus CreateTypeEnumerator(DMProcState state) { var enumeratorId = state.ReadInt(); var typeValue = state.Pop(); diff --git a/OpenDreamRuntime/Procs/DMProc.cs b/OpenDreamRuntime/Procs/DMProc.cs index 4b83a2477b..825a665418 100644 --- a/OpenDreamRuntime/Procs/DMProc.cs +++ b/OpenDreamRuntime/Procs/DMProc.cs @@ -242,6 +242,7 @@ public sealed class DMProcState : ProcState { {DreamProcOpcode.OutputControl, DMOpcodeHandlers.OutputControl}, {DreamProcOpcode.BitShiftRight, DMOpcodeHandlers.BitShiftRight}, {DreamProcOpcode.CreateFilteredListEnumerator, DMOpcodeHandlers.CreateFilteredListEnumerator}, + {DreamProcOpcode.CreateFilteredBaseTypesListEnumerator, DMOpcodeHandlers.CreateFilteredBaseTypesListEnumerator}, {DreamProcOpcode.Power, DMOpcodeHandlers.Power}, {DreamProcOpcode.Prompt, DMOpcodeHandlers.Prompt}, {DreamProcOpcode.Ftp, DMOpcodeHandlers.Ftp}, diff --git a/OpenDreamRuntime/Procs/ProcDecoder.cs b/OpenDreamRuntime/Procs/ProcDecoder.cs index f5725b082a..f9142b3905 100644 --- a/OpenDreamRuntime/Procs/ProcDecoder.cs +++ b/OpenDreamRuntime/Procs/ProcDecoder.cs @@ -162,6 +162,7 @@ public ITuple DecodeInstruction() { return (opcode, ReadInt(), ReadReference(), ReadInt()); case DreamProcOpcode.CreateFilteredListEnumerator: + case DreamProcOpcode.CreateFilteredBaseTypesListEnumerator: case DreamProcOpcode.EnumerateNoAssign: return (opcode, ReadInt(), ReadInt()); @@ -289,6 +290,12 @@ or DreamProcOpcode.JumpIfTrueReference text.Append(getTypePath(type)); break; + case (DreamProcOpcode.CreateFilteredBaseTypesListEnumerator, int enumeratorId, int type): + text.Append(enumeratorId); + text.Append(' '); + text.Append(getTypePath(type)); // TODO: maybe make sure this works / leave as thing for reviewer to talk abt + break; + case (DreamProcOpcode.CreateListNRefs or DreamProcOpcode.PushNRefs, DMReference[] refs): { foreach (var reference in refs) { From b20f2719ce4dc0aa8536e6d426b7895191b6cc50 Mon Sep 17 00:00:00 2001 From: Romayne <27376947+MeggalBozale@users.noreply.github.com> Date: Sun, 11 Aug 2024 20:59:56 -0400 Subject: [PATCH 3/9] remove output.txt (dummy file) --- output.txt | Bin 283408 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 output.txt diff --git a/output.txt b/output.txt deleted file mode 100644 index f78e80a4dd17b60cc967e0daf8c76377e3cb2d43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 283408 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 Date: Mon, 12 Aug 2024 07:42:09 -0400 Subject: [PATCH 4/9] try swapping order of EmitPushValue and CreateFilteredBaseTypesListEnumerator --- DMCompiler/DM/Builders/DMProcBuilder.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/DMCompiler/DM/Builders/DMProcBuilder.cs b/DMCompiler/DM/Builders/DMProcBuilder.cs index 640ab45802..52ca056594 100644 --- a/DMCompiler/DM/Builders/DMProcBuilder.cs +++ b/DMCompiler/DM/Builders/DMProcBuilder.cs @@ -602,6 +602,7 @@ public void ProcessStatementForList(DMExpression list, DMExpression outputVar, D lValue = null; } + // Depending on the var's type and possibly a given "as [types]", an implicit istype() check is performed DreamPath? implicitTypeCheck = null; if (dmTypes == null) { @@ -610,12 +611,6 @@ public void ProcessStatementForList(DMExpression list, DMExpression outputVar, D } 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. - byte typeId = (byte) dmTypes.Value.Type; - proc.CreateFilteredBaseTypesListEnumerator(typeId, dmTypes.Value.Type); - // DMCompiler.UnimplementedWarning(outputVar.Location, - // $"As type {dmTypes} in for loops is unimplemented. No type check will be performed."); } list.EmitPushValue(dmObject, proc); @@ -628,6 +623,9 @@ public void ProcessStatementForList(DMExpression list, DMExpression outputVar, D $"Cannot filter enumeration by type {implicitTypeCheck.Value}, it does not exist"); proc.CreateListEnumerator(); } + } else if (dmTypes != null && !dmTypes.Value.IsAnything) { + byte typeId = (byte) dmTypes.Value.Type; + proc.CreateFilteredBaseTypesListEnumerator(typeId, dmTypes.Value.Type); } else { proc.CreateListEnumerator(); } From 1657d6d225db807a5f4e1506d81a2b2b161c045d Mon Sep 17 00:00:00 2001 From: Romayne <27376947+MeggalBozale@users.noreply.github.com> Date: Mon, 12 Aug 2024 09:21:21 -0400 Subject: [PATCH 5/9] Reorder things to be inline with the current code. Requires an unnecessary second bytecase and a unnecessary null check, though. Also add annotated bytecode case --- DMCompiler/DM/Builders/DMProcBuilder.cs | 31 +++++++++++-------- .../Optimizer/AnnotatedBytecodeSerializer.cs | 3 ++ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/DMCompiler/DM/Builders/DMProcBuilder.cs b/DMCompiler/DM/Builders/DMProcBuilder.cs index 52ca056594..dcb63adc2a 100644 --- a/DMCompiler/DM/Builders/DMProcBuilder.cs +++ b/DMCompiler/DM/Builders/DMProcBuilder.cs @@ -602,32 +602,37 @@ public void ProcessStatementForList(DMExpression list, DMExpression outputVar, D lValue = null; } - + list.EmitPushValue(dmObject, proc); // Depending on the var's type and possibly a given "as [types]", an implicit istype() check is performed DreamPath? implicitTypeCheck = null; + bool alreadyCreatedEnumerator = false; // Keep track of if we did CreateFilteredBaseTypesListEnumerator already 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 than that, all that's left are type assignments like `as mob|obj|area`. + + proc.CreateFilteredBaseTypesListEnumerator((byte) dmTypes.Value.Type, dmTypes.Value.Type); + alreadyCreatedEnumerator = true; + DMCompiler.VerbosePrint($"Created CreateFilteredBaseTypesListEnumerator"); } - list.EmitPushValue(dmObject, proc); - if (implicitTypeCheck != null) { - if (DMObjectTree.TryGetTypeId(implicitTypeCheck.Value, out var filterTypeId)) { - // Create an enumerator that will do the implicit istype() for us - proc.CreateFilteredListEnumerator(filterTypeId, implicitTypeCheck.Value); + if (!alreadyCreatedEnumerator) { + if (implicitTypeCheck != null) { + if (DMObjectTree.TryGetTypeId(implicitTypeCheck.Value, out var filterTypeId)) { + // Create an enumerator that will do the implicit istype() for us + proc.CreateFilteredListEnumerator(filterTypeId, implicitTypeCheck.Value); + } else { + DMCompiler.Emit(WarningCode.ItemDoesntExist, outputVar.Location, + $"Cannot filter enumeration by type {implicitTypeCheck.Value}, it does not exist"); + proc.CreateListEnumerator(); + } } else { - DMCompiler.Emit(WarningCode.ItemDoesntExist, outputVar.Location, - $"Cannot filter enumeration by type {implicitTypeCheck.Value}, it does not exist"); proc.CreateListEnumerator(); } - } else if (dmTypes != null && !dmTypes.Value.IsAnything) { - byte typeId = (byte) dmTypes.Value.Type; - proc.CreateFilteredBaseTypesListEnumerator(typeId, dmTypes.Value.Type); - } else { - proc.CreateListEnumerator(); } proc.StartScope(); diff --git a/DMCompiler/Optimizer/AnnotatedBytecodeSerializer.cs b/DMCompiler/Optimizer/AnnotatedBytecodeSerializer.cs index 9d92355fae..cb1dda3412 100644 --- a/DMCompiler/Optimizer/AnnotatedBytecodeSerializer.cs +++ b/DMCompiler/Optimizer/AnnotatedBytecodeSerializer.cs @@ -140,6 +140,9 @@ private void SerializeInstruction(AnnotatedBytecodeInstruction instruction) { case AnnotatedBytecodeTypeId annotatedBytecodeTypeId: _bytecodeWriter.Write(annotatedBytecodeTypeId.TypeId); break; + case AnnotatedBytecodeTypeFilter annotatedBytecodeTypeFilter: + _bytecodeWriter.Write(annotatedBytecodeTypeFilter.FilterTypeId); + break; default: throw new ArgumentOutOfRangeException(); } From 13aef79f0f394cee6fcbca94462ca4b5d3eb0185 Mon Sep 17 00:00:00 2001 From: Romayne <27376947+MeggalBozale@users.noreply.github.com> Date: Mon, 12 Aug 2024 09:32:19 -0400 Subject: [PATCH 6/9] remove excess whitespace --- DMCompiler/DM/Builders/DMProcBuilder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/DMCompiler/DM/Builders/DMProcBuilder.cs b/DMCompiler/DM/Builders/DMProcBuilder.cs index dcb63adc2a..2ed16a4199 100644 --- a/DMCompiler/DM/Builders/DMProcBuilder.cs +++ b/DMCompiler/DM/Builders/DMProcBuilder.cs @@ -614,7 +614,6 @@ public void ProcessStatementForList(DMExpression list, DMExpression outputVar, D implicitTypeCheck = dmTypes.Value.TypePath; } else if (!dmTypes.Value.IsAnything) { // "as anything" performs no check. Other than that, all that's left are type assignments like `as mob|obj|area`. - proc.CreateFilteredBaseTypesListEnumerator((byte) dmTypes.Value.Type, dmTypes.Value.Type); alreadyCreatedEnumerator = true; DMCompiler.VerbosePrint($"Created CreateFilteredBaseTypesListEnumerator"); From 62b73f597aa41e0dc656ed1471a4c95108fcd8e2 Mon Sep 17 00:00:00 2001 From: Romayne <27376947+MeggalBozale@users.noreply.github.com> Date: Mon, 12 Aug 2024 09:37:48 -0400 Subject: [PATCH 7/9] few comment changes --- DMCompiler/Optimizer/AnnotatedByteCodeWriter.cs | 7 ++++--- OpenDreamRuntime/Procs/DMOpcodeHandlers.cs | 4 ++-- OpenDreamRuntime/Procs/ProcDecoder.cs | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/DMCompiler/Optimizer/AnnotatedByteCodeWriter.cs b/DMCompiler/Optimizer/AnnotatedByteCodeWriter.cs index d3ca5f9645..903b8aa67f 100644 --- a/DMCompiler/Optimizer/AnnotatedByteCodeWriter.cs +++ b/DMCompiler/Optimizer/AnnotatedByteCodeWriter.cs @@ -156,10 +156,11 @@ public void WriteFilterId(int filterTypeId, DreamPath filterPath, Location locat /// Write a filter. Filters are stored as reference IDs in the raw bytecode, which refer /// to a string in the string table containing the datum path of the filter. /// - /// The datum paths of the filter + /// The type ID of the filter + /// /// The base types of the filter /// The location of the filter in the source code /// - public void WriteFilterTypeId(byte filterTypeId, DMValueType filterPaths, Location location) { + public void WriteFilterTypeId(byte filterTypeId, DMValueType filterTypes, Location location) { _location = location; if (_requiredArgs.Count == 0 || _requiredArgs.Peek() != OpcodeArgType.FilterId) { @@ -168,7 +169,7 @@ public void WriteFilterTypeId(byte filterTypeId, DMValueType filterPaths, Locati _requiredArgs.Pop(); - _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeTypeFilter(filterTypeId, filterPaths, location)); + _annotatedBytecode[^1].AddArg(new AnnotatedBytecodeTypeFilter(filterTypeId, filterTypes, location)); } /// diff --git a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs index af85f7e4dc..8be0d3425b 100644 --- a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs +++ b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs @@ -125,10 +125,10 @@ public static ProcStatus CreateFilteredListEnumerator(DMProcState state) { return ProcStatus.Continue; } - public static ProcStatus CreateFilteredBaseTypesListEnumerator(DMProcState state) { // this is also plagarized from the above. make sure thats ok? and remove this comment + public static ProcStatus CreateFilteredBaseTypesListEnumerator(DMProcState state) { var enumeratorId = state.ReadInt(); var filterTypeId = state.ReadInt(); - var filterType = state.Proc.ObjectTree.GetTreeEntry(filterTypeId); // this REALLY probably has to change. seems to be a path rn + var filterType = state.Proc.ObjectTree.GetTreeEntry(filterTypeId); var enumerator = GetContentsEnumerator(state.Proc.ObjectTree, state.Proc.AtomManager, state.Pop(), filterType); state.Enumerators[enumeratorId] = enumerator; diff --git a/OpenDreamRuntime/Procs/ProcDecoder.cs b/OpenDreamRuntime/Procs/ProcDecoder.cs index f9142b3905..dc1eaf3a6d 100644 --- a/OpenDreamRuntime/Procs/ProcDecoder.cs +++ b/OpenDreamRuntime/Procs/ProcDecoder.cs @@ -293,7 +293,7 @@ or DreamProcOpcode.JumpIfTrueReference case (DreamProcOpcode.CreateFilteredBaseTypesListEnumerator, int enumeratorId, int type): text.Append(enumeratorId); text.Append(' '); - text.Append(getTypePath(type)); // TODO: maybe make sure this works / leave as thing for reviewer to talk abt + text.Append(getTypePath(type)); break; case (DreamProcOpcode.CreateListNRefs From 2e6068e9232e35e4a627c238ec5a186e5c2ee43a Mon Sep 17 00:00:00 2001 From: Romayne <27376947+MeggalBozale@users.noreply.github.com> Date: Mon, 12 Aug 2024 09:38:57 -0400 Subject: [PATCH 8/9] change filter type id from int to byte as it should be --- OpenDreamRuntime/Procs/DMOpcodeHandlers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs index 8be0d3425b..cb95777daf 100644 --- a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs +++ b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs @@ -127,7 +127,7 @@ public static ProcStatus CreateFilteredListEnumerator(DMProcState state) { public static ProcStatus CreateFilteredBaseTypesListEnumerator(DMProcState state) { var enumeratorId = state.ReadInt(); - var filterTypeId = state.ReadInt(); + var filterTypeId = state.ReadByte(); var filterType = state.Proc.ObjectTree.GetTreeEntry(filterTypeId); var enumerator = GetContentsEnumerator(state.Proc.ObjectTree, state.Proc.AtomManager, state.Pop(), filterType); From a906dc339206e18d94fbff61d770bd879d6e2ff8 Mon Sep 17 00:00:00 2001 From: Romayne <27376947+MeggalBozale@users.noreply.github.com> Date: Mon, 12 Aug 2024 13:02:08 -0400 Subject: [PATCH 9/9] put this up for now as I work on something else (this is a bit much rn) --- DMCompiler/DM/DMObject.cs | 4 +++ OpenDreamRuntime/Objects/DreamObject.cs | 4 +++ .../Objects/DreamObjectDefinition.cs | 5 +++ OpenDreamRuntime/Objects/DreamObjectTree.cs | 5 +++ OpenDreamRuntime/Procs/DMOpcodeHandlers.cs | 32 ++++++++++++++++- OpenDreamRuntime/Procs/DreamEnumerators.cs | 35 ++++++++++++++++++- 6 files changed, 83 insertions(+), 2 deletions(-) diff --git a/DMCompiler/DM/DMObject.cs b/DMCompiler/DM/DMObject.cs index ea65433a88..52172642b8 100644 --- a/DMCompiler/DM/DMObject.cs +++ b/DMCompiler/DM/DMObject.cs @@ -230,4 +230,8 @@ public DMValueType GetDMValueType() { return DMValueType.Anything; } + + public DreamPath[] GetDMAncestors() { + + } } diff --git a/OpenDreamRuntime/Objects/DreamObject.cs b/OpenDreamRuntime/Objects/DreamObject.cs index 912ba49781..7c81f8d930 100644 --- a/OpenDreamRuntime/Objects/DreamObject.cs +++ b/OpenDreamRuntime/Objects/DreamObject.cs @@ -120,6 +120,10 @@ public bool IsSubtypeOf(TreeEntry ancestor) { return ObjectDefinition.IsSubtypeOf(ancestor); } + public bool IsSubtypeOf(byte ancestors) { + return ObjectDefinition.IsSubtypeOf(ancestors); + } + #region Variables public virtual bool IsSaved(string name) { return ObjectDefinition.Variables.ContainsKey(name) diff --git a/OpenDreamRuntime/Objects/DreamObjectDefinition.cs b/OpenDreamRuntime/Objects/DreamObjectDefinition.cs index 670299e238..eebd3a104c 100644 --- a/OpenDreamRuntime/Objects/DreamObjectDefinition.cs +++ b/OpenDreamRuntime/Objects/DreamObjectDefinition.cs @@ -197,4 +197,9 @@ public bool TryGetVariable(string varName, out DreamValue value) { public bool IsSubtypeOf(TreeEntry ancestor) { return TreeEntry.IsSubtypeOf(ancestor); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsSubtypeOf(byte ancestors) { + return TreeEntry.IsSubtypeOf(ancestors); + } } diff --git a/OpenDreamRuntime/Objects/DreamObjectTree.cs b/OpenDreamRuntime/Objects/DreamObjectTree.cs index ce86bb4c33..bf351c0ac6 100644 --- a/OpenDreamRuntime/Objects/DreamObjectTree.cs +++ b/OpenDreamRuntime/Objects/DreamObjectTree.cs @@ -516,6 +516,11 @@ public bool IsSubtypeOf(TreeEntry ancestor) { return (TreeIndex - ancestor.TreeIndex) <= ancestor.ChildCount; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsSubtypeOf(byte ancestors) { + return true; + } + public override string ToString() { return Path; } diff --git a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs index cb95777daf..c143fdfe25 100644 --- a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs +++ b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs @@ -107,6 +107,36 @@ private static IDreamValueEnumerator GetContentsEnumerator(DreamObjectTree objec return new DreamValueArrayEnumerator(Array.Empty()); } + private static IDreamValueEnumerator GetContentsEnumeratorAeiou(DreamObjectTree objectTree, AtomManager atomManager, DreamValue value, byte? filterType) { + if (!value.TryGetValueAsDreamList(out var list)) { + if (value.TryGetValueAsDreamObject(out var dreamObject)) { + if (dreamObject == null) + return new DreamValueArrayEnumerator(Array.Empty()); + + if (dreamObject is DreamObjectAtom) { + list = dreamObject.GetVariable("contents").MustGetValueAsDreamList(); + } else if (dreamObject is DreamObjectWorld) { + return new WorldContentsEnumerator(atomManager, filterType); + } + } + } + + if (list != null) { + // world.contents has its own special enumerator to prevent the huge copy + if (list is WorldContentsList) + return new WorldContentsEnumerator(atomManager, filterType); + + var values = list.GetValues().ToArray(); + + return filterType == null + ? new DreamValueArrayEnumerator(values) + : new FilteredDreamValueArrayEnumeratorAeiou(values, (byte) filterType); + } + + // BYOND ignores all floats, strings, types, etc. here and just doesn't run the loop. + return new DreamValueArrayEnumerator(Array.Empty()); + } + public static ProcStatus CreateListEnumerator(DMProcState state) { var enumeratorId = state.ReadInt(); var enumerator = GetContentsEnumerator(state.Proc.ObjectTree, state.Proc.AtomManager, state.Pop(), null); @@ -129,7 +159,7 @@ public static ProcStatus CreateFilteredBaseTypesListEnumerator(DMProcState state var enumeratorId = state.ReadInt(); var filterTypeId = state.ReadByte(); var filterType = state.Proc.ObjectTree.GetTreeEntry(filterTypeId); - var enumerator = GetContentsEnumerator(state.Proc.ObjectTree, state.Proc.AtomManager, state.Pop(), filterType); + var enumerator = GetContentsEnumeratorAeiou(state.Proc.ObjectTree, state.Proc.AtomManager, state.Pop(), filterType); state.Enumerators[enumeratorId] = enumerator; return ProcStatus.Continue; diff --git a/OpenDreamRuntime/Procs/DreamEnumerators.cs b/OpenDreamRuntime/Procs/DreamEnumerators.cs index b954ea507b..c47d4ec9dd 100644 --- a/OpenDreamRuntime/Procs/DreamEnumerators.cs +++ b/OpenDreamRuntime/Procs/DreamEnumerators.cs @@ -84,7 +84,7 @@ 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) +/// for (var/I as obj|mob in contents) /// internal sealed class FilteredDreamValueArrayEnumerator : IDreamValueEnumerator { private readonly DreamValue[] _dreamValueArray; @@ -115,6 +115,39 @@ public bool Enumerate(DMProcState state, DreamReference? reference) { } } +/// +/// Enumerates over an array of DreamValues, filtering for a certain type +/// for (var/I as obj|mob in contents) +/// +internal sealed class FilteredDreamValueArrayEnumeratorAeiou : IDreamValueEnumerator { + private readonly DreamValue[] _dreamValueArray; + private readonly byte _filterType; + private int _current = -1; + + public FilteredDreamValueArrayEnumeratorAeiou(DreamValue[] dreamValueArray, byte filterType) { + _dreamValueArray = dreamValueArray; + _filterType = filterType; + } + + public bool Enumerate(DMProcState state, DreamReference? reference) { + do { + _current++; + 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)) { + if (reference != null) + state.AssignReference(reference.Value, value); + return true; + } + } while (true); + } +} + /// /// Enumerates over all atoms in the world, possibly filtering for a certain type /// for (var/obj/item/I in world)