Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Diagnostics.DataContractReader.Contracts;


Expand Down Expand Up @@ -116,7 +117,34 @@ public abstract class ContractRegistry
/// </summary>
public virtual IDebugger Debugger => GetContract<IDebugger>();

public abstract TContract GetContract<TContract>() where TContract : IContract;
/// <summary>
/// Attempts to get an instance of the requested contract for the target.
/// </summary>
/// <typeparam name="TContract">The contract type to retrieve.</typeparam>
/// <param name="contract">
/// When this method returns <see langword="true"/>, contains the requested contract instance; otherwise, <see langword="null"/>.
/// </param>
/// <param name="failureReason">
/// When this method returns <see langword="false"/>, contains a human-readable explanation of why the contract could not be retrieved; otherwise, <see langword="null"/>.
/// </param>
/// <returns>
/// <see langword="true"/> if the requested contract is present and was retrieved successfully; <see langword="false"/> if the contract is not present or registered"/>.
/// </returns>
public abstract bool TryGetContract<TContract>([NotNullWhen(true)] out TContract contract, out string? failureReason) where TContract : IContract;

public TContract GetContract<TContract>() where TContract : IContract
{
if (!TryGetContract(out TContract contract, out string? failureReason))
{
throw new NotImplementedException($"Contract '{typeof(TContract).Name}' is not supported by the target. Reason: {failureReason ?? "no reason provided"}");
}
return contract;
}

public bool TryGetContract<TContract>([NotNullWhen(true)] out TContract contract) where TContract : IContract
{
return TryGetContract(out contract, out _);
}

/// <summary>
/// Register a contract implementation for a specific version.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Diagnostics.DataContractReader.Contracts;

namespace Microsoft.Diagnostics.DataContractReader;
Expand Down Expand Up @@ -37,22 +38,36 @@ public override void Register<TContract>(int version, Func<Target, TContract> cr
_creators[(typeof(TContract), version)] = t => creator(t);
}

public override TContract GetContract<TContract>()
public override bool TryGetContract<TContract>([NotNullWhen(true)] out TContract contract, out string? failureReason)
{
contract = default!;
failureReason = null;
if (_contracts.TryGetValue(typeof(TContract), out IContract? cached))
return (TContract)cached;
{
contract = (TContract)cached;
return true;
}

if (!_tryGetContractVersion(TContract.Name, out int version))
throw new NotImplementedException($"Contract '{TContract.Name}' is not present in the contract descriptor.");
{
failureReason = $"Target does not support contract '{typeof(TContract).Name}'.";
return false;
}

if (!_creators.TryGetValue((typeof(TContract), version), out Func<Target, IContract>? creator))
throw new NotImplementedException($"No implementation registered for contract '{TContract.Name}' version {version}.");
{
failureReason = $"Target supports contract '{typeof(TContract).Name}' version {version}, but no implementation is registered for that version.";
return false;
}

TContract contract = (TContract)creator(_target);
contract = (TContract)creator(_target);
if (_contracts.TryAdd(typeof(TContract), contract))
return contract;
{
return true;
}

return (TContract)_contracts[typeof(TContract)];
contract = (TContract)_contracts[typeof(TContract)];
return true;
}

public override void Flush()
Expand Down
28 changes: 19 additions & 9 deletions src/native/managed/cdac/tests/TestPlaceholderTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -521,30 +521,40 @@ public void SetMock<TContract>(TContract mock) where TContract : IContract
public override void Register<TContract>(int version, Func<Target, TContract> creator)
=> _creators[(typeof(TContract), version)] = t => creator(t);

public override TContract GetContract<TContract>()
public override bool TryGetContract<TContract>([NotNullWhen(true)] out TContract contract, out string? failureReason)
{
contract = default!;
failureReason = null;
if (_resolved.TryGetValue(typeof(TContract), out var cached))
return (TContract)cached;
{
contract = (TContract)cached;
return true;
}

IContract contract;
IContract resolved;
if (_mocks.TryGetValue(typeof(TContract), out var mock))
{
contract = mock;
resolved = mock;
}
else if (_versions.TryGetValue(typeof(TContract), out int version))
{
if (!_creators.TryGetValue((typeof(TContract), version), out var creator))
throw new NotImplementedException($"No implementation registered for contract '{typeof(TContract).Name}' version {version}.");
{
failureReason = $"Target supports contract '{typeof(TContract).Name}' version {version}, but no implementation is registered for that version.";
return false;
}

contract = creator(_target);
resolved = creator(_target);
}
else
{
throw new NotImplementedException($"Contract {typeof(TContract).Name} is not registered. Use SetVersion<T>(version) or SetMock<T>(mock) to configure contracts.");
failureReason = $"Contract '{typeof(TContract).Name}' is not supported by the target.";
return false;
}

_resolved[typeof(TContract)] = contract;
return (TContract)contract;
_resolved[typeof(TContract)] = resolved;
contract = (TContract)resolved;
return true;
}

public override void Flush() { }
Expand Down