Skip to content
Merged
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
35 changes: 23 additions & 12 deletions src/WinRT.Projection.Writer/Factories/MappedInterfaceStubFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,13 @@ private static void EmitDictionary(IndentedTextWriter writer, ProjectionEmitCont
string enumerableObjRefName = "_objRef_System_Collections_Generic_IEnumerable_" + IidExpressionGenerator.EscapeTypeNameForIdentifier(kvLong, stripGlobal: false) + "_";

writer.WriteLine();

Comment thread
Sergio0694 marked this conversation as resolved.
// 'Keys'/'Values' take the projected runtime class directly (passed as 'this'), rather than the
// interface object reference like the other accessors. This lets the returned collection be cached
// in the public property's backing 'field' so it preserves reference identity across accesses.
EmitUnsafeAccessor(writer, "Keys", $"ICollection<{k}>", $"{prefix}Keys", interopType, "", receiver: "WindowsRuntimeObject windowsRuntimeObject");
EmitUnsafeAccessor(writer, "Values", $"ICollection<{v}>", $"{prefix}Values", interopType, "", receiver: "WindowsRuntimeObject windowsRuntimeObject");
EmitUnsafeAccessors(writer, interopType, [
new("Keys", $"ICollection<{k}>", $"{prefix}Keys", ""),
new("Values", $"ICollection<{v}>", $"{prefix}Values", ""),
new("Count", "int", $"{prefix}Count", ""),
new("Item", v, $"{prefix}Item", $", {k} key"),
new("Item", "void", $"{prefix}Item", $", {k} key, {v} value"),
Expand All @@ -223,8 +227,8 @@ private static void EmitDictionary(IndentedTextWriter writer, ProjectionEmitCont
// GetEnumerator is NOT emitted here -- it's handled separately by IIterable<KVP>'s own
// EmitGenericEnumerable invocation.
writer.WriteLine(isMultiline: true, $$"""
public ICollection<{{k}}> Keys => {{prefix}}Keys(null, {{objRefName}});
public ICollection<{{v}}> Values => {{prefix}}Values(null, {{objRefName}});
public ICollection<{{k}}> Keys => field ??= {{prefix}}Keys(null, this);
public ICollection<{{v}}> Values => field ??= {{prefix}}Values(null, this);
public int Count => {{prefix}}Count(null, {{objRefName}});
public bool IsReadOnly => false;
public {{v}} this[{{k}} key]
Expand Down Expand Up @@ -261,9 +265,13 @@ private static void EmitReadOnlyDictionary(IndentedTextWriter writer, Projection
string prefix = "IReadOnlyDictionaryMethods_" + keyId + "_" + valId + "_";

writer.WriteLine();

// 'Keys'/'Values' take the projected runtime class directly (passed as 'this'), rather than the
// interface object reference like the other accessors. This lets the returned collection be cached
// in the public property's backing 'field' so it preserves reference identity across accesses.
EmitUnsafeAccessor(writer, "Keys", $"IEnumerable<{k}>", $"{prefix}Keys", interopType, "", receiver: "WindowsRuntimeObject windowsRuntimeObject");
EmitUnsafeAccessor(writer, "Values", $"IEnumerable<{v}>", $"{prefix}Values", interopType, "", receiver: "WindowsRuntimeObject windowsRuntimeObject");
EmitUnsafeAccessors(writer, interopType, [
new("Keys", $"ICollection<{k}>", $"{prefix}Keys", ""),
new("Values", $"ICollection<{v}>", $"{prefix}Values", ""),
new("Count", "int", $"{prefix}Count", ""),
new("Item", v, $"{prefix}Item", $", {k} key"),
new("ContainsKey", "bool", $"{prefix}ContainsKey", $", {k} key"),
Expand All @@ -273,8 +281,8 @@ private static void EmitReadOnlyDictionary(IndentedTextWriter writer, Projection
// EmitGenericEnumerable invocation.
writer.WriteLine();
writer.WriteLine($"public {v} this[{k} key] => {prefix}Item(null, {objRefName}, key);");
writer.WriteLine($"public IEnumerable<{k}> Keys => {prefix}Keys(null, {objRefName});");
writer.WriteLine($"public IEnumerable<{v}> Values => {prefix}Values(null, {objRefName});");
writer.WriteLine($"public IEnumerable<{k}> Keys => field ??= {prefix}Keys(null, this);");
writer.WriteLine($"public IEnumerable<{v}> Values => field ??= {prefix}Values(null, this);");
writer.WriteLine($"public int Count => {prefix}Count(null, {objRefName});");
writer.WriteLine($"public bool ContainsKey({k} key) => {prefix}ContainsKey(null, {objRefName}, key);");
writer.WriteLine($"public bool TryGetValue({k} key, out {v} value) => {prefix}TryGetValue(null, {objRefName}, key, out value);");
Expand Down Expand Up @@ -380,24 +388,27 @@ private static void EmitList(IndentedTextWriter writer, ProjectionEmitContext co

/// <summary>
/// Emits a single <c>[UnsafeAccessor]</c> static extern declaration that targets a method on a
/// WinRT.Interop helper type. The function signature is built from the supplied parts.
/// WinRT.Interop helper type. The function signature is built from the supplied parts. The
/// <paramref name="receiver"/> defaults to the interface object reference
/// (<c>WindowsRuntimeObjectReference objRef</c>); a few accessors (e.g. dictionary
/// <c>Keys</c>/<c>Values</c>) instead take the projected runtime class (<c>WindowsRuntimeObject</c>).
/// </summary>
private static void EmitUnsafeAccessor(IndentedTextWriter writer, string accessName, string returnType, string functionName, string interopType, string extraParams)
private static void EmitUnsafeAccessor(IndentedTextWriter writer, string accessName, string returnType, string functionName, string interopType, string extraParams, string receiver = "WindowsRuntimeObjectReference objRef")
{
UnsafeAccessorFactory.EmitStaticMethod(
writer,
accessName: accessName,
returnType: returnType,
functionName: functionName,
interopType: interopType,
parameterList: $"WindowsRuntimeObjectReference objRef{extraParams}");
parameterList: $"{receiver}{extraParams}");
writer.WriteLine();
}

/// <summary>
/// Emits a sequence of <c>[UnsafeAccessor]</c> static extern declarations sharing the same
/// <paramref name="interopType"/>. Each row of <paramref name="accessors"/> is forwarded to
/// <see cref="EmitUnsafeAccessor(IndentedTextWriter, string, string, string, string, string)"/>.
/// <see cref="EmitUnsafeAccessor(IndentedTextWriter, string, string, string, string, string, string)"/>.
/// Used by the collection-stub emitters which emit table-shaped sets of accessors.
/// </summary>
private static void EmitUnsafeAccessors(
Expand Down