Skip to content

Commit

Permalink
Merge pull request dotnet#2867 from dotnet/TypeMetadataCache
Browse files Browse the repository at this point in the history
added TypeMetadataCache
  • Loading branch information
stephen-hawley authored Dec 11, 2024
2 parents 23d0cff + f7026b2 commit 24dc902
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 0 deletions.
31 changes: 31 additions & 0 deletions src/Swift.Runtime/src/Metadata/ITypeMetadataCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Diagnostics.CodeAnalysis;

namespace Swift.Runtime;

/// <summary>
/// Represents an interface that defines how a cache for TypeMetadata should operate.
/// The implementation of this interface needs to be thread safe.
/// </summary>
public interface ITypeMetadataCache
{
/// <summary>
/// Returns true if the cache contains an entry for type and sets metadata to the resulting value,
/// otherwise it returns false and metadata will be null.
/// </summary>
/// <param name="type">The type to look up in the cache</param>
/// <param name="metadata">The resulting metadata if found</param>
/// <returns>true if the lookup was successful, false otherwise</returns>
bool TryGet(Type type, [NotNullWhen(true)]out TypeMetadata? metadata);

/// <summary>
/// Gets the TypeMetadata for the given Type type or if it is not present,
/// adds it to the cache using the given factory to generate the value.
/// </summary>
/// <param name="type">The type to look up in the cache</param>
/// <param name="metadataFactory">a factory to generate the TypeMetadata if not present</param>
/// <returns>The TypeMetadata associated with the give Type type</returns>
TypeMetadata GetOrAdd(Type type, Func<Type, TypeMetadata> metadataFactory);
}
12 changes: 12 additions & 0 deletions src/Swift.Runtime/src/Metadata/TypeMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ public enum TypeMetadataKind {
public readonly struct TypeMetadata : IEquatable<TypeMetadata> {
readonly IntPtr handle;

static TypeMetadata ()
{
// TODO - add metadata for common built-in types like scalars and strings
cache = new TypeMetadataCache();
}

/// <summary>
/// An empty/invalid TypeMetadata object
/// </summary>
Expand Down Expand Up @@ -237,4 +243,10 @@ public override int GetHashCode ()
{
return handle.GetHashCode ();
}

static readonly TypeMetadataCache cache;
/// <summary>
/// Gets the type metadata cache for the runtime.
/// </summary>
public static ITypeMetadataCache Cache => cache;
}
70 changes: 70 additions & 0 deletions src/Swift.Runtime/src/Metadata/TypeMetadataCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;

namespace Swift.Runtime;

/// <summary>
/// Internal implementation of ITypeMetadataCache
/// </summary>
internal class TypeMetadataCache : ITypeMetadataCache
{
readonly ConcurrentDictionary<Type, TypeMetadata> cache = new();

/// <summary>
/// Constructs an empty cache.
/// </summary>
public TypeMetadataCache()
{

}

/// <summary>
/// Constructs a cache with the supplied initial values.
/// </summary>
/// <param name="initialValues">An enumeration of tuples of Type and TypeMetadata to initialize the cache</param>
public TypeMetadataCache(IEnumerable<(Type, TypeMetadata)> initialValues)
{
var dictCache = (IDictionary<Type, TypeMetadata>)cache;
foreach (var (key, value) in initialValues)
{
dictCache.Add(key, value);
}
}

/// <summary>
/// Returns true if the cache contains an entry for type and sets metadata to the resulting value,
/// otherwise it returns false and metadata will be null.
/// </summary>
/// <param name="type">The type to look up in the cache</param>
/// <param name="metadata">The resulting metadata if found</param>
/// <returns>true if the lookup was successful, false otherwise</returns>
public bool TryGet(Type type, [NotNullWhen(true)] out TypeMetadata? metadata)
{
if (cache.TryGetValue(type, out var md))
{
metadata = md;
return true;
}
else
{
metadata = null;
return false;
}
}

/// <summary>
/// Gets the TypeMetadata for the given Type type or if it is not present,
/// adds it to the cache using the given factory to generate the value.
/// </summary>
/// <param name="type">The type to look up in the cache</param>
/// <param name="metadataFactory">a factory to generate the TypeMetadata if not present</param>
/// <returns>The TypeMetadata associated with the give Type type</returns>
public TypeMetadata GetOrAdd(Type type, Func<Type, TypeMetadata> metadataFactory)
{
return cache.GetOrAdd(type, metadataFactory);
}
}
3 changes: 3 additions & 0 deletions src/Swift.Runtime/src/Swift.Runtime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
<Content Include="Library/*.cs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Metadata/*.cs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

</Project>
69 changes: 69 additions & 0 deletions src/Swift.Runtime/tests/TypeMetadataTests/TypeMetadataTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Xunit;
using Swift.Runtime;
using System.Reflection;

namespace BindingsGeneration.Tests;

public class TypeMetadataTests : IClassFixture<TypeMetadataTests.TestFixture>
{
private readonly TestFixture _fixture;

public TypeMetadataTests(TestFixture fixture)
{
_fixture = fixture;
}

public class TestFixture
{
static TestFixture()
{
}

private static void InitializeResources()
{
}
}

static TypeMetadata MakePhonyMetadata(int value)
{
IntPtr p = new IntPtr(value);

var t = typeof(TypeMetadata);
var ci = t.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, new Type[] { typeof(IntPtr) })!;

return (TypeMetadata)(ci.Invoke(new object[] { p }));
}

[Fact]
public static void CacheWorks()
{
var fakeMeta = MakePhonyMetadata(42);
TypeMetadata.Cache.GetOrAdd(typeof(System.Convert), (t) =>
{
return fakeMeta;
});
Assert.True(TypeMetadata.Cache.TryGet(typeof(System.Convert), out var result));
}

[Fact]
public static void TryGetFail()
{
var contains = TypeMetadata.Cache.TryGet(typeof(System.EventArgs), out var result);
Assert.False(contains);
}

[Fact]
public static void TryGetSucceed()
{
var fakeMeta = MakePhonyMetadata(43);
TypeMetadata.Cache.GetOrAdd(typeof(System.Random), (t) =>
{
return fakeMeta;
});
var contains = TypeMetadata.Cache.TryGet(typeof(System.Random), out var result);
Assert.True(contains);
}
}

0 comments on commit 24dc902

Please sign in to comment.