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
5 changes: 5 additions & 0 deletions src/Files.App.CsWin32/Files.App.CsWin32.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,9 @@
<PackageReference Include="Dongle.GuidRVAGen" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Files.Shared\Files.Shared.csproj" />
<ProjectReference Include="..\Files.Core.SourceGenerator\Files.Core.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>

</Project>
20 changes: 5 additions & 15 deletions src/Files.App.CsWin32/IStorageProviderQuotaUI.cs
Original file line number Diff line number Diff line change
@@ -1,29 +1,19 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

using Files.Shared.Attributes;
using System;
using System.Runtime.CompilerServices;
using Windows.Win32.Foundation;

namespace Windows.Win32.System.WinRT
{
public unsafe partial struct IStorageProviderQuotaUI : IComIID
{
#pragma warning disable CS0649 // Field 'field' is never assigned to, and will always have its default value 'value'
private void** lpVtbl;
#pragma warning restore CS0649 // Field 'field' is never assigned to, and will always have its default value 'value'
[GeneratedVTableFunction(Index = 6)]
public partial HRESULT GetQuotaTotalInBytes(ulong* value);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HRESULT GetQuotaTotalInBytes(ulong* value)
{
return (HRESULT)((delegate* unmanaged[MemberFunction]<IStorageProviderQuotaUI*, ulong*, int>)(lpVtbl[6]))((IStorageProviderQuotaUI*)Unsafe.AsPointer(ref this), value);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HRESULT GetQuotaUsedInBytes(ulong* value)
{
return (HRESULT)((delegate* unmanaged[MemberFunction]<IStorageProviderQuotaUI*, ulong*, int>)(lpVtbl[8]))((IStorageProviderQuotaUI*)Unsafe.AsPointer(ref this), value);
}
[GeneratedVTableFunction(Index = 8)]
public partial HRESULT GetQuotaUsedInBytes(ulong* value);

[GuidRVAGen.Guid("BA6295C3-312E-544F-9FD5-1F81B21F3649")]
public static partial ref readonly Guid Guid { get; }
Expand Down
13 changes: 3 additions & 10 deletions src/Files.App.CsWin32/IStorageProviderStatusUI.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

using Files.Shared.Attributes;
using System;
using System.Runtime.CompilerServices;
using Windows.Win32.Foundation;

namespace Windows.Win32.System.WinRT
{
public unsafe partial struct IStorageProviderStatusUI : IComIID
{
#pragma warning disable CS0649 // Field 'field' is never assigned to, and will always have its default value 'value'
private void** lpVtbl;
#pragma warning restore CS0649 // Field 'field' is never assigned to, and will always have its default value 'value'

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HRESULT GetQuotaUI(IStorageProviderQuotaUI** result)
{
return (HRESULT)((delegate* unmanaged[MemberFunction]<IStorageProviderStatusUI*, IStorageProviderQuotaUI**, int>)lpVtbl[14])((IStorageProviderStatusUI*)Unsafe.AsPointer(ref this), result);
}
[GeneratedVTableFunction(Index = 14)]
public partial HRESULT GetQuotaUI(IStorageProviderQuotaUI** result);

[GuidRVAGen.Guid("D6B6A758-198D-5B80-977F-5FF73DA33118")]
public static partial ref readonly Guid Guid { get; }
Expand Down
13 changes: 3 additions & 10 deletions src/Files.App.CsWin32/IStorageProviderStatusUISource.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

using Files.Shared.Attributes;
using System;
using System.Runtime.CompilerServices;
using Windows.Win32.Foundation;

namespace Windows.Win32.System.WinRT
{
public unsafe partial struct IStorageProviderStatusUISource : IComIID
{
#pragma warning disable CS0649 // Field 'field' is never assigned to, and will always have its default value 'value'
private void** lpVtbl;
#pragma warning restore CS0649 // Field 'field' is never assigned to, and will always have its default value 'value'

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HRESULT GetStatusUI(IStorageProviderStatusUI** result)
{
return (HRESULT)((delegate* unmanaged[MemberFunction]<IStorageProviderStatusUISource*, IStorageProviderStatusUI**, int>)lpVtbl[6])((IStorageProviderStatusUISource*)Unsafe.AsPointer(ref this), result);
}
[GeneratedVTableFunction(Index = 6)]
public partial HRESULT GetStatusUI(IStorageProviderStatusUI** result);

[GuidRVAGen.Guid("A306C249-3D66-5E70-9007-E43DF96051FF")]
public static partial ref readonly Guid Guid { get; }
Expand Down
13 changes: 3 additions & 10 deletions src/Files.App.CsWin32/IStorageProviderStatusUISourceFactory.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

using Files.Shared.Attributes;
using System;
using System.Runtime.CompilerServices;
using Windows.Win32.Foundation;

namespace Windows.Win32.System.WinRT
{
public unsafe partial struct IStorageProviderStatusUISourceFactory : IComIID
{
#pragma warning disable CS0649 // Field 'field' is never assigned to, and will always have its default value 'value'
private void** lpVtbl;
#pragma warning restore CS0649 // Field 'field' is never assigned to, and will always have its default value 'value'

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HRESULT GetStatusUISource(nint syncRootId, IStorageProviderStatusUISource** result)
{
return (HRESULT)((delegate* unmanaged[MemberFunction]<IStorageProviderStatusUISourceFactory*, nint, IStorageProviderStatusUISource**, int>)lpVtbl[6])((IStorageProviderStatusUISourceFactory*)Unsafe.AsPointer(ref this), syncRootId, result);
}
[GeneratedVTableFunction(Index = 6)]
public partial HRESULT GetStatusUISource(nint syncRootId, IStorageProviderStatusUISource** result);

[GuidRVAGen.Guid("12E46B74-4E5A-58D1-A62F-0376E8EE7DD8")]
public static partial ref readonly Guid Guid { get; }
Expand Down
3 changes: 0 additions & 3 deletions src/Files.App.CsWin32/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,7 @@ WinVerifyTrust
FileTimeToSystemTime
FileTimeToLocalFileTime
SystemTimeToFileTime
CRYPTOAPI_BLOB
CMSG_SIGNER_INFO
SignDataHandle
CRYPT_ATTRIBUTE
FILETIME
CRYPT_BIT_BLOB
Expand All @@ -266,6 +264,5 @@ CATALOG_INFO
WINTRUST_FILE_INFO
WINTRUST_DATA
HCERTSTORE
HCRYPTMSG
CERT_QUERY_ENCODING_TYPE
CertGetNameString
7 changes: 7 additions & 0 deletions src/Files.Core.SourceGenerator/Data/ParameterTypeNamePair.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

namespace Files.Core.SourceGenerator.Data
{
internal record ParameterTypeNamePair(string FullyQualifiedTypeName, string ValueName);
}
15 changes: 15 additions & 0 deletions src/Files.Core.SourceGenerator/Data/VTableFunctionInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

namespace Files.Core.SourceGenerator.Data
{
internal record VTableFunctionInfo(
string FullyQualifiedParentTypeName,
string ParentTypeNamespace,
string ParentTypeName,
bool IsReturnTypeVoid,
string Name,
string ReturnTypeName,
int Index,
EquatableArray<ParameterTypeNamePair> Parameters);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
<PlatformTarget>AnyCPU</PlatformTarget>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<Configurations>Debug;Release</Configurations>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsRoslynComponent>true</IsRoslynComponent>
</PropertyGroup>

<ItemGroup>
Expand Down
115 changes: 115 additions & 0 deletions src/Files.Core.SourceGenerator/Generators/VTableFunctionGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

namespace Files.Core.SourceGenerator.Generators
{
[Generator(LanguageNames.CSharp)]

Check warning on line 6 in src/Files.Core.SourceGenerator/Generators/VTableFunctionGenerator.cs

View workflow job for this annotation

GitHub Actions / build (Release, x64)

This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces. The Microsoft.CodeAnalysis.Workspaces assembly is not provided during command line compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. (https://github.com/dotnet/roslyn/blob/main/docs/roslyn-analyzers/rules/RS1038.md)

Check warning on line 6 in src/Files.Core.SourceGenerator/Generators/VTableFunctionGenerator.cs

View workflow job for this annotation

GitHub Actions / build (Debug, arm64)

This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces. The Microsoft.CodeAnalysis.Workspaces assembly is not provided during command line compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. (https://github.com/dotnet/roslyn/blob/main/docs/roslyn-analyzers/rules/RS1038.md)

Check warning on line 6 in src/Files.Core.SourceGenerator/Generators/VTableFunctionGenerator.cs

View workflow job for this annotation

GitHub Actions / build (Debug, x64)

This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces. The Microsoft.CodeAnalysis.Workspaces assembly is not provided during command line compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. (https://github.com/dotnet/roslyn/blob/main/docs/roslyn-analyzers/rules/RS1038.md)

Check warning on line 6 in src/Files.Core.SourceGenerator/Generators/VTableFunctionGenerator.cs

View workflow job for this annotation

GitHub Actions / build (Release, arm64)

This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces. The Microsoft.CodeAnalysis.Workspaces assembly is not provided during command line compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. (https://github.com/dotnet/roslyn/blob/main/docs/roslyn-analyzers/rules/RS1038.md)
internal class VTableFunctionGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var sources = context.SyntaxProvider.ForAttributeWithMetadataName(
"Files.Shared.Attributes.GeneratedVTableFunctionAttribute",
static (node, token) =>
{
token.ThrowIfCancellationRequested();

// Check if the method has partial modifier and is public or internal (and not static)
if (node is not MethodDeclarationSyntax { AttributeLists.Count: > 0 } method ||
!method.Modifiers.Any(SyntaxKind.PartialKeyword) ||
!(method.Modifiers.Any(SyntaxKind.PublicKeyword) || method.Modifiers.Any(SyntaxKind.InternalKeyword)) ||
method.Modifiers.Any(SyntaxKind.StaticKeyword))
return false;

// Check if the type containing the method has partial modifier and is a struct
if (node.Parent is not TypeDeclarationSyntax { Keyword.RawKind: (int)SyntaxKind.StructKeyword, Modifiers: { } modifiers } ||
!modifiers.Any(SyntaxKind.PartialKeyword))
return false;

return true;
},
static (context, token) =>
{
token.ThrowIfCancellationRequested();

var fullyQualifiedParentTypeName = context.TargetSymbol.ContainingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
var structNamespace = context.TargetSymbol.ContainingType.ContainingNamespace.ToString();
var structName = context.TargetSymbol.ContainingType.Name;
var methodSymbol = (IMethodSymbol)context.TargetSymbol;
var isReturnTypeVoid = methodSymbol.ReturnsVoid;
var functionName = methodSymbol.Name;
var returnTypeName = methodSymbol.ReturnType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
var parameters = methodSymbol.Parameters.Select(x => new ParameterTypeNamePair(x.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), x.Name));
var index = (int)context.Attributes[0].NamedArguments.FirstOrDefault(x => x.Key.Equals("Index")).Value.Value!;

return new VTableFunctionInfo(fullyQualifiedParentTypeName, structNamespace, structName, isReturnTypeVoid, functionName, returnTypeName, index, new(parameters.ToImmutableArray()));
})
.Where(static item => item is not null)
.Collect()
.Select((items, token) =>
{
token.ThrowIfCancellationRequested();

return items.GroupBy(source => source.FullyQualifiedParentTypeName, StringComparer.OrdinalIgnoreCase).ToImmutableArray();
});


context.RegisterSourceOutput(sources, (context, sources) =>
{
foreach (var source in sources)
{
var fileName = $"{source.ToImmutableArray().ElementAt(0).ParentTypeNamespace}.{source.ToImmutableArray().ElementAt(0).ParentTypeName}_VTableFunctions.g.cs";
var generatedCSharpCode = GenerateVtableFunctionsForStruct(source.ToImmutableArray());

context.AddSource(fileName, generatedCSharpCode);
}
});
}

private string GenerateVtableFunctionsForStruct(ImmutableArray<VTableFunctionInfo> sources)
{
StringBuilder builder = new();

builder.AppendLine($"// <auto-generated/>");
builder.AppendLine();
builder.AppendLine($"using global::System.Runtime.CompilerServices;");
builder.AppendLine();
builder.AppendLine($"#pragma warning disable");
builder.AppendLine();

builder.AppendLine($"namespace {sources.ElementAt(0).ParentTypeNamespace};");
builder.AppendLine();

builder.AppendLine($"public unsafe partial struct {sources.ElementAt(0).ParentTypeName}");
builder.AppendLine($"{{");

builder.AppendLine($" private void** lpVtbl;");
builder.AppendLine();

var sourceIndex = 0;
var sourceCount = sources.Count();

foreach (var source in sources)
{
var returnTypeName = source.IsReturnTypeVoid ? "void" : "int";

builder.AppendLine($" [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]");

builder.AppendLine($" public partial {source.ReturnTypeName} {source.Name}({string.Join(", ", source.Parameters.Select(x => $"{x.FullyQualifiedTypeName} {x.ValueName}"))})");
builder.AppendLine($" {{");
builder.AppendLine($" return ({source.ReturnTypeName})((delegate* unmanaged[MemberFunction]<{sources.ElementAt(0).FullyQualifiedParentTypeName}*, {string.Join(", ", source.Parameters.Select(x => $"{x.FullyQualifiedTypeName}"))}, {returnTypeName}>)(lpVtbl[{source.Index}]))");
builder.AppendLine($" (({sources.ElementAt(0).FullyQualifiedParentTypeName}*)global::System.Runtime.CompilerServices.Unsafe.AsPointer(ref this), {string.Join(", ", source.Parameters.Select(x => $"{x.ValueName}"))});");
builder.AppendLine($" }}");

if (sourceIndex < sourceCount - 1)
builder.AppendLine();

sourceIndex++;
}

builder.AppendLine($"}}");

return builder.ToString();
}
}
}
20 changes: 20 additions & 0 deletions src/Files.Core.SourceGenerator/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"profiles": {
"Files.App.CsWin32": {
"commandName": "DebugRoslynComponent",
"targetProject": "..\\Files.App.CsWin32\\Files.App.CsWin32.csproj"
},
"Files.App.Controls": {
"commandName": "DebugRoslynComponent",
"targetProject": "..\\Files.App.Controls\\Files.App.Controls.csproj"
},
"Files.App.Server": {
"commandName": "DebugRoslynComponent",
"targetProject": "..\\Files.App.Server\\Files.App.Server.csproj"
},
"Files.App": {
"commandName": "DebugRoslynComponent",
"targetProject": "..\\Files.App\\Files.App.csproj"
}
}
}
Loading
Loading