Skip to content

Conversation

@clodanSPT
Copy link
Contributor

No description provided.

@qodo-merge-for-open-source
Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Avoid public setters on ExtensionData

Remove the public setters being added to ExtensionData properties. This change,
introduced via IL weaving, breaks encapsulation and risks data loss by allowing
the entire extension data dictionary to be replaced.

Examples:

Patches/Ceciler.JsonExtensionData/JsonExtensionDataPatch.cs [61-74]
            // Add setter
            var set = new MethodDefinition(
                "set_ExtensionData",
                MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
                assembly.MainModule.TypeSystem.Void
            );

            set.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, _dictionaryStringObjectReference));
            set.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
            set.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1));

 ... (clipped 4 lines)
Libraries/SPTarkov.Server.Core/Utils/Reference/StaticReferences.cs [15]
        set { _reference = value; }

Solution Walkthrough:

Before:

// In Patches/Ceciler.JsonExtensionData/JsonExtensionDataPatch.cs
// This IL weaving code generates properties with public setters
// for many model classes.

// Add setter
var set = new MethodDefinition("set_ExtensionData", ...);
set.Parameters.Add(new ParameterDefinition("value", ...));
set.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
set.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1));
set.Body.Instructions.Add(Instruction.Create(OpCodes.Stfld, field));
set.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));

propertyDefinition.SetMethod = set;
typeDefinition.Methods.Add(set);

After:

// In Patches/Ceciler.JsonExtensionData/JsonExtensionDataPatch.cs
// The code generating the public setter should be removed.
// The property will become get-only, preventing the entire dictionary
// from being replaced.

// Remove the following lines:
// var set = new MethodDefinition("set_ExtensionData", ...);
// ...
// propertyDefinition.SetMethod = set;
// typeDefinition.Methods.Add(set);

// The property will now be implicitly get-only:
propertyDefinition.GetMethod = get;
typeDefinition.Methods.Add(get);
typeDefinition.Properties.Add(propertyDefinition);
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical design flaw, breaking encapsulation, which is being systematically introduced across many models via IL weaving and could lead to subtle data loss bugs.

High
Learned
best practice
Add null guard to emitted setter

Insert a null-coalescing path so the setter assigns an empty dictionary when
value is null, preserving invariants.

Patches/Ceciler.JsonExtensionData/JsonExtensionDataPatch.cs [62-72]

 var set = new MethodDefinition(
     "set_ExtensionData",
     MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
     assembly.MainModule.TypeSystem.Void
 );
 
 set.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, _dictionaryStringObjectReference));
+// if (value == null) value = new Dictionary<string, object>();
+var skipInit = Instruction.Create(OpCodes.Nop);
+set.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1));
+set.Body.Instructions.Add(Instruction.Create(OpCodes.Brtrue_S, skipInit));
+set.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1)); // stack placeholder not needed; reassign via starg
+set.Body.Instructions.Add(Instruction.Create(OpCodes.Newobj, ((TypeReference)_dictionaryStringObjectReference).Resolve().GetConstructors().First(c => c.Parameters.Count == 0)));
+set.Body.Instructions.Add(Instruction.Create(OpCodes.Starg_S, set.Parameters[0]));
+set.Body.Instructions.Add(skipInit);
+// this._extensionData = value;
 set.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
 set.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1));
 set.Body.Instructions.Add(Instruction.Create(OpCodes.Stfld, field));
 set.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Guard state mutations with robust error handling; ensure generated setters handle null safely and preserve invariants.

Low
Validate and safely assign setter

Validate the incoming value and avoid assigning null; consider copying to
prevent external mutation of internal state.

Libraries/SPTarkov.Server.Core/Utils/Reference/StaticReferences.cs [12-16]

 public Dictionary<string, object> Reference
 {
     get { return _reference; }
-    set { _reference = value; }
+    set { _reference = value ?? new Dictionary<string, object>(); }
 }
Suggestion importance[1-10]: 5

__

Why:
Relevant best practice - Guard high‑risk state mutations with validation and error handling; avoid exposing mutable internal collections directly.

Low
  • More

@chompDev chompDev merged commit f1710cf into develop Oct 20, 2025
5 checks passed
@chompDev chompDev deleted the extension-data-patch-setter branch October 20, 2025 08:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants