diff --git a/src/Packages/Audience/Runtime/AudienceConfig.cs b/src/Packages/Audience/Runtime/AudienceConfig.cs index c4b2ae9af..deda29847 100644 --- a/src/Packages/Audience/Runtime/AudienceConfig.cs +++ b/src/Packages/Audience/Runtime/AudienceConfig.cs @@ -40,5 +40,11 @@ public class AudienceConfig // Test seam for HttpTransport; not part of the public API. internal System.Net.Http.HttpMessageHandler? HttpHandler { get; set; } + + // Makes a shallow copy so Init can fill in PersistentDataPath without + // modifying the caller's object. Delegates and handlers (OnError, + // HttpHandler) are deliberately kept as the same instance — cloning + // them would break their behaviour. + internal AudienceConfig Clone() => (AudienceConfig)MemberwiseClone(); } } diff --git a/src/Packages/Audience/Runtime/ImmutableAudience.cs b/src/Packages/Audience/Runtime/ImmutableAudience.cs index e90ef7b46..c35c08e5b 100644 --- a/src/Packages/Audience/Runtime/ImmutableAudience.cs +++ b/src/Packages/Audience/Runtime/ImmutableAudience.cs @@ -47,9 +47,14 @@ public static class ImmutableAudience public static void Init(AudienceConfig config) { if (config == null) throw new ArgumentNullException(nameof(config)); + + // Copy the caller's config so filling in PersistentDataPath below + // doesn't change the object they passed in. Validating after the + // clone keeps the compiler's null-flow analysis for PublishableKey + // alive through the HttpTransport constructor below. + config = config.Clone(); if (string.IsNullOrEmpty(config.PublishableKey)) throw new ArgumentException("PublishableKey is required", nameof(config)); - if (string.IsNullOrEmpty(config.PersistentDataPath)) config.PersistentDataPath = DefaultPersistentDataPathProvider?.Invoke(); if (string.IsNullOrEmpty(config.PersistentDataPath))