├── .github └── workflows │ ├── build-debug.yml │ ├── build-release.yml │ └── stale.yml ├── .gitignore ├── Directory.Build.props ├── Icon.png ├── LICENSE.md ├── PrivateProxy.sln ├── README.md ├── opensource.snk ├── sandbox ├── ClassLibrary │ ├── Class1.cs │ └── ClassLibrary.csproj └── ConsoleAppNet8 │ ├── BlogSample.cs │ ├── ConsoleAppNet8.csproj │ └── Program.cs ├── src └── PrivateProxy.Generator │ ├── DiagnosticDescriptors.cs │ ├── EmitHelper.cs │ ├── MetaMember.cs │ ├── PrivateProxy.Generator.csproj │ ├── PrivateProxyGenerator.cs │ └── Properties │ └── launchSettings.json └── tests └── PrivateProxy.Tests ├── ChangeKindTest.cs ├── ClassGenerateTest.cs ├── DiagnosticsTest.cs ├── GenerateInternalTest.cs ├── GlobalUsings.cs ├── OtherTypesTest.cs ├── PrivateProxy.Tests.csproj ├── RefStructGenerateTest.cs ├── StructGenerateTest.cs └── Utils └── CsharpGeneratorRunner.cs /.github/workflows/build-debug.yml: -------------------------------------------------------------------------------- 1 | name: Build-Debug 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - "main" 8 | pull_request: 9 | branches: 10 | - "main" 11 | 12 | jobs: 13 | build-dotnet: 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 10 16 | steps: 17 | - uses: actions/checkout@v3 18 | - uses: Cysharp/Actions/.github/actions/setup-dotnet@main 19 | with: 20 | dotnet-version: | 21 | 8.0.x 22 | - run: dotnet build -c Debug 23 | - run: dotnet test -c Debug --no-build -------------------------------------------------------------------------------- /.github/workflows/build-release.yml: -------------------------------------------------------------------------------- 1 | name: Build-Release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | tag: 7 | description: "tag: git tag you want create. (sample 1.0.0)" 8 | required: true 9 | dry-run: 10 | description: "dry-run: true will never create relase/nuget." 11 | required: true 12 | default: false 13 | type: boolean 14 | 15 | jobs: 16 | build-dotnet: 17 | runs-on: ubuntu-latest 18 | timeout-minutes: 10 19 | steps: 20 | - uses: actions/checkout@v3 21 | - uses: Cysharp/Actions/.github/actions/setup-dotnet@main 22 | # pack nuget 23 | - run: dotnet build -c Release -p:Version=${{ inputs.tag }} 24 | - run: dotnet test -c Release --no-build 25 | - run: dotnet pack -c Release --no-build -p:Version=${{ inputs.tag }} -o ./publish 26 | # Store artifacts. 27 | - uses: Cysharp/Actions/.github/actions/upload-artifact@main 28 | with: 29 | name: nuget 30 | path: ./publish/ 31 | retention-days: 1 32 | 33 | create-release: 34 | needs: [build-dotnet] 35 | uses: Cysharp/Actions/.github/workflows/create-release.yaml@main 36 | with: 37 | commit-id: ${{ needs.update-packagejson.outputs.sha }} 38 | tag: ${{ inputs.tag }} 39 | dry-run: ${{ inputs.dry-run }} 40 | nuget-push: true 41 | release-upload: false 42 | secrets: inherit 43 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: "Close stale issues" 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | 8 | jobs: 9 | stale: 10 | uses: Cysharp/Actions/.github/workflows/stale-issue.yaml@main 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | 5 | # mstest test results 6 | TestResults 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.sln.docstates 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Rr]elease/ 19 | *_i.c 20 | *_p.c 21 | *.ilk 22 | *.obj 23 | *.pch 24 | *.pdb 25 | *.pgc 26 | *.pgd 27 | *.rsp 28 | *.sbr 29 | *.tlb 30 | *.tli 31 | *.tlh 32 | *.tmp 33 | *.log 34 | *.vspscc 35 | *.vssscc 36 | .builds 37 | 38 | # Visual C++ cache files 39 | ipch/ 40 | *.aps 41 | *.ncb 42 | *.opensdf 43 | *.sdf 44 | 45 | # Visual Studio profiler 46 | *.psess 47 | *.vsp 48 | *.vspx 49 | 50 | # Guidance Automation Toolkit 51 | *.gpState 52 | 53 | # ReSharper is a .NET coding add-in 54 | _ReSharper* 55 | 56 | # NCrunch 57 | *.ncrunch* 58 | .*crunch*.local.xml 59 | 60 | # Installshield output folder 61 | [Ee]xpress 62 | 63 | # DocProject is a documentation generator add-in 64 | DocProject/buildhelp/ 65 | DocProject/Help/*.HxT 66 | DocProject/Help/*.HxC 67 | DocProject/Help/*.hhc 68 | DocProject/Help/*.hhk 69 | DocProject/Help/*.hhp 70 | DocProject/Help/Html2 71 | DocProject/Help/html 72 | 73 | # Click-Once directory 74 | publish 75 | 76 | # Publish Web Output 77 | *.Publish.xml 78 | 79 | # NuGet Packages Directory 80 | # packages # upm pacakge will use Packages 81 | 82 | # Windows Azure Build Output 83 | csx 84 | *.build.csdef 85 | 86 | # Windows Store app package directory 87 | AppPackages/ 88 | 89 | # Others 90 | [Bb]in 91 | [Oo]bj 92 | sql 93 | TestResults 94 | [Tt]est[Rr]esult* 95 | *.Cache 96 | ClientBin 97 | [Ss]tyle[Cc]op.* 98 | ~$* 99 | *.dbmdl 100 | Generated_Code #added for RIA/Silverlight projects 101 | 102 | # Backup & report files from converting an old project file to a newer 103 | # Visual Studio version. Backup files are not needed, because we have git ;-) 104 | _UpgradeReport_Files/ 105 | Backup*/ 106 | UpgradeLog*.XML 107 | .vs/config/applicationhost.config 108 | .vs/restore.dg 109 | 110 | nuget/tools/* 111 | nuget/*.nupkg 112 | nuget/*.unitypackage 113 | .vs/ 114 | 115 | # Jetbrains Rider 116 | .idea/ 117 | 118 | __packages/ 119 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | latest 4 | enable 5 | true 6 | $(NoWarn);CS1591 7 | true 8 | $(MSBuildThisFileDirectory)opensource.snk 9 | 10 | 11 | $(Version) 12 | Cysharp 13 | Cysharp 14 | © Cysharp, Inc. 15 | https://github.com/Cysharp/PrivateProxy 16 | $(PackageProjectUrl) 17 | git 18 | MIT 19 | Icon.png 20 | $(MSBuildThisFileDirectory)opensource.snk 21 | 22 | -------------------------------------------------------------------------------- /Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cysharp/PrivateProxy/8ddcd17466b423f77799dc3897438c40676037f6/Icon.png -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Cysharp, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PrivateProxy.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.8.34112.27 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F2E8F22F-A91D-457A-932A-E3647B964A94}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{FB22D44B-8937-4C9F-92A6-5A0A4190D1A2}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sandbox", "sandbox", "{7753D4E4-4967-4ED2-97CC-5B1A71B53BCD}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PrivateProxy.Generator", "src\PrivateProxy.Generator\PrivateProxy.Generator.csproj", "{57EEAB5C-EDFE-4186-8101-8B969A57CB6F}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleAppNet8", "sandbox\ConsoleAppNet8\ConsoleAppNet8.csproj", "{F1349485-DCA7-4F9B-B434-DC6950988E7A}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PrivateProxy.Tests", "tests\PrivateProxy.Tests\PrivateProxy.Tests.csproj", "{06BE6B13-4795-4E98-B1F4-BE3D10270C6C}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary", "sandbox\ClassLibrary\ClassLibrary.csproj", "{23947225-D1A8-4097-B758-731E1D375CB0}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {57EEAB5C-EDFE-4186-8101-8B969A57CB6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {57EEAB5C-EDFE-4186-8101-8B969A57CB6F}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {57EEAB5C-EDFE-4186-8101-8B969A57CB6F}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {57EEAB5C-EDFE-4186-8101-8B969A57CB6F}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {F1349485-DCA7-4F9B-B434-DC6950988E7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {F1349485-DCA7-4F9B-B434-DC6950988E7A}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {F1349485-DCA7-4F9B-B434-DC6950988E7A}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {F1349485-DCA7-4F9B-B434-DC6950988E7A}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {06BE6B13-4795-4E98-B1F4-BE3D10270C6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {06BE6B13-4795-4E98-B1F4-BE3D10270C6C}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {06BE6B13-4795-4E98-B1F4-BE3D10270C6C}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {06BE6B13-4795-4E98-B1F4-BE3D10270C6C}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {23947225-D1A8-4097-B758-731E1D375CB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {23947225-D1A8-4097-B758-731E1D375CB0}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {23947225-D1A8-4097-B758-731E1D375CB0}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {23947225-D1A8-4097-B758-731E1D375CB0}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | GlobalSection(NestedProjects) = preSolution 47 | {57EEAB5C-EDFE-4186-8101-8B969A57CB6F} = {F2E8F22F-A91D-457A-932A-E3647B964A94} 48 | {F1349485-DCA7-4F9B-B434-DC6950988E7A} = {7753D4E4-4967-4ED2-97CC-5B1A71B53BCD} 49 | {06BE6B13-4795-4E98-B1F4-BE3D10270C6C} = {FB22D44B-8937-4C9F-92A6-5A0A4190D1A2} 50 | {23947225-D1A8-4097-B758-731E1D375CB0} = {7753D4E4-4967-4ED2-97CC-5B1A71B53BCD} 51 | EndGlobalSection 52 | GlobalSection(ExtensibilityGlobals) = postSolution 53 | SolutionGuid = {800D301B-C4F2-454D-87B9-BA3A404A9937} 54 | EndGlobalSection 55 | EndGlobal 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!Important] 2 | > The current Roslyn/Source Generator has limitations in accessing private members of referenced projects. Moreover, accessing private members can lead to situations where it works fine in the IDE but fails during the build process. For more details, please refer to the Roslyn issue, [MetadataImportOptions.All doesn't work in source generators?](https://github.com/dotnet/roslyn/issues/72369). To avoid confusion, I will archive this until the relationship between Source Generators and private members is improved. 3 | 4 | # PrivateProxy 5 | [![GitHub Actions](https://github.com/Cysharp/PrivateProxy/workflows/Build-Debug/badge.svg)](https://github.com/Cysharp/PrivateProxy/actions) [![Releases](https://img.shields.io/github/release/Cysharp/PrivateProxy.svg)](https://github.com/Cysharp/PrivateProxy/releases) 6 | [![NuGet package](https://img.shields.io/nuget/v/PrivateProxy.svg)](https://nuget.org/packages/PrivateProxy) 7 | 8 | Source Generator and .NET 8 [UnsafeAccessor](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.unsafeaccessorattribute) based high-performance strongly-typed private accessor for unit testing and runtime. 9 | 10 | `[GeneratePrivateProxy(typeof(TargetType))]` generates accessor proxy. 11 | 12 | ```csharp 13 | using PrivateProxy; 14 | 15 | public class Sample 16 | { 17 | int _field1; 18 | int PrivateAdd(int x, int y) => x + y; 19 | } 20 | 21 | [GeneratePrivateProxy(typeof(Sample))] 22 | public partial struct SampleProxy; 23 | ``` 24 | 25 | ```csharp 26 | // Source Generator generate this type 27 | partial struct SampleProxy(Sample target) 28 | { 29 | [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_field1")] 30 | static extern ref int ___field1__(Sample target); 31 | 32 | [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "PrivateAdd")] 33 | static extern int __PrivateAdd__(Sample target, int x, int y); 34 | 35 | public ref int _field1 => ref ___field1__(target); 36 | public int PrivateAdd(int x, int y) => __PrivateAdd__(target, x, y); 37 | } 38 | 39 | public static class SamplePrivateProxyExtensions 40 | { 41 | public static SampleProxy AsPrivateProxy(this Sample target) 42 | { 43 | return new SampleProxy(target); 44 | } 45 | } 46 | ``` 47 | 48 | ```csharp 49 | // You can access like this. 50 | var sample = new Sample(); 51 | sample.AsPrivateProxy()._field1 = 10; 52 | ``` 53 | 54 | Generated code is fully typed, you can access private filed via IntelliSense and when private field was changed, can check compiler error. 55 | 56 | ![image](https://github.com/Cysharp/MemoryPack/assets/46207/f6dd22e1-e82e-4acc-ba6e-8895c8c8734b) 57 | 58 | * No performance penalty, it can be used not only for unit testing but also for runtime 59 | * No runtime dependency(all codes are included in source generator) 60 | * Private accessors are strongly-typed 61 | * Supports both instance, static and fields, properties, methods 62 | * Supports `ref`, `out`, `in`, and `ref readonly` method parameters 63 | * Supports `readonly` field and property 64 | * Supports `ref` return 65 | * Supports mutable struct 66 | * Supports instance constructor 67 | 68 | For example, this is the mutable struct and static, ref return, and constructor sample. 69 | 70 | ```csharp 71 | using PrivateProxy; 72 | 73 | public struct MutableStructSample 74 | { 75 | int _counter; 76 | void Increment() => _counter++; 77 | 78 | // static and ref sample 79 | static ref int GetInstanceCounter(ref MutableStructSample sample) => ref sample._counter; 80 | 81 | // constructor sample 82 | MutalbeStructSample(int x, int y) { /* ... */ } 83 | } 84 | 85 | // use ref partial struct 86 | [GeneratePrivateProxy(typeof(MutableStructSample))] 87 | public ref partial struct MutableStructSampleProxy; 88 | ``` 89 | 90 | ```csharp 91 | var sample = new MutableStructSample(); 92 | var proxy = sample.AsPrivateProxy(); 93 | proxy.Increment(); 94 | proxy.Increment(); 95 | proxy.Increment(); 96 | 97 | // call private static method. 98 | ref var counter = ref MutableStructSampleProxy.GetInstanceCounter(ref sample); 99 | 100 | Console.WriteLine(counter); // 3 101 | counter = 9999; 102 | Console.WriteLine(proxy._counter); // 9999 103 | 104 | // call private constructor and create instance. 105 | var sample = MutableStructSampleProxy.CreateMutableStructFromConstructor(111, 222); 106 | ``` 107 | 108 | Installation 109 | --- 110 | This library is distributed via NuGet, minimum requirement is .NET 8 and C# 12. 111 | 112 | PM> Install-Package [PrivateProxy](https://www.nuget.org/packages/PrivateProxy) 113 | 114 | Package provides only analyzer and generated code does not dependent any other libraries. 115 | 116 | Supported Members 117 | --- 118 | `GeneratePrivateProxy` target type and member 119 | 120 | ```csharp 121 | public class/* struct */ SupportTarget 122 | { 123 | // field 124 | private int field; 125 | private readonly int readOnlyField; 126 | 127 | // property 128 | private int Property { get; set; } 129 | private int GetOnlyProperty { get; } 130 | public int GetOnlyPrivateProperty { private get; set; } 131 | public int SetOnlyPrivateProperty { get; private set; } 132 | private int SetOnlyProperty { set => field = value; } 133 | private ref int RefGetOnlyProperty => ref field; 134 | private ref readonly int RefReadOnlyGetOnlyProperty => ref field; 135 | 136 | // method 137 | private void VoidMethod() { } 138 | private int ReturnMethod() => field; 139 | private int ParameterMethod(int x, int y) => x + y; 140 | private void RefOutInMethod(in int x, ref int y, out int z, ref readonly int xyz) { z = field; } 141 | private ref int RefReturnMethod() => ref field; 142 | private ref readonly int RefReadOnlyReturnMethod() => ref field; 143 | 144 | // static 145 | static int staticField; 146 | static readonly int staticReadOnlyField; 147 | static int StaticProperty { get; set; } 148 | static int StaticGetOnlyProperty { get; } 149 | public static int StaticGetOnlyPrivateProperty { private get; set; } 150 | public static int StaticSetOnlyPrivateProperty { get; private set; } 151 | private static int StaticSetOnlyProperty { set => staticField = value; } 152 | private static ref int StaticRefGetOnlyProperty => ref staticField; 153 | private static ref readonly int StaticRefReadOnlyGetOnlyProperty => ref staticField; 154 | private static void StaticVoidMethod() { } 155 | static int StaticReturnMethod() => staticField; 156 | static int StaticParameterMethod(int x, int y) => x + y; 157 | static void StaticRefOutInMethod(in int x, ref int y, out int z, ref readonly int xyz) { z = staticField; } 158 | static ref int StaticRefReturnMethod() => ref staticField; 159 | static ref readonly int StaticRefReadOnlyReturnMethod() => ref staticField; 160 | static ref int StaticRefReturnMethodParameter() => ref staticField; 161 | 162 | // constructor 163 | SupportTarget() { } 164 | SupportTarget(int x, int y) { } 165 | } 166 | ``` 167 | 168 | Proxy type can be `class` => `class` or `struct`, `struct` => `ref struct`. 169 | 170 | ```csharp 171 | using PrivateProxy; 172 | 173 | public class Sample; 174 | 175 | // class proxy type both supports class and struct(recommend is struct) 176 | [GeneratePrivateProxy(typeof(Sample))] 177 | public partial class SampleProxy1; 178 | 179 | [GeneratePrivateProxy(typeof(Sample))] 180 | public partial struct SampleProxy2; 181 | 182 | public struct SamplleStruct; 183 | 184 | // struct only supports ref struct(when use standard struct, analyzer shows error) 185 | [GeneratePrivateProxy(typeof(SamplleStruct))] 186 | public ref partial struct SamplleStructProxy; 187 | ``` 188 | 189 | GeneratePrivateProxyAttribute has two constructor, when use `PrivateProxyGenerateKinds` parameter, can configure generate member kind. 190 | 191 | ```csharp 192 | public GeneratePrivateProxyAttribute(Type target) // use PrivateProxyGenerateKinds.All 193 | public GeneratePrivateProxyAttribute(Type target, PrivateProxyGenerateKinds generateKinds) 194 | 195 | [Flags] 196 | internal enum PrivateProxyGenerateKinds 197 | { 198 | All = 0, // Field | Method | Property | Instance | Static | Constructor 199 | Field = 1, 200 | Method = 2, 201 | Property = 4, 202 | Instance = 8, 203 | Static = 16, 204 | Constructor = 32, 205 | } 206 | ``` 207 | 208 | Limitation 209 | --- 210 | Currently, the following features are not supported 211 | 212 | * Generics type 213 | * see: [dotnet/runtime#89439 Implement unbound Generic support for UnsafeAccessorAttribute](https://github.com/dotnet/runtime/issues/89439) 214 | * Static class, Non public return/parameter type(ignore generate) 215 | * see: [dotnet/runtime#90081 UnsafeAccessorTypeAttribute for static or private type access](https://github.com/dotnet/runtime/issues/90081) 216 | * ref struct 217 | * ref field can not pass to ref method parameter 218 | * Types from external dll(for example `String`) 219 | * Probably, by enabling MetadataImportOptions.All in the Source Generator, it should be possible to read it. However, I haven't been able to find a way to do that. I need help. 220 | 221 | License 222 | --- 223 | This library is licensed under the MIT License. 224 | -------------------------------------------------------------------------------- /opensource.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cysharp/PrivateProxy/8ddcd17466b423f77799dc3897438c40676037f6/opensource.snk -------------------------------------------------------------------------------- /sandbox/ClassLibrary/Class1.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS8604 2 | #pragma warning disable CS8321 3 | #pragma warning disable CS0414 4 | 5 | using System.Runtime.CompilerServices; 6 | using System.Runtime.InteropServices.Marshalling; 7 | 8 | namespace ClassLibrary 9 | { 10 | public class MyClass 11 | { 12 | private int _privateField; 13 | private NestedPrivate _nesterPrivate = new NestedPrivate(); 14 | private NestedPublic _nesterPublic = new NestedPublic(); 15 | private InternalEnum _internalEnum = default; 16 | 17 | MyClass() 18 | { 19 | } 20 | 21 | private void PrivateMethod() 22 | { 23 | _privateField++; 24 | } 25 | 26 | class NestedPrivate 27 | { 28 | 29 | } 30 | 31 | public class NestedPublic 32 | { 33 | 34 | } 35 | } 36 | 37 | internal enum InternalEnum 38 | { 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /sandbox/ClassLibrary/ClassLibrary.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | false 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /sandbox/ConsoleAppNet8/BlogSample.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS8604 2 | #pragma warning disable CS8321 3 | #pragma warning disable CS0414 4 | #pragma warning disable CS0169 5 | #pragma warning disable CS0649 6 | 7 | using PrivateProxy; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Runtime.CompilerServices; 12 | using System.Security.Principal; 13 | using System.Text; 14 | using System.Threading.Tasks; 15 | 16 | public class Sample 17 | { 18 | int _field1; 19 | int _field2; 20 | public int PrivateProperty { get; private set; } 21 | int PrivateAdd(int x, int y) => x + y; 22 | 23 | Sample() 24 | { 25 | } 26 | 27 | Sample(int x, int y) 28 | { 29 | _field1 = x; 30 | _field2 = y; 31 | } 32 | } 33 | 34 | [GeneratePrivateProxy(typeof(Sample))] 35 | public partial struct SampleProxy; 36 | 37 | 38 | public struct MutableStructSample 39 | { 40 | int _counter; 41 | void Increment() => _counter++; 42 | 43 | // static and ref sample 44 | static ref int GetInstanceCounter(ref MutableStructSample sample) => ref sample._counter; 45 | } 46 | 47 | // use ref partial struct 48 | [GeneratePrivateProxy(typeof(MutableStructSample))] 49 | public ref partial struct MutableStructSampleProxy; 50 | 51 | 52 | public static class Calle 53 | { 54 | public static void Foo() 55 | { 56 | var sample = new MutableStructSample(); 57 | var proxy = sample.AsPrivateProxy(); 58 | proxy.Increment(); 59 | proxy.Increment(); 60 | proxy.Increment(); 61 | 62 | // call private static method. 63 | ref var counter = ref MutableStructSampleProxy.GetInstanceCounter(ref sample); 64 | 65 | Console.WriteLine(counter); // 3 66 | counter = 9999; 67 | Console.WriteLine(proxy._counter); // 9999 68 | } 69 | } -------------------------------------------------------------------------------- /sandbox/ConsoleAppNet8/ConsoleAppNet8.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | disable 7 | enable 8 | false 9 | 10 | 11 | 12 | 13 | Analyzer 14 | false 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /sandbox/ConsoleAppNet8/Program.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS8604 2 | #pragma warning disable CS8321 3 | #pragma warning disable CS0414 4 | #pragma warning disable CS0169 5 | #pragma warning disable CS0649 6 | 7 | // using PrivateProxy; 8 | using PrivateProxy; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Data; 12 | using System.Runtime.CompilerServices; 13 | using System.Runtime.InteropServices; 14 | 15 | 16 | Calle.Foo(); 17 | 18 | 19 | static void Bar(ref int x, out int y, in int z) 20 | { 21 | Foo(ref x, out y, in z); 22 | } 23 | 24 | 25 | static void Foo(ref int x, out int y, in int z) 26 | { 27 | y = 10; 28 | } 29 | 30 | 31 | 32 | 33 | [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "field")] 34 | static extern ref int UnsafeAccessorTest(ref global::StructTest target); 35 | 36 | [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "field")] 37 | static extern ref int UnsafeAccessorTest2(global::MyClass target); 38 | 39 | [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "TakoyakiX")] 40 | static extern int UnsafeAccessorTest3(in global::MyClass target, int x, int y); 41 | 42 | public partial struct Hoge 43 | { 44 | static Hoge ____default__ = default(Hoge); 45 | 46 | public void Tako() { } 47 | 48 | public int MyProperty { get; set; } 49 | static int y; 50 | public ref readonly int MyProperty2 { get => ref y; } 51 | 52 | public int Foo 53 | { 54 | set 55 | { 56 | } 57 | } 58 | 59 | 60 | [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "TakoyakiX")] 61 | static extern int UnsafeAccessorTest3(ref global::Hoge target, int x, int y); 62 | 63 | public static int CallTakoyakiX(int x, int y) => UnsafeAccessorTest3(ref ____default__, x, y); 64 | 65 | static int TakoyakiX(int x, int y) 66 | { 67 | return x + y; 68 | } 69 | } 70 | 71 | 72 | [GeneratePrivateProxy(typeof(ClassLibrary.MyClass))] 73 | public partial struct ClassLibraryMyClassProxy; 74 | 75 | 76 | public class MyClass 77 | { 78 | int field; 79 | 80 | public int MyProperty { get; set; } 81 | 82 | public void Show() => Console.WriteLine(field); 83 | 84 | 85 | public int Show2 => field; 86 | 87 | 88 | public ref readonly int RetField() => ref field; 89 | 90 | 91 | static int TakoyakiX(int x, int y) 92 | { 93 | return x + y; 94 | } 95 | } 96 | 97 | 98 | public static class ExtTest 99 | { 100 | public static MyClassProxy AsProxy2(this MyClass mc) 101 | { 102 | return new MyClassProxy(mc); 103 | } 104 | } 105 | 106 | 107 | //public ref struct MyClass 108 | //{ 109 | 110 | //} 111 | 112 | ref partial struct Tako 113 | { 114 | } 115 | 116 | public struct MyClassProxy 117 | { 118 | MyClass target; 119 | 120 | public MyClassProxy(MyClass target) 121 | { 122 | this.target = target; 123 | } 124 | } 125 | 126 | 127 | public struct TestRef 128 | { 129 | } 130 | 131 | public ref struct TestRefRef 132 | { 133 | ref TestRef target; 134 | 135 | public TestRefRef(ref TestRef target) 136 | { 137 | this.target = ref target; 138 | } 139 | 140 | 141 | 142 | [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "field")] 143 | static extern ref int __field__(ref global::TestRef target); 144 | 145 | public ref int field => ref __field__(ref target); 146 | 147 | public int field2 148 | { 149 | get => __field__(ref target); 150 | set => __field__(ref target) = value; 151 | } 152 | 153 | } 154 | 155 | 156 | 157 | public struct StructTest 158 | { 159 | //public StructTest(ref int xxx) 160 | //{ 161 | //this.refReadOnly = ref xxx; 162 | //} 163 | 164 | // Roslyn IFieldSymbol behaviour 165 | public int field; // RefKind=None 166 | public readonly int readOnlyField; // RefKind=None, IsReadOnly=true | can't assign 167 | //public ref int refField; // RefKind=Ref 168 | //public ref readonly int refReadOnly; // RefKind=In | can't assign 169 | //public readonly ref int readonlyRef; // RefKind=Ref, IsReadOnly=true 170 | //public readonly ref readonly int readOnlyRefReadOnly; // RefKind=In, IsReadOnly=true | can't assign 171 | 172 | 173 | 174 | 175 | // Roslyn IPropertySymbol behaviour 176 | public int Prop0 => 1; 177 | public readonly int Prop1 => 1; 178 | //public ref int Prop2 => ref refField; // ReturnsBy 179 | //public ref readonly int Prop3 => ref refField; 180 | //public readonly ref int Prop4 => ref refField; 181 | //public readonly ref readonly int Prop5 => ref refField; 182 | 183 | // Roslyn IMethodSYmbol behaviour 184 | public int Method0() => 1; 185 | public readonly int Method1() => 1; 186 | //public ref int Method2() => ref refField; 187 | //public ref readonly int Method3() => ref refField; 188 | //public readonly ref int Method4() => ref refField; 189 | //public readonly ref readonly int Method5() => ref refField; 190 | 191 | public void Show() 192 | { 193 | Console.WriteLine(field); 194 | } 195 | 196 | static void HogeMogeDayo() 197 | { 198 | } 199 | } 200 | 201 | //public ref partial struct RefStructTestProxy2 202 | //{ 203 | // RefStructTest target; 204 | 205 | // public RefStructTestProxy2(ref RefStructTest target) 206 | // { 207 | // this.target = target; 208 | // } 209 | 210 | // static global::RefStructTest ____static_instance => default; 211 | 212 | // public RefStructTestProxy2(ref RefStructTest test) 213 | // { 214 | // // this.target = MemoryMarshal.GetReference(test; 215 | // } 216 | 217 | 218 | // [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "HogeMogeDayo")] 219 | // static extern void __HogeMogeDayo__(in global::RefStructTest target); 220 | 221 | // public static void HogeMogeDayo() 222 | // { 223 | // __HogeMogeDayo__(____static_instance); 224 | // } 225 | 226 | 227 | //} 228 | 229 | 230 | 231 | 232 | [GeneratePrivateProxy(typeof(StructTest))] 233 | public ref partial struct StructTestProxy; 234 | 235 | 236 | 237 | partial struct PrivateClass2Proxy2 238 | { 239 | global::PrivateClass2 target; 240 | 241 | public PrivateClass2Proxy2(global::PrivateClass2 target) 242 | { 243 | this.target = target; 244 | } 245 | 246 | [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_privateField")] 247 | static extern ref int ___privateField__(global::PrivateClass2 target); 248 | public ref int _privateField => ref ___privateField__(target); 249 | 250 | [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_PrivateMyProperty")] 251 | static extern int __get_PrivateMyProperty__(global::PrivateClass2 target); 252 | 253 | [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_PrivateMyProperty")] 254 | static extern void __set_PrivateMyProperty__(global::PrivateClass2 target, int value); 255 | 256 | public int PrivateMyProperty 257 | { 258 | get => __get_PrivateMyProperty__(target); 259 | set => __set_PrivateMyProperty__(target, value); 260 | } 261 | 262 | 263 | 264 | public void Tako() => Console.WriteLine("hoge"); 265 | } 266 | 267 | 268 | 269 | 270 | 271 | public class PrivateClass 272 | { 273 | private int _privateField; 274 | private static int _privateStaticField; 275 | 276 | private int PrivateMethod() => 10; 277 | private static int PrivateStaticMethod() => 99; 278 | 279 | 280 | public int PublicField; 281 | public readonly int ReadOnlyPublicField; 282 | } 283 | 284 | 285 | public class PrivateClass2 286 | { 287 | private int _privateField; 288 | 289 | private int PrivateMyProperty { get; set; } 290 | 291 | public void ShowPrivateField() 292 | { 293 | 294 | 295 | Console.WriteLine(_privateField); 296 | } 297 | 298 | void Show2() 299 | { 300 | Console.WriteLine("2"); 301 | } 302 | 303 | 304 | 305 | void ShowWithP(int x, int y) 306 | { 307 | Console.WriteLine(x + y); 308 | } 309 | 310 | int FooBarBaz(ref int x, out int y, in int z) => y = x + z; 311 | } 312 | 313 | [GeneratePrivateProxy(typeof(PrivateClass2))] 314 | partial struct PrivateClass2Proxy; 315 | 316 | public class TakoyakIX 317 | { 318 | public int get_MyProperty() => 10; 319 | // public int MyProperty { get; set; } 320 | } 321 | 322 | 323 | public struct NonRefStructTest 324 | { 325 | int field; 326 | } 327 | 328 | public ref partial struct NonRefStructTestProxy333 329 | { 330 | global::NonRefStructTest target; 331 | 332 | public NonRefStructTestProxy333(ref global::NonRefStructTest target) 333 | { 334 | this.target = target; 335 | } 336 | 337 | //[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "field")] 338 | //static extern ref int __field__(ref global::NonRefStructTest target); 339 | 340 | //public ref int field 341 | //{ 342 | // get 343 | // { 344 | // return ref __field__(ref this.target); 345 | // } 346 | //} 347 | 348 | [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_Prop0")] 349 | static extern int __get_Prop0__(ref global::NonRefStructTest target); 350 | 351 | public int Prop0 352 | { 353 | get => __get_Prop0__(ref this.target); 354 | } 355 | 356 | 357 | } -------------------------------------------------------------------------------- /src/PrivateProxy.Generator/DiagnosticDescriptors.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | 3 | namespace PrivateProxy.Generator; 4 | 5 | public static class DiagnosticDescriptors 6 | { 7 | const string Category = "PrivateProxy"; 8 | 9 | public static readonly DiagnosticDescriptor MustBePartial = new( 10 | id: "PP001", 11 | title: "PrivateProxy type must be partial", 12 | messageFormat: "The PrivateProxy type '{0}' must be partial", 13 | category: Category, 14 | defaultSeverity: DiagnosticSeverity.Error, 15 | isEnabledByDefault: true); 16 | 17 | public static readonly DiagnosticDescriptor NotAllowReadOnly = new( 18 | id: "PP002", 19 | title: "PrivateProxy does not allow readonly struct", 20 | messageFormat: "The PrivateProxy struct '{0}' does not allow readonly struct", 21 | category: Category, 22 | defaultSeverity: DiagnosticSeverity.Error, 23 | isEnabledByDefault: true); 24 | 25 | public static readonly DiagnosticDescriptor ClassNotAllowRefStruct = new( 26 | id: "PP003", 27 | title: "PrivateProxy class does not allow ref struct", 28 | messageFormat: "The PrivateProxy class(reference-type) '{0}' does not allow ref struct", 29 | category: Category, 30 | defaultSeverity: DiagnosticSeverity.Error, 31 | isEnabledByDefault: true); 32 | 33 | public static readonly DiagnosticDescriptor StructNotAllowClass = new( 34 | id: "PP004", 35 | title: "PrivateProxy struct does not allow class", 36 | messageFormat: "The PrivateProxy struct '{0}' does not allow class, only allows ref struct", 37 | category: Category, 38 | defaultSeverity: DiagnosticSeverity.Error, 39 | isEnabledByDefault: true); 40 | 41 | public static readonly DiagnosticDescriptor StructNotAllowStruct = new( 42 | id: "PP005", 43 | title: "PrivateProxy struct does not allow struct", 44 | messageFormat: "The PrivateProxy struct '{0}' does not allow struct, only allows ref struct", 45 | category: Category, 46 | defaultSeverity: DiagnosticSeverity.Error, 47 | isEnabledByDefault: true); 48 | 49 | public static readonly DiagnosticDescriptor RefStructNotSupported = new( 50 | id: "PP006", 51 | title: "PrivateProxy does not support ref struct", 52 | messageFormat: "The PrivateProxy does not support ref struct", 53 | category: Category, 54 | defaultSeverity: DiagnosticSeverity.Error, 55 | isEnabledByDefault: true); 56 | 57 | public static readonly DiagnosticDescriptor GenericsNotSupported = new( 58 | id: "PP007", 59 | title: "PrivateProxy does not support generics type", 60 | messageFormat: "The PrivateProxy does not support generics type", 61 | category: Category, 62 | defaultSeverity: DiagnosticSeverity.Error, 63 | isEnabledByDefault: true); 64 | 65 | public static readonly DiagnosticDescriptor StaticNotSupported = new( 66 | id: "PP008", 67 | title: "PrivateProxy does not support static type", 68 | messageFormat: "The PrivateProxy does not support static type", 69 | category: Category, 70 | defaultSeverity: DiagnosticSeverity.Error, 71 | isEnabledByDefault: true); 72 | } -------------------------------------------------------------------------------- /src/PrivateProxy.Generator/EmitHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using System.Collections.Immutable; 3 | 4 | namespace PrivateProxy.Generator; 5 | 6 | internal static class EmitHelper 7 | { 8 | public static string ToCode(this Accessibility accessibility) 9 | { 10 | switch (accessibility) 11 | { 12 | case Accessibility.NotApplicable: 13 | return ""; 14 | case Accessibility.Private: 15 | return "private"; 16 | case Accessibility.ProtectedAndInternal: 17 | return "private protected"; 18 | case Accessibility.Protected: 19 | return "protected"; 20 | case Accessibility.Internal: 21 | return "internal"; 22 | case Accessibility.ProtectedOrInternal: 23 | return "protected internal"; 24 | case Accessibility.Public: 25 | return "public"; 26 | default: 27 | return ""; 28 | } 29 | } 30 | 31 | public static string ToParameterPrefix(this RefKind kind) 32 | { 33 | switch (kind) 34 | { 35 | case RefKind.Out: return "out "; 36 | case RefKind.Ref: return "ref "; 37 | case RefKind.In: return "in "; 38 | // case RefKind.RefReadOnlyParameter: return "ref readonly "; 39 | case (RefKind)4: return "ref readonly "; 40 | case RefKind.None: return ""; 41 | default: return ""; 42 | } 43 | } 44 | 45 | public static string ToUseParameterPrefix(this RefKind kind) 46 | { 47 | switch (kind) 48 | { 49 | case RefKind.Out: return "out "; 50 | case RefKind.Ref: return "ref "; 51 | case RefKind.In: return "in "; 52 | case (RefKind)4: return "in "; // ref readonly 53 | case RefKind.None: return ""; 54 | default: return ""; 55 | } 56 | } 57 | 58 | public static string ToFullyQualifiedFormatString(this ISymbol symbol) 59 | { 60 | return symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); 61 | } 62 | 63 | public static string ForEachLine(string indent, IEnumerable values, Func lineSelector) 64 | { 65 | return string.Join(Environment.NewLine, values.Select(x => indent + lineSelector(x))); 66 | } 67 | 68 | public static string ForLine(string indent, int begin, int end, Func lineSelector) 69 | { 70 | return string.Join(Environment.NewLine, Enumerable.Range(begin, end - begin).Select(x => indent + lineSelector(x))); 71 | } 72 | 73 | public static string If(bool condition, string code) 74 | { 75 | return condition ? code : ""; 76 | } 77 | 78 | public static string If(bool condition, string ifCode, string elseCode) 79 | { 80 | return condition ? ifCode : elseCode; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/PrivateProxy.Generator/MetaMember.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using System.Collections.Immutable; 3 | 4 | namespace PrivateProxy.Generator; 5 | 6 | public enum MemberKind 7 | { 8 | Field, Property, Method, Constructor 9 | } 10 | 11 | public class MetaMember 12 | { 13 | public ITypeSymbol MemberType { get; } 14 | public string MemberTypeFullName { get; } 15 | public string Name { get; } 16 | public bool IsPublic { get; } 17 | public bool IsStatic { get; } 18 | public MemberKind MemberKind { get; } 19 | 20 | public bool HasGetMethod { get; } // only for property 21 | public bool HasSetMethod { get; } // only for property 22 | 23 | public bool IsRequireReadOnly { get; } 24 | public bool IsRefReturn { get; } 25 | 26 | public ImmutableArray MethodParameters { get; } // only for method 27 | 28 | public MetaMember(ISymbol symbol) 29 | { 30 | this.Name = symbol.Name; 31 | this.IsStatic = symbol.IsStatic; 32 | 33 | if (symbol is IFieldSymbol f) 34 | { 35 | // PrivateProxy only handles assignable(use readonly keyword) 36 | 37 | // int field; // RefKind=None 38 | // readonly int readOnlyField; // RefKind=None, IsReadOnly=true | can't assign 39 | // ref int refField; // RefKind=Ref 40 | // ref readonly int refReadOnly; // RefKind=In | can't assign 41 | // readonly ref int readonlyRef; // RefKind=Ref, IsReadOnly=true 42 | // readonly ref readonly int readOnlyRefReadOnly; // RefKind=In, IsReadOnly=true | can't assign 43 | 44 | this.MemberType = f.Type; 45 | this.MemberKind = MemberKind.Field; 46 | this.IsRequireReadOnly = (f.RefKind, f.IsReadOnly) switch 47 | { 48 | (RefKind.None, true) => true, 49 | (RefKind.In, _) => true, 50 | _ => false 51 | }; 52 | this.IsRefReturn = f.RefKind != RefKind.None; 53 | } 54 | else if (symbol is IPropertySymbol p) 55 | { 56 | // int Prop0 => 1; 57 | // readonly int Prop1 => 1; 58 | // ref int Prop2 => ref refField; // ReturnsByRef:true 59 | // ref readonly int Prop3 => ref refField; // ReturnsByRefReadOnly:true | can't assign 60 | // readonly ref int Prop4 => ref refField; // ReturnsByRef:true 61 | // readonly ref readonly int Prop5 => ref refField; // ReturnsByRefReadOnly:true | can't assign 62 | 63 | this.MemberType = p.Type; 64 | this.MemberKind = MemberKind.Property; 65 | this.HasGetMethod = p.GetMethod != null; 66 | this.HasSetMethod = p.SetMethod != null; 67 | 68 | (this.IsRefReturn, this.IsRequireReadOnly) = (p.ReturnsByRef, p.ReturnsByRefReadonly) switch 69 | { 70 | (true, true) => (true, true), // but maybe don't come here. 71 | (true, false) => (true, false), 72 | (false, true) => (true, true), 73 | (false, false) => (false, false), 74 | }; 75 | } 76 | else if (symbol is IMethodSymbol m) 77 | { 78 | // same as property 79 | // int Method0() => 1; 80 | // readonly int Method1() => 1; 81 | // ref int Method2() => ref refField; 82 | // ref readonly int Method3() => ref refField; 83 | // readonly ref int Method4() => ref refField; 84 | // readonly ref readonly int Method5() => ref refField; 85 | 86 | this.MemberType = m.ReturnType; 87 | this.MethodParameters = m.Parameters; 88 | this.MemberKind = m.Name == ".ctor" ? MemberKind.Constructor : MemberKind.Method; 89 | 90 | (this.IsRefReturn, this.IsRequireReadOnly) = (m.ReturnsByRef, m.ReturnsByRefReadonly) switch 91 | { 92 | (true, true) => (true, true), // but maybe don't come here. 93 | (true, false) => (true, false), 94 | (false, true) => (true, true), 95 | (false, false) => (false, false), 96 | }; 97 | } 98 | else 99 | { 100 | throw new InvalidOperationException("Symbol type is invalid. " + symbol.GetType().FullName); 101 | } 102 | this.MemberTypeFullName = this.MemberType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/PrivateProxy.Generator/PrivateProxy.Generator.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 12 6 | enable 7 | cs 8 | enable 9 | 10 | PrivateProxy 11 | 12 | $(TargetsForTfmSpecificContentInPackage);PackBuildOutputs 13 | false 14 | true 15 | false 16 | true 17 | true 18 | codegenerator 19 | PrivateProxy SourceGenerator. 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | all 29 | runtime; build; native; contentfiles; analyzers; buildtransitive 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/PrivateProxy.Generator/PrivateProxyGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | using Microsoft.CodeAnalysis; 3 | using Microsoft.CodeAnalysis.CSharp; 4 | using Microsoft.CodeAnalysis.CSharp.Syntax; 5 | using System.Text; 6 | using static PrivateProxy.Generator.EmitHelper; 7 | 8 | namespace PrivateProxy.Generator; 9 | 10 | [Generator(LanguageNames.CSharp)] 11 | public partial class PrivateProxyGenerator : IIncrementalGenerator 12 | { 13 | public void Initialize(IncrementalGeneratorInitializationContext context) 14 | { 15 | context.RegisterPostInitializationOutput(EmitAttributes); 16 | 17 | var source = context.SyntaxProvider.ForAttributeWithMetadataName( 18 | "PrivateProxy.GeneratePrivateProxyAttribute", 19 | static (node, token) => node is StructDeclarationSyntax or ClassDeclarationSyntax, 20 | static (context, token) => context); 21 | 22 | // NOTE: currently does not provide private metadata from external dll. 23 | // context.CompilationProvider.Select((x, token) => x.WithOptions(x.Options.WithMetadataImportOptions(MetadataImportOptions.All)); 24 | 25 | context.RegisterSourceOutput(source, Emit); 26 | } 27 | 28 | static void EmitAttributes(IncrementalGeneratorPostInitializationContext context) 29 | { 30 | context.AddSource("GeneratePrivateProxyAttribute.cs", """ 31 | using System; 32 | using System.Collections.Generic; 33 | 34 | namespace PrivateProxy 35 | { 36 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] 37 | internal sealed class GeneratePrivateProxyAttribute : Attribute 38 | { 39 | public Type Target { get; } 40 | public PrivateProxyGenerateKinds GenerateKinds { get; } 41 | 42 | public GeneratePrivateProxyAttribute(Type target) 43 | { 44 | this.Target = target; 45 | this.GenerateKinds = PrivateProxyGenerateKinds.All; 46 | } 47 | 48 | public GeneratePrivateProxyAttribute(Type target, PrivateProxyGenerateKinds generateKinds) 49 | { 50 | this.Target = target; 51 | this.GenerateKinds = generateKinds; 52 | } 53 | } 54 | 55 | [Flags] 56 | internal enum PrivateProxyGenerateKinds 57 | { 58 | All = 0, // Field | Method | Property | Instance | Static | Constructor 59 | Field = 1, 60 | Method = 2, 61 | Property = 4, 62 | Instance = 8, 63 | Static = 16, 64 | Constructor = 32, 65 | } 66 | } 67 | """); 68 | } 69 | 70 | [Flags] 71 | public enum PrivateProxyGenerateKinds 72 | { 73 | All = 0, // Field | Method | Property | Instance | Static | Constructor 74 | Field = 1, 75 | Method = 2, 76 | Property = 4, 77 | Instance = 8, 78 | Static = 16, 79 | Constructor = 32, 80 | } 81 | 82 | static void Emit(SourceProductionContext context, GeneratorAttributeSyntaxContext source) 83 | { 84 | var attr = source.Attributes[0]; // allowMultiple:false 85 | GetAttributeParameters(attr, out var targetType, out var kind); 86 | 87 | var members = GetMembers(targetType, kind); 88 | 89 | if (!Verify(context, (TypeDeclarationSyntax)source.TargetNode, (INamedTypeSymbol)source.TargetSymbol, targetType)) 90 | { 91 | return; 92 | } 93 | 94 | if (members.Length == 0) 95 | { 96 | return; 97 | } 98 | 99 | //// Generate Code 100 | var code = EmitCode((ITypeSymbol)source.TargetSymbol, targetType, members); 101 | AddSource(context, source.TargetSymbol, code); 102 | } 103 | 104 | static void GetAttributeParameters(AttributeData attr, out INamedTypeSymbol targetType, out PrivateProxyGenerateKinds kind) 105 | { 106 | // Extract attribute parameter 107 | // public GeneratePrivateProxyAttribute(Type target) 108 | // public GeneratePrivateProxyAttribute(Type target, PrivateProxyGenerateKinds generateKinds) 109 | 110 | targetType = (INamedTypeSymbol)attr.ConstructorArguments[0].Value!; 111 | 112 | if (attr.ConstructorArguments.Length == 1) 113 | { 114 | kind = PrivateProxyGenerateKinds.All; 115 | } 116 | else 117 | { 118 | kind = (PrivateProxyGenerateKinds)attr.ConstructorArguments[1].Value!; 119 | } 120 | } 121 | 122 | static MetaMember[] GetMembers(INamedTypeSymbol targetType, PrivateProxyGenerateKinds kind) 123 | { 124 | var members = targetType.GetMembers(); 125 | 126 | var list = new List(members.Length); 127 | 128 | kind = (kind == PrivateProxyGenerateKinds.All) ? PrivateProxyGenerateKinds.Field | PrivateProxyGenerateKinds.Method | PrivateProxyGenerateKinds.Property | PrivateProxyGenerateKinds.Instance | PrivateProxyGenerateKinds.Static | PrivateProxyGenerateKinds.Constructor : kind; 129 | 130 | var generateField = kind.HasFlag(PrivateProxyGenerateKinds.Field); 131 | var generateProperty = kind.HasFlag(PrivateProxyGenerateKinds.Property); 132 | var generateMethod = kind.HasFlag(PrivateProxyGenerateKinds.Method); 133 | var generateInstance = kind.HasFlag(PrivateProxyGenerateKinds.Instance); 134 | var generateStatic = kind.HasFlag(PrivateProxyGenerateKinds.Static); 135 | var generateConstructor = kind.HasFlag(PrivateProxyGenerateKinds.Constructor); 136 | 137 | // If only set Static or Instance, generate all member kind 138 | if (!generateField && !generateProperty && !generateMethod && !generateConstructor) 139 | { 140 | generateField = generateProperty = generateMethod = true; 141 | } 142 | // If only set member kind, generate both static and instance 143 | if (!generateStatic && !generateInstance) 144 | { 145 | generateStatic = generateInstance = true; 146 | } 147 | 148 | foreach (var item in members) 149 | { 150 | if (!item.CanBeReferencedByName && item.Name != ".ctor") continue; 151 | 152 | if (item.IsStatic && !generateStatic) continue; 153 | if (!item.IsStatic && !generateInstance) continue; 154 | 155 | // add field/property/method 156 | if (generateField && item is IFieldSymbol f) 157 | { 158 | // public member don't generate 159 | if (f.DeclaredAccessibility == Accessibility.Public) continue; 160 | 161 | // return type can not access, don't generate 162 | if (!CanExpose(f.Type)) continue; 163 | 164 | list.Add(new(item)); 165 | } 166 | else if (generateProperty && item is IPropertySymbol p) 167 | { 168 | if (p.DeclaredAccessibility == Accessibility.Public) 169 | { 170 | var getPublic = true; 171 | var setPublic = true; 172 | if (p.GetMethod != null) 173 | { 174 | getPublic = p.GetMethod.DeclaredAccessibility == Accessibility.Public; 175 | } 176 | if (p.SetMethod != null) 177 | { 178 | setPublic = p.SetMethod.DeclaredAccessibility == Accessibility.Public; 179 | } 180 | 181 | if (getPublic && setPublic) continue; 182 | } 183 | 184 | if (!CanExpose(p.Type)) continue; 185 | 186 | list.Add(new(item)); 187 | } 188 | else if ((generateMethod || generateConstructor) && item is IMethodSymbol m) 189 | { 190 | if (m.DeclaredAccessibility == Accessibility.Public) continue; 191 | 192 | // both return type and parameter type must be accessible 193 | if (!CanExpose(m.ReturnType)) goto Next; 194 | foreach (var parameter in m.Parameters) 195 | { 196 | if (!CanExpose(parameter.Type)) goto Next; 197 | } 198 | 199 | if (m.Name == ".ctor") 200 | { 201 | if (!generateConstructor) continue; 202 | } 203 | else 204 | { 205 | if (!generateMethod) continue; 206 | } 207 | 208 | list.Add(new(item)); 209 | Next: 210 | ; 211 | } 212 | } 213 | return list.ToArray(); 214 | } 215 | 216 | static bool Verify(SourceProductionContext context, TypeDeclarationSyntax typeSyntax, INamedTypeSymbol proxyType, INamedTypeSymbol targetType) 217 | { 218 | // Type Rule 219 | // ProxyClass: class -> allows class or struct 220 | // : struct -> allows ref struct 221 | 222 | var hasError = false; 223 | 224 | // require partial 225 | if (!typeSyntax.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword))) 226 | { 227 | context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.MustBePartial, typeSyntax.Identifier.GetLocation(), proxyType.Name)); 228 | hasError = true; 229 | } 230 | 231 | // not allow readonly struct 232 | if (proxyType.IsValueType && proxyType.IsReadOnly) 233 | { 234 | context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.NotAllowReadOnly, typeSyntax.Identifier.GetLocation(), proxyType.Name)); 235 | hasError = true; 236 | } 237 | 238 | // class, not allow ref struct 239 | if (targetType.IsReferenceType && proxyType.IsRefLikeType) 240 | { 241 | context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.ClassNotAllowRefStruct, typeSyntax.Identifier.GetLocation(), proxyType.Name)); 242 | hasError = true; 243 | } 244 | 245 | // struct, not allow class or struct(only allows ref struct) 246 | if (targetType.IsValueType) 247 | { 248 | if (proxyType.IsReferenceType) 249 | { 250 | context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.StructNotAllowClass, typeSyntax.Identifier.GetLocation(), proxyType.Name)); 251 | hasError = true; 252 | } 253 | else if (proxyType.IsValueType && !proxyType.IsRefLikeType) 254 | { 255 | context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.StructNotAllowStruct, typeSyntax.Identifier.GetLocation(), proxyType.Name)); 256 | hasError = true; 257 | } 258 | } 259 | 260 | // target type not allow `ref struct` 261 | if (targetType.IsValueType && targetType.IsRefLikeType) 262 | { 263 | context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.RefStructNotSupported, typeSyntax.Identifier.GetLocation())); 264 | hasError = true; 265 | } 266 | 267 | // generics is not supported 268 | if (targetType.IsGenericType) 269 | { 270 | context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.GenericsNotSupported, typeSyntax.Identifier.GetLocation())); 271 | hasError = true; 272 | } 273 | 274 | // static class is not supported 275 | if (targetType.IsStatic) 276 | { 277 | context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.StaticNotSupported, typeSyntax.Identifier.GetLocation())); 278 | hasError = true; 279 | } 280 | 281 | return !hasError; 282 | } 283 | 284 | static string EmitCode(ITypeSymbol proxyType, INamedTypeSymbol targetType, MetaMember[] members) 285 | { 286 | var targetTypeFullName = targetType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); 287 | var code = new StringBuilder(); 288 | 289 | var accessibility = proxyType.DeclaredAccessibility.ToCode(); 290 | var structOrClass = proxyType.IsReferenceType ? "class" : "struct"; 291 | var refStruct = proxyType.IsRefLikeType ? "ref " : ""; 292 | 293 | var hasStatic = members.Any(x => x.IsStatic); 294 | 295 | code.AppendLine($$""" 296 | {{refStruct}}partial {{structOrClass}} {{proxyType.Name}} 297 | { 298 | {{If(hasStatic, $$""" 299 | static {{targetTypeFullName}} ____static_instance = default!; 300 | """)}} 301 | {{refStruct}}{{targetTypeFullName}} target; 302 | 303 | public {{proxyType.Name}}({{refStruct}}{{targetTypeFullName}} target) 304 | { 305 | this.target = {{refStruct}}target; 306 | } 307 | 308 | """); 309 | 310 | foreach (var item in members) 311 | { 312 | var readonlyCode = item.IsRequireReadOnly ? "readonly " : ""; 313 | var refReturn = item.IsRefReturn ? "ref " : ""; 314 | 315 | var staticCode = item.IsStatic ? "Static" : ""; 316 | var staticCode2 = item.IsStatic ? "static " : ""; 317 | var targetInstance = item.IsStatic ? $"{refStruct}____static_instance" : $"{refStruct}this.target"; 318 | switch (item.MemberKind) 319 | { 320 | case MemberKind.Field: 321 | code.AppendLine($$""" 322 | [UnsafeAccessor(UnsafeAccessorKind.{{staticCode}}Field, Name = "{{item.Name}}")] 323 | static extern ref {{readonlyCode}}{{item.MemberTypeFullName}} __{{item.Name}}__({{refStruct}}{{targetTypeFullName}} target); 324 | 325 | public {{staticCode2}}ref {{readonlyCode}}{{item.MemberTypeFullName}} {{item.Name}} => ref __{{item.Name}}__({{targetInstance}}); 326 | """); 327 | break; 328 | case MemberKind.Property: 329 | 330 | if (item.HasGetMethod) 331 | { 332 | code.AppendLine($$""" 333 | [UnsafeAccessor(UnsafeAccessorKind.{{staticCode}}Method, Name = "get_{{item.Name}}")] 334 | static extern {{refReturn}}{{item.MemberTypeFullName}} __get_{{item.Name}}__({{refStruct}}{{targetTypeFullName}} target); 335 | 336 | """); 337 | } 338 | 339 | if (item.HasSetMethod) 340 | { 341 | code.AppendLine($$""" 342 | [UnsafeAccessor(UnsafeAccessorKind.{{staticCode}}Method, Name = "set_{{item.Name}}")] 343 | static extern void __set_{{item.Name}}__({{refStruct}}{{targetTypeFullName}} target, {{item.MemberTypeFullName}} value); 344 | 345 | """); 346 | } 347 | 348 | code.AppendLine($$""" 349 | public {{staticCode2}}{{refReturn}}{{readonlyCode}}{{item.MemberTypeFullName}} {{item.Name}} 350 | { 351 | """); 352 | if (item.HasGetMethod) 353 | { 354 | code.AppendLine($" get => {refReturn}__get_{item.Name}__({targetInstance});"); 355 | } 356 | if (item.HasSetMethod) 357 | { 358 | code.AppendLine($" set => __set_{item.Name}__({targetInstance}, value);"); 359 | } 360 | 361 | code.AppendLine(" }"); // close property 362 | break; 363 | case MemberKind.Constructor: 364 | case MemberKind.Method: 365 | var parameters = string.Join(", ", item.MethodParameters.Select(x => $"{x.RefKind.ToParameterPrefix()}{x.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} {x.Name}")); 366 | var parametersWithComma = (parameters != "") ? ", " + parameters : ""; 367 | var useParameters = string.Join(", ", item.MethodParameters.Select(x => $"{x.RefKind.ToUseParameterPrefix()}{x.Name}")); 368 | if (useParameters != "") useParameters = ", " + useParameters; 369 | 370 | if (item.MemberKind == MemberKind.Constructor) 371 | { 372 | code.AppendLine($$""" 373 | [UnsafeAccessor(UnsafeAccessorKind.Constructor)] 374 | public static extern {{targetTypeFullName}} Create{{targetType.Name}}FromConstructor({{parameters}}); 375 | 376 | """); 377 | } 378 | else 379 | { 380 | code.AppendLine($$""" 381 | [UnsafeAccessor(UnsafeAccessorKind.{{staticCode}}Method, Name = "{{item.Name}}")] 382 | static extern {{refReturn}}{{item.MemberTypeFullName}} __{{item.Name}}__({{refStruct}}{{targetTypeFullName}} target{{parametersWithComma}}); 383 | 384 | public {{staticCode2}}{{refReturn}}{{readonlyCode}}{{item.MemberTypeFullName}} {{item.Name}}({{parameters}}) => {{refReturn}}__{{item.Name}}__({{targetInstance}}{{useParameters}}); 385 | 386 | """); 387 | } 388 | break; 389 | default: 390 | break; 391 | } 392 | } 393 | 394 | code.AppendLine("}"); // close Proxy partial 395 | 396 | code.AppendLine($$""" 397 | 398 | {{accessibility}} static class {{targetType.Name}}PrivateProxyExtensions 399 | { 400 | public static {{proxyType.ToFullyQualifiedFormatString()}} AsPrivateProxy(this {{refStruct}}{{targetTypeFullName}} target) 401 | { 402 | return new {{proxyType.ToFullyQualifiedFormatString()}}({{refStruct}}target); 403 | } 404 | } 405 | """); 406 | 407 | return code.ToString(); 408 | } 409 | 410 | static void AddSource(SourceProductionContext context, ISymbol targetSymbol, string code, string fileExtension = ".g.cs") 411 | { 412 | var fullType = targetSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) 413 | .Replace("global::", "") 414 | .Replace("<", "_") 415 | .Replace(">", "_"); 416 | 417 | var sb = new StringBuilder(); 418 | 419 | sb.AppendLine(""" 420 | // 421 | #nullable enable 422 | #pragma warning disable CS0108 423 | #pragma warning disable CS0162 424 | #pragma warning disable CS0164 425 | #pragma warning disable CS0219 426 | #pragma warning disable CS8600 427 | #pragma warning disable CS8601 428 | #pragma warning disable CS8602 429 | #pragma warning disable CS8604 430 | #pragma warning disable CS8619 431 | #pragma warning disable CS8620 432 | #pragma warning disable CS8631 433 | #pragma warning disable CS8765 434 | #pragma warning disable CS9074 435 | #pragma warning disable CA1050 436 | 437 | using System; 438 | using System.Runtime.CompilerServices; 439 | using System.Runtime.InteropServices; 440 | """); 441 | 442 | var ns = targetSymbol.ContainingNamespace; 443 | if (!ns.IsGlobalNamespace) 444 | { 445 | sb.AppendLine($"namespace {ns} {{"); 446 | } 447 | sb.AppendLine(); 448 | 449 | sb.AppendLine(code); 450 | 451 | if (!ns.IsGlobalNamespace) 452 | { 453 | sb.AppendLine($"}}"); 454 | } 455 | 456 | var sourceCode = sb.ToString(); 457 | context.AddSource($"{fullType}{fileExtension}", sourceCode); 458 | } 459 | 460 | static bool CanExpose(ITypeSymbol typeSymbol) 461 | { 462 | if (typeSymbol is IPointerTypeSymbol) return false; 463 | 464 | if (typeSymbol is IArrayTypeSymbol arrayType) 465 | { 466 | var elementType = arrayType.ElementType; 467 | if (!CanExpose(elementType)) return false; 468 | 469 | while (elementType is IArrayTypeSymbol nestedArrayType) 470 | { 471 | if (!CanExpose(elementType)) return false; 472 | elementType = nestedArrayType.ElementType; 473 | } 474 | } 475 | 476 | if (typeSymbol is INamedTypeSymbol { IsGenericType: true } genericType) 477 | { 478 | foreach (var t in genericType.TypeArguments) 479 | { 480 | if (!CanExpose(t)) return false; 481 | } 482 | } 483 | return typeSymbol.DeclaredAccessibility == Accessibility.Public || 484 | (typeSymbol.DeclaredAccessibility == Accessibility.NotApplicable && typeSymbol is IArrayTypeSymbol); 485 | } 486 | } 487 | -------------------------------------------------------------------------------- /src/PrivateProxy.Generator/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Profile 1": { 4 | "commandName": "DebugRoslynComponent", 5 | "targetProject": "../../sandbox/ConsoleAppNet8/ConsoleAppNet8.csproj" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /tests/PrivateProxy.Tests/ChangeKindTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | using System.Threading.Tasks; 8 | 9 | namespace PrivateProxy.Tests 10 | { 11 | public class KindTarget 12 | { 13 | private int field; 14 | private int Property { get; set; } 15 | int Method() => 3; 16 | 17 | static private int staticField; 18 | static private int StaticProperty { get; set; } 19 | static private int StaticMethod() => 3; 20 | } 21 | 22 | [GeneratePrivateProxy(typeof(KindTarget), PrivateProxyGenerateKinds.Property | PrivateProxyGenerateKinds.Static)] 23 | public partial struct KindTargetProxy; 24 | 25 | 26 | public class ChangeKindTest 27 | { 28 | string GenerateCode(PrivateProxyGenerateKinds generateKinds) 29 | { 30 | var kind = string.Join(" | ", generateKinds.ToString().Split(',').Select(x => "PrivateProxyGenerateKinds." + x.Trim())); 31 | 32 | var codes = CSharpGeneratorRunner.RunGeneratorCode($$""" 33 | using PrivateProxy; 34 | 35 | public class KindTarget 36 | { 37 | private int field; 38 | private int Property { get; set; } 39 | int Method() => 3; 40 | 41 | static private int staticField; 42 | static private int StaticProperty { get; set; } 43 | static private int StaticMethod() => 3; 44 | 45 | private KindTarget(int x) { } 46 | } 47 | 48 | [GeneratePrivateProxy(typeof(KindTarget), {{kind}})] 49 | public partial struct KindTargetProxy { } 50 | """); 51 | 52 | var code = codes.First(x => x.FilePath.Contains("g.cs")).Code; 53 | return code; 54 | } 55 | 56 | [Fact] 57 | public void All() 58 | { 59 | var all = GenerateCode(PrivateProxyGenerateKinds.All); 60 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(9); 61 | } 62 | 63 | [Fact] 64 | public void StaticOnly() 65 | { 66 | var all = GenerateCode(PrivateProxyGenerateKinds.Static); 67 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(4); 68 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(4); 69 | } 70 | 71 | [Fact] 72 | public void InstanceOnly() 73 | { 74 | var all = GenerateCode(PrivateProxyGenerateKinds.Instance); 75 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(4); 76 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(4); 77 | } 78 | 79 | [Fact] 80 | public void MethodOnly() 81 | { 82 | var all = GenerateCode(PrivateProxyGenerateKinds.Method); 83 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(2); 84 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(1); 85 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(1); 86 | 87 | all = GenerateCode(PrivateProxyGenerateKinds.Static | PrivateProxyGenerateKinds.Method); 88 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(1); 89 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(1); 90 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(0); 91 | 92 | all = GenerateCode(PrivateProxyGenerateKinds.Instance | PrivateProxyGenerateKinds.Method); 93 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(1); 94 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(0); 95 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(1); 96 | } 97 | 98 | [Fact] 99 | public void PropertyOnly() 100 | { 101 | var all = GenerateCode(PrivateProxyGenerateKinds.Property); 102 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(4); 103 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(2); 104 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(2); 105 | 106 | all = GenerateCode(PrivateProxyGenerateKinds.Static | PrivateProxyGenerateKinds.Property); 107 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(2); 108 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(2); 109 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(0); 110 | 111 | all = GenerateCode(PrivateProxyGenerateKinds.Instance | PrivateProxyGenerateKinds.Property); 112 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(2); 113 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(0); 114 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(2); 115 | } 116 | 117 | [Fact] 118 | public void FieldOnly() 119 | { 120 | var all = GenerateCode(PrivateProxyGenerateKinds.Field); 121 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(2); 122 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(1); 123 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(1); 124 | 125 | all = GenerateCode(PrivateProxyGenerateKinds.Static | PrivateProxyGenerateKinds.Field); 126 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(1); 127 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(1); 128 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(0); 129 | 130 | all = GenerateCode(PrivateProxyGenerateKinds.Instance | PrivateProxyGenerateKinds.Field); 131 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(1); 132 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(0); 133 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(1); 134 | } 135 | 136 | [Fact] 137 | public void ConstructorOnly() 138 | { 139 | var all = GenerateCode(PrivateProxyGenerateKinds.Constructor); 140 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(1); 141 | } 142 | 143 | [Fact] 144 | public void Mix() 145 | { 146 | var all = GenerateCode(PrivateProxyGenerateKinds.Field | PrivateProxyGenerateKinds.Method); 147 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(4); 148 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(2); 149 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(2); 150 | 151 | all = GenerateCode(PrivateProxyGenerateKinds.Static| PrivateProxyGenerateKinds.Instance | PrivateProxyGenerateKinds.Field | PrivateProxyGenerateKinds.Method); 152 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(4); 153 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(2); 154 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(2); 155 | 156 | all = GenerateCode(PrivateProxyGenerateKinds.Static | PrivateProxyGenerateKinds.Field | PrivateProxyGenerateKinds.Method); 157 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(2); 158 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(2); 159 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(0); 160 | 161 | all = GenerateCode(PrivateProxyGenerateKinds.Instance | PrivateProxyGenerateKinds.Field | PrivateProxyGenerateKinds.Method); 162 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(2); 163 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(0); 164 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(2); 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /tests/PrivateProxy.Tests/ClassGenerateTest.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS0649 2 | #pragma warning disable CS0169 3 | #pragma warning disable CS0414 4 | 5 | using Microsoft.CodeAnalysis; 6 | using System.Runtime.CompilerServices; 7 | 8 | namespace PrivateProxy.Tests; 9 | 10 | public class PrivateClassTarget 11 | { 12 | public PrivateClassTarget(int readOnlyField) 13 | { 14 | this.readOnlyField = readOnlyField; 15 | this.GetOnlyProperty = readOnlyField; 16 | this._refReadOnlyGetOnlyPropertyField = readOnlyField; 17 | } 18 | 19 | // public(no generate) 20 | public int publicField; 21 | public int PublicProperty { get; set; } 22 | public void PublicMethod() { } 23 | 24 | // field 25 | private int field; 26 | private readonly int readOnlyField; 27 | // public required int requiredField; 28 | 29 | // property 30 | private int Property { get; set; } 31 | private int GetOnlyProperty { get; } 32 | public int GetOnlyPrivateProperty { private get; set; } 33 | public int SetOnlyPrivateProperty { get; private set; } 34 | public required int RequiredProperty { get; set; } 35 | public int InitProperty { get; init; } 36 | public required int RequiredInitProperty { get; init; } 37 | 38 | int _setOnlyPropertyField; 39 | private int SetOnlyProperty { set => _setOnlyPropertyField = value; } 40 | 41 | int _refGetOnlyPropertyField; 42 | private ref int RefGetOnlyProperty => ref _refGetOnlyPropertyField; 43 | 44 | int _refReadOnlyGetOnlyPropertyField; 45 | private ref readonly int RefReadOnlyGetOnlyProperty => ref _refReadOnlyGetOnlyPropertyField; 46 | 47 | // method 48 | public int voidMethodCalledCount; 49 | 50 | private void VoidMethod() 51 | { 52 | voidMethodCalledCount++; 53 | } 54 | 55 | private int ReturnMethod() => 10; 56 | 57 | private int ParameterMethod1(int x) => x; 58 | 59 | private int ParameterMethod2(int x, int y) => x + y; 60 | 61 | private void VoidParameter(int x, int y) 62 | { 63 | voidMethodCalledCount = x + y; 64 | } 65 | 66 | private void RefOutInMethod(in int x, ref int y, out int z, ref readonly int xyz) 67 | { 68 | y = 40; 69 | z = 9999; 70 | voidMethodCalledCount = x + xyz; 71 | } 72 | 73 | int _refReturnMethodField; 74 | private ref int RefReturnMethod() => ref _refReturnMethodField; 75 | 76 | int _refReadOnlyReturnMethodField; 77 | private ref readonly int RefReadOnlyReturnMethod() => ref _refReadOnlyReturnMethodField; 78 | 79 | int _refReturnMethodParameterField; 80 | private ref int RefReturnMethodParameter(ref int x) 81 | { 82 | return ref x; 83 | } 84 | 85 | // checker 86 | public (int field, int readOnlyField) GetFields() => (field, readOnlyField); 87 | public (int property, int getOnlyProperty, int getOnlyPrivate, int setOnlyPrivate, int setOnlyProperty) GetProperties() => (Property, GetOnlyProperty, GetOnlyPrivateProperty, SetOnlyPrivateProperty, _setOnlyPropertyField); 88 | 89 | public (int refGetOnlyPropertyField, int _refReadOnlyGetOnlyPropertyField) GetRefProperties() => (_refGetOnlyPropertyField, _refReadOnlyGetOnlyPropertyField); 90 | 91 | 92 | // static 93 | 94 | static int staticField; 95 | static readonly int staticReadOnlyField = 4444; 96 | 97 | 98 | static int StaticProperty { get; set; } 99 | static int StaticGetOnlyProperty { get; } = 5000; 100 | public static int StaticGetOnlyPrivateProperty { private get; set; } 101 | public static int StaticSetOnlyPrivateProperty { get; private set; } 102 | 103 | static int _StaticsetOnlyPropertyField; 104 | private static int StaticSetOnlyProperty { set => _StaticsetOnlyPropertyField = value; } 105 | 106 | static int _StaticrefGetOnlyPropertyField; 107 | private static ref int StaticRefGetOnlyProperty => ref _StaticrefGetOnlyPropertyField; 108 | 109 | static int _StaticrefReadOnlyGetOnlyPropertyField; 110 | private static ref readonly int StaticRefReadOnlyGetOnlyProperty => ref _StaticrefReadOnlyGetOnlyPropertyField; 111 | 112 | 113 | public static int staticvoidMethodCalledCount; 114 | 115 | private static void StaticVoidMethod() 116 | { 117 | staticvoidMethodCalledCount++; 118 | } 119 | 120 | static int StaticReturnMethod() => 10; 121 | 122 | static int StaticParameterMethod1(int x) => x; 123 | 124 | static int StaticParameterMethod2(int x, int y) => x + y; 125 | 126 | static void StaticVoidParameter(int x, int y) 127 | { 128 | staticvoidMethodCalledCount = x + y; 129 | } 130 | 131 | static void StaticRefOutInMethod(in int x, ref int y, out int z, ref readonly int xyz) 132 | { 133 | y = 40; 134 | z = 9999; 135 | staticvoidMethodCalledCount = x + xyz; 136 | } 137 | 138 | static int _StaticrefReturnMethodField; 139 | static ref int StaticRefReturnMethod() => ref _StaticrefReturnMethodField; 140 | 141 | static int _StaticrefReadOnlyReturnMethodField; 142 | static ref readonly int StaticRefReadOnlyReturnMethod() => ref _StaticrefReadOnlyReturnMethodField; 143 | 144 | static ref int StaticRefReturnMethodParameter(ref int x) 145 | { 146 | return ref x; 147 | } 148 | } 149 | 150 | 151 | #if true 152 | [GeneratePrivateProxy(typeof(PrivateClassTarget))] 153 | public partial struct PrivateClassTargetStructProxy; 154 | 155 | #else 156 | [GeneratePrivateProxy(typeof(PrivateClassTarget))] 157 | public partial class PrivateClassTargetStructProxy; // ClassProxy but use same name... 158 | #endif 159 | 160 | public class ClassGenerateTest 161 | { 162 | [Fact] 163 | public void Field() 164 | { 165 | var target = new PrivateClassTarget(5000) { RequiredInitProperty = 0, RequiredProperty = 0 }; 166 | var proxy = target.AsPrivateProxy(); 167 | 168 | proxy.field = 1000; 169 | 170 | var (field, readOnlyField) = target.GetFields(); 171 | 172 | field.Should().Be(1000).And.Be(proxy.field); 173 | readOnlyField.Should().Be(5000).And.Be(proxy.readOnlyField); 174 | } 175 | 176 | [Fact] 177 | public void Property() 178 | { 179 | var target = new PrivateClassTarget(5000) { RequiredInitProperty = 0, RequiredProperty = 0 }; 180 | var proxy = target.AsPrivateProxy(); 181 | 182 | proxy.Property = 9999; 183 | proxy.GetOnlyPrivateProperty = 8888; 184 | proxy.SetOnlyPrivateProperty = 7777; 185 | 186 | proxy.SetOnlyProperty = 6666; 187 | 188 | ref var refGet = ref proxy.RefGetOnlyProperty; 189 | ref readonly var refRead = ref proxy.RefReadOnlyGetOnlyProperty; 190 | 191 | refGet = 5555; 192 | // refRead = 100; can't write 193 | 194 | var (prop, getOnly, getOnlyPrivate, setOnlyPrivate, setOnly) = target.GetProperties(); 195 | 196 | prop.Should().Be(9999).And.Be(proxy.Property); 197 | getOnlyPrivate.Should().Be(8888).And.Be(proxy.GetOnlyPrivateProperty); 198 | setOnlyPrivate.Should().Be(7777).And.Be(proxy.SetOnlyPrivateProperty); 199 | getOnly.Should().Be(5000).And.Be(proxy.GetOnlyProperty); 200 | 201 | setOnly.Should().Be(6666); 202 | 203 | var (refReadOnly, refReadOnly2) = target.GetRefProperties(); 204 | 205 | proxy.RefGetOnlyProperty.Should().Be(5555); 206 | 207 | 208 | refGet.Should().Be(5555).And.Be(refReadOnly); 209 | refReadOnly2.Should().Be(5000).And.Be(refRead); 210 | } 211 | 212 | [Fact] 213 | public void Method() 214 | { 215 | var target = new PrivateClassTarget(5000) { RequiredInitProperty = 0, RequiredProperty = 0 }; 216 | var proxy = target.AsPrivateProxy(); 217 | 218 | proxy.VoidMethod(); 219 | proxy.VoidMethod(); 220 | proxy.VoidMethod(); 221 | 222 | target.voidMethodCalledCount.Should().Be(3); 223 | 224 | 225 | proxy.ReturnMethod().Should().Be(10); 226 | proxy.ParameterMethod1(9999).Should().Be(9999); 227 | 228 | proxy.ParameterMethod2(10, 20).Should().Be(30); 229 | 230 | proxy.VoidParameter(10, 20); 231 | target.voidMethodCalledCount.Should().Be(30); 232 | 233 | var y = 0; 234 | var zz = 20; 235 | proxy.RefOutInMethod(10, ref y, out var z, in zz); 236 | y.Should().Be(40); 237 | z.Should().Be(9999); 238 | target.voidMethodCalledCount.Should().Be(30); // x + xyz 239 | 240 | proxy.RefReturnMethod() = 1111; 241 | proxy._refReturnMethodField.Should().Be(1111); 242 | 243 | proxy._refReadOnlyReturnMethodField = 99999; 244 | proxy.RefReadOnlyReturnMethod().Should().Be(99999); 245 | 246 | var zzzzz = 999; 247 | ref var xxxxx = ref proxy.RefReturnMethodParameter(ref zzzzz); 248 | xxxxx.Should().Be(999); 249 | xxxxx = 111; 250 | zzzzz.Should().Be(111); 251 | } 252 | 253 | [Fact] 254 | public void StaticField() 255 | { 256 | PrivateClassTargetStructProxy.staticField = 9999; 257 | PrivateClassTargetStructProxy.staticField.Should().Be(9999); 258 | PrivateClassTargetStructProxy.staticReadOnlyField.Should().Be(4444); 259 | } 260 | 261 | [Fact] 262 | public void StaticProperty() 263 | { 264 | PrivateClassTargetStructProxy.StaticProperty = 9999; 265 | PrivateClassTargetStructProxy.StaticGetOnlyPrivateProperty = 8888; 266 | PrivateClassTargetStructProxy.StaticSetOnlyPrivateProperty = 7777; 267 | 268 | PrivateClassTargetStructProxy.StaticSetOnlyProperty = 6666; 269 | 270 | ref var refGet = ref PrivateClassTargetStructProxy.StaticRefGetOnlyProperty; 271 | ref readonly var refRead = ref PrivateClassTargetStructProxy.StaticRefReadOnlyGetOnlyProperty; 272 | 273 | refGet = 5555; 274 | 275 | PrivateClassTargetStructProxy._StaticrefReadOnlyGetOnlyPropertyField = 5000; 276 | 277 | PrivateClassTargetStructProxy.StaticProperty.Should().Be(9999); 278 | PrivateClassTargetStructProxy.StaticGetOnlyPrivateProperty.Should().Be(8888); 279 | PrivateClassTargetStructProxy.StaticSetOnlyPrivateProperty.Should().Be(7777); 280 | PrivateClassTargetStructProxy.StaticGetOnlyProperty.Should().Be(5000); 281 | 282 | PrivateClassTargetStructProxy._StaticsetOnlyPropertyField.Should().Be(6666); 283 | 284 | PrivateClassTargetStructProxy.StaticRefGetOnlyProperty.Should().Be(5555); 285 | 286 | PrivateClassTargetStructProxy._StaticrefGetOnlyPropertyField.Should().Be(5555); 287 | PrivateClassTargetStructProxy._StaticrefReadOnlyGetOnlyPropertyField.Should().Be(5000).And.Be(refRead); 288 | } 289 | 290 | [Fact] 291 | public void StaticMethod() 292 | { 293 | PrivateClassTargetStructProxy.StaticVoidMethod(); 294 | PrivateClassTargetStructProxy.StaticVoidMethod(); 295 | PrivateClassTargetStructProxy.StaticVoidMethod(); 296 | 297 | PrivateClassTarget.staticvoidMethodCalledCount.Should().Be(3); 298 | 299 | 300 | PrivateClassTargetStructProxy.StaticReturnMethod().Should().Be(10); 301 | PrivateClassTargetStructProxy.StaticParameterMethod1(9999).Should().Be(9999); 302 | 303 | PrivateClassTargetStructProxy.StaticParameterMethod2(10, 20).Should().Be(30); 304 | 305 | PrivateClassTargetStructProxy.StaticVoidParameter(10, 20); 306 | PrivateClassTarget.staticvoidMethodCalledCount.Should().Be(30); 307 | 308 | var y = 0; 309 | var zz = 20; 310 | PrivateClassTargetStructProxy.StaticRefOutInMethod(10, ref y, out var z, in zz); 311 | y.Should().Be(40); 312 | z.Should().Be(9999); 313 | PrivateClassTarget.staticvoidMethodCalledCount.Should().Be(30); // x + xyz 314 | 315 | PrivateClassTargetStructProxy.StaticRefReturnMethod() = 1111; 316 | PrivateClassTargetStructProxy._StaticrefReturnMethodField.Should().Be(1111); 317 | 318 | PrivateClassTargetStructProxy._StaticrefReadOnlyReturnMethodField = 99999; 319 | PrivateClassTargetStructProxy.StaticRefReadOnlyReturnMethod().Should().Be(99999); 320 | 321 | var zzzzz = 999; 322 | ref var xxxxx = ref PrivateClassTargetStructProxy.StaticRefReturnMethodParameter(ref zzzzz); 323 | xxxxx.Should().Be(999); 324 | xxxxx = 111; 325 | zzzzz.Should().Be(111); 326 | } 327 | } -------------------------------------------------------------------------------- /tests/PrivateProxy.Tests/DiagnosticsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace PrivateProxy.Tests; 8 | 9 | public class DiagnosticsTest 10 | { 11 | void Compile(int id, string code, bool allowMultipleError = false) 12 | { 13 | var diagnostics = CSharpGeneratorRunner.RunGenerator(code); 14 | if (!allowMultipleError) 15 | { 16 | diagnostics.Length.Should().Be(1); 17 | diagnostics[0].Id.Should().Be("PP" + id.ToString("000")); 18 | } 19 | else 20 | { 21 | diagnostics.Select(x => x.Id).Should().Contain("PP" + id.ToString("000")); 22 | } 23 | } 24 | 25 | [Fact] 26 | public void PP001_MustBePartial() 27 | { 28 | Compile(1, """ 29 | using PrivateProxy; 30 | 31 | public struct Foo 32 | { 33 | int X; 34 | } 35 | 36 | [GeneratePrivateProxy(typeof(Foo))] 37 | public ref struct FooPrivateProxy { } 38 | """); 39 | } 40 | 41 | [Fact] 42 | public void PP002_NotAllowReadOnly() 43 | { 44 | Compile(2, """ 45 | using PrivateProxy; 46 | 47 | public struct Foo 48 | { 49 | int X; 50 | } 51 | 52 | [GeneratePrivateProxy(typeof(Foo))] 53 | public readonly ref partial struct FooPrivateProxy { } 54 | """); 55 | } 56 | 57 | [Fact] 58 | public void PP003_ClassNotAllowRefStruct() 59 | { 60 | Compile(3, """ 61 | using PrivateProxy; 62 | 63 | public class Foo 64 | { 65 | int X; 66 | } 67 | 68 | [GeneratePrivateProxy(typeof(Foo))] 69 | public ref partial struct FooPrivateProxy { } 70 | """); 71 | } 72 | 73 | [Fact] 74 | public void PP004_StructNotAllowClass() 75 | { 76 | Compile(4, """ 77 | using PrivateProxy; 78 | 79 | public struct Foo 80 | { 81 | int X; 82 | } 83 | 84 | [GeneratePrivateProxy(typeof(Foo))] 85 | public partial class FooPrivateProxy { } 86 | """); 87 | } 88 | 89 | [Fact] 90 | public void PP005_StructNotAllowStruct() 91 | { 92 | Compile(5, """ 93 | using PrivateProxy; 94 | 95 | public struct Foo 96 | { 97 | int X; 98 | } 99 | 100 | [GeneratePrivateProxy(typeof(Foo))] 101 | public partial struct FooPrivateProxy { } 102 | """); 103 | } 104 | 105 | [Fact] 106 | public void PP006_RefStructNotSupported() 107 | { 108 | Compile(6, """ 109 | using PrivateProxy; 110 | 111 | public ref struct Foo 112 | { 113 | int X; 114 | } 115 | 116 | [GeneratePrivateProxy(typeof(Foo))] 117 | public ref partial struct FooPrivateProxy { } 118 | """); 119 | } 120 | 121 | [Fact] 122 | public void PP007_GenericsNotSupported() 123 | { 124 | Compile(7, """ 125 | using PrivateProxy; 126 | 127 | public class Foo 128 | { 129 | T X; 130 | } 131 | 132 | [GeneratePrivateProxy(typeof(Foo))] 133 | public partial class FooPrivateProxy { } 134 | """); 135 | 136 | Compile(7, """ 137 | using PrivateProxy; 138 | 139 | public class Foo 140 | { 141 | T X; 142 | } 143 | 144 | [GeneratePrivateProxy(typeof(Foo<>))] 145 | public partial class FooPrivateProxy { } 146 | """); 147 | } 148 | 149 | [Fact] 150 | public void PP008_StaticNotSupported() 151 | { 152 | Compile(8, """ 153 | using PrivateProxy; 154 | 155 | public static class Foo 156 | { 157 | static int x; 158 | } 159 | 160 | [GeneratePrivateProxy(typeof(Foo))] 161 | public partial class FooPrivateProxy { } 162 | """); 163 | } 164 | } -------------------------------------------------------------------------------- /tests/PrivateProxy.Tests/GenerateInternalTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace PrivateProxy.Tests; 8 | 9 | 10 | public class InternalTarget 11 | { 12 | InnerClass innerField = new(); 13 | 14 | private void Hoge() 15 | { 16 | } 17 | 18 | private InnerClass GetInnerClass() => new InnerClass(); 19 | 20 | private InternalClass GetInternalClass => new(); 21 | private InternalClass[] GetInternalArray() => []; 22 | private InternalClass[][] GetInternalNestedArray() => []; 23 | private Dictionary GetInternalGeneric => []; 24 | 25 | class InnerClass; 26 | } 27 | 28 | internal class InternalClass; 29 | 30 | [GeneratePrivateProxy(typeof(InternalTarget))] 31 | public partial struct InnerTargetProxy; 32 | 33 | public class GenerateInternalTest 34 | 35 | { 36 | } 37 | -------------------------------------------------------------------------------- /tests/PrivateProxy.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; 2 | global using Xunit.Abstractions; 3 | global using FluentAssertions; -------------------------------------------------------------------------------- /tests/PrivateProxy.Tests/OtherTypesTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace PrivateProxy.Tests; 8 | 9 | 10 | 11 | // TODO: test inheritance 12 | // TODO: check explicit interface member 13 | // TODO: inheritance class uses new? 14 | // TODO: target record, record struct 15 | 16 | public class BaseType 17 | { 18 | int _baseInt; 19 | } 20 | 21 | public class InheritType : BaseType 22 | { 23 | int _inheritInt; 24 | } 25 | 26 | [GeneratePrivateProxy(typeof(InheritType))] 27 | public partial class InheritProxy; 28 | 29 | public interface TestInterface 30 | { 31 | int Foo { get; set; } 32 | } 33 | 34 | public class ExplitImpl : TestInterface 35 | { 36 | int a; 37 | int TestInterface.Foo { get => a; set => a = value; } 38 | } 39 | 40 | [GeneratePrivateProxy(typeof(ExplitImpl))] 41 | public partial class ExplitImplProxy; 42 | 43 | public record TestRecord(int x, int y) 44 | { 45 | int z; 46 | } 47 | 48 | public record struct TestRecordStruct(int x, int y) 49 | { 50 | int z; 51 | } 52 | 53 | [GeneratePrivateProxy(typeof(TestRecord))] 54 | public partial class TestRecordProxy; 55 | 56 | [GeneratePrivateProxy(typeof(TestRecordStruct))] 57 | public ref partial struct TestRecordStructPrxy; 58 | 59 | public class OtherTypesTest 60 | { 61 | public void Foo() 62 | { 63 | // new InheritType().AsPrivateProxy()._base 64 | // new ExplitImpl().AsPrivateProxy().Foo 65 | 66 | //new TestRecord().AsPrivateProxy().z 67 | // new TestRecordStruct(1, 2).AsPrivateProxy().z; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/PrivateProxy.Tests/PrivateProxy.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | false 8 | true 9 | 10 | 11 | 12 | $(NoWarn);CS1591;CS8604;CS8321;CS0414;CS0169;CS0649 13 | 14 | 15 | 16 | $(NoWarn);CS1591;CS8604;CS8321;CS0414;CS0169;CS0649 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | runtime; build; native; contentfiles; analyzers; buildtransitive 26 | all 27 | 28 | 29 | 30 | 31 | 32 | 33 | Analyzer 34 | false 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /tests/PrivateProxy.Tests/RefStructGenerateTest.cs: -------------------------------------------------------------------------------- 1 | namespace PrivateProxy.Tests; 2 | 3 | public class RefStructGenerateTest 4 | { 5 | // [Fact] 6 | // public void CheckRefStructField() 7 | // { 8 | // var diagnostics = CSharpGeneratorRunner.RunGenerator(""" 9 | //using PrivateProxy; 10 | 11 | //public ref struct RefStructTest 12 | //{ 13 | // public int field; // RefKind=None 14 | // public readonly int readOnlyField; // RefKind=None, IsReadOnly=true 15 | // public ref int refField; // RefKind=Ref 16 | // public ref readonly int refReadOnly; // RefKind=In 17 | // public readonly ref int readonlyRef; // RefKind=Ref, IsReadOnly=true 18 | // public readonly ref readonly int readOnlyRefReadOnly; 19 | //} 20 | 21 | //[GeneratePrivateProxy(typeof(RefStructTest))] 22 | //public partial struct Proxy { } 23 | //"""); 24 | 25 | // diagnostics.Should().BeEmpty(); 26 | // } 27 | 28 | // [Fact] 29 | // public void CheckRefStructProperty() 30 | // { 31 | // var diagnostics = CSharpGeneratorRunner.RunGenerator(""" 32 | //using PrivateProxy; 33 | 34 | //public ref struct RefStructTest 35 | //{ 36 | // public ref int refField; 37 | 38 | // public int Prop0 => 1; 39 | // public readonly int Prop1 => 1; 40 | // public ref int Prop2 => ref refField; // ReturnsByRef:true 41 | // public ref readonly int Prop3 => ref refField; // ReturnsByRefReadOnly:true | can't assign 42 | // public readonly ref int Prop4 => ref refField; // ReturnsByRef:true 43 | // public readonly ref readonly int Prop5 => ref refField;// ReturnsByRefReadOnly:true | can't assign 44 | //} 45 | 46 | //[GeneratePrivateProxy(typeof(RefStructTest))] 47 | //public partial struct Proxy { } 48 | //"""); 49 | 50 | // diagnostics.Should().BeEmpty(); 51 | // } 52 | 53 | // [Fact] 54 | // public void CheckRefStructMethod() 55 | // { 56 | // var diagnostics = CSharpGeneratorRunner.RunGenerator(""" 57 | //using PrivateProxy; 58 | 59 | //public ref struct RefStructTest 60 | //{ 61 | // public ref int refField; 62 | 63 | // public int Method0() => 1; 64 | // public readonly int Method1() => 1; 65 | // public ref int Method2() => ref refField; 66 | // public ref readonly int Method3() => ref refField; 67 | // public readonly ref int Method4() => ref refField; 68 | // public readonly ref readonly int Method5() => ref refField; 69 | //} 70 | 71 | //[GeneratePrivateProxy(typeof(RefStructTest))] 72 | //public partial struct Proxy { } 73 | //"""); 74 | 75 | // diagnostics.Should().BeEmpty(); 76 | // } 77 | 78 | // [Fact] 79 | // public void CheckRefKind() 80 | // { 81 | // var diagnostics = CSharpGeneratorRunner.RunGenerator(""" 82 | //using PrivateProxy; 83 | 84 | //public class ClassTest 85 | //{ 86 | // private void RefOutInMethod(ref readonly int xyz) { } 87 | //} 88 | 89 | //[GeneratePrivateProxy(typeof(ClassTest))] 90 | //public partial struct Proxy { } 91 | //"""); 92 | 93 | // diagnostics.Should().BeEmpty(); 94 | // } 95 | } 96 | -------------------------------------------------------------------------------- /tests/PrivateProxy.Tests/StructGenerateTest.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS0649 2 | #pragma warning disable CS0169 3 | #pragma warning disable CS0414 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace PrivateProxy.Tests; 12 | 13 | public struct PrivateStructTarget 14 | { 15 | public PrivateStructTarget(int readOnlyField) 16 | { 17 | this.readOnlyField = readOnlyField; 18 | this.GetOnlyProperty = readOnlyField; 19 | // this._refReadOnlyGetOnlyPropertyField = readOnlyField; 20 | } 21 | 22 | // public(no generate) 23 | public int publicField; 24 | public int PublicProperty { get; set; } 25 | public void PublicMethod() { } 26 | 27 | // field 28 | private int field; 29 | private readonly int readOnlyField; 30 | // public required int requiredField; 31 | 32 | // property 33 | private int Property { get; set; } 34 | private int GetOnlyProperty { get; } 35 | public int GetOnlyPrivateProperty { private get; set; } 36 | public int SetOnlyPrivateProperty { get; private set; } 37 | public required int RequiredProperty { get; set; } 38 | public int InitProperty { get; init; } 39 | public required int RequiredInitProperty { get; init; } 40 | 41 | int _setOnlyPropertyField; 42 | private int SetOnlyProperty { set => _setOnlyPropertyField = value; } 43 | 44 | public static int _refGetOnlyPropertyField; 45 | private ref int RefGetOnlyProperty => ref _refGetOnlyPropertyField; 46 | 47 | public static int _refReadOnlyGetOnlyPropertyField; 48 | private ref readonly int RefReadOnlyGetOnlyProperty => ref _refReadOnlyGetOnlyPropertyField; 49 | 50 | // method 51 | public int voidMethodCalledCount; 52 | 53 | private void VoidMethod() 54 | { 55 | voidMethodCalledCount++; 56 | } 57 | 58 | private int ReturnMethod() => 10; 59 | 60 | private int ParameterMethod1(int x) => x; 61 | 62 | private int ParameterMethod2(int x, int y) => x + y; 63 | 64 | private void VoidParameter(int x, int y) 65 | { 66 | voidMethodCalledCount = x + y; 67 | } 68 | 69 | private void RefOutInMethod(in int x, ref int y, out int z, ref readonly int xyz) 70 | { 71 | y = 40; 72 | z = 9999; 73 | voidMethodCalledCount = x + xyz; 74 | } 75 | 76 | public static int _refReturnMethodField; 77 | ref int RefReturnMethod() => ref _refReturnMethodField; 78 | 79 | public static int _refReadOnlyReturnMethodField; 80 | private ref readonly int RefReadOnlyReturnMethod() => ref _refReadOnlyReturnMethodField; 81 | 82 | int _refReturnMethodParameterField; 83 | private ref int RefReturnMethodParameter(ref int x) 84 | { 85 | return ref x; 86 | } 87 | 88 | // checker 89 | public (int field, int readOnlyField) GetFields() => (field, readOnlyField); 90 | public (int property, int getOnlyProperty, int getOnlyPrivate, int setOnlyPrivate, int setOnlyProperty) GetProperties() => (Property, GetOnlyProperty, GetOnlyPrivateProperty, SetOnlyPrivateProperty, _setOnlyPropertyField); 91 | 92 | public (int refGetOnlyPropertyField, int _refReadOnlyGetOnlyPropertyField) GetRefProperties() => (_refGetOnlyPropertyField, _refReadOnlyGetOnlyPropertyField); 93 | 94 | 95 | // static 96 | 97 | static int staticField; 98 | static readonly int staticReadOnlyField = 4444; 99 | 100 | 101 | static int StaticProperty { get; set; } 102 | static int StaticGetOnlyProperty { get; } = 5000; 103 | public static int StaticGetOnlyPrivateProperty { private get; set; } 104 | public static int StaticSetOnlyPrivateProperty { get; private set; } 105 | 106 | static int _StaticsetOnlyPropertyField; 107 | private static int StaticSetOnlyProperty { set => _StaticsetOnlyPropertyField = value; } 108 | 109 | static int _StaticrefGetOnlyPropertyField; 110 | private static ref int StaticRefGetOnlyProperty => ref _StaticrefGetOnlyPropertyField; 111 | 112 | static int _StaticrefReadOnlyGetOnlyPropertyField; 113 | private static ref readonly int StaticRefReadOnlyGetOnlyProperty => ref _StaticrefReadOnlyGetOnlyPropertyField; 114 | 115 | 116 | public static int staticvoidMethodCalledCount; 117 | 118 | private static void StaticVoidMethod() 119 | { 120 | staticvoidMethodCalledCount += 1; 121 | } 122 | 123 | static int StaticReturnMethod() => 10; 124 | 125 | static int StaticParameterMethod1(int x) => x; 126 | 127 | static int StaticParameterMethod2(int x, int y) => x + y; 128 | 129 | static void StaticVoidParameter(int x, int y) 130 | { 131 | staticvoidMethodCalledCount = x + y; 132 | } 133 | 134 | static void StaticRefOutInMethod(in int x, ref int y, out int z, ref readonly int xyz) 135 | { 136 | y = 40; 137 | z = 9999; 138 | staticvoidMethodCalledCount = x + xyz; 139 | } 140 | 141 | static int _StaticrefReturnMethodField; 142 | static ref int StaticRefReturnMethod() => ref _StaticrefReturnMethodField; 143 | 144 | static int _StaticrefReadOnlyReturnMethodField; 145 | static ref readonly int StaticRefReadOnlyReturnMethod() => ref _StaticrefReadOnlyReturnMethodField; 146 | 147 | static ref int StaticRefReturnMethodParameter(ref int x) 148 | { 149 | return ref x; 150 | } 151 | 152 | 153 | } 154 | 155 | [GeneratePrivateProxy(typeof(PrivateStructTarget))] 156 | public ref partial struct PrivateStructTargetStructProxy; 157 | 158 | 159 | 160 | public class StructGenerateTest 161 | { 162 | [Fact] 163 | public void Field() 164 | { 165 | var target = new PrivateStructTarget(5000) { RequiredInitProperty = 0, RequiredProperty = 0 }; 166 | 167 | var proxy = target.AsPrivateProxy(); 168 | 169 | proxy.field = 1000; 170 | 171 | var (field, readOnlyField) = target.GetFields(); 172 | 173 | field.Should().Be(1000).And.Be(proxy.field); 174 | readOnlyField.Should().Be(5000).And.Be(proxy.readOnlyField); 175 | } 176 | 177 | [Fact] 178 | public void Property() 179 | { 180 | var target = new PrivateStructTarget(5000) { RequiredInitProperty = 0, RequiredProperty = 0 }; 181 | PrivateStructTarget._refReadOnlyGetOnlyPropertyField = 5000; 182 | var proxy = target.AsPrivateProxy(); 183 | 184 | proxy.Property = 9999; 185 | proxy.GetOnlyPrivateProperty = 8888; 186 | proxy.SetOnlyPrivateProperty = 7777; 187 | 188 | proxy.SetOnlyProperty = 6666; 189 | 190 | ref var refGet = ref proxy.RefGetOnlyProperty; 191 | ref readonly var refRead = ref proxy.RefReadOnlyGetOnlyProperty; 192 | 193 | refGet = 5555; 194 | // refRead = 100; can't write 195 | 196 | var (prop, getOnly, getOnlyPrivate, setOnlyPrivate, setOnly) = target.GetProperties(); 197 | 198 | prop.Should().Be(9999).And.Be(proxy.Property); 199 | getOnlyPrivate.Should().Be(8888).And.Be(proxy.GetOnlyPrivateProperty); 200 | setOnlyPrivate.Should().Be(7777).And.Be(proxy.SetOnlyPrivateProperty); 201 | getOnly.Should().Be(5000).And.Be(proxy.GetOnlyProperty); 202 | 203 | setOnly.Should().Be(6666); 204 | 205 | var (refReadOnly, refReadOnly2) = target.GetRefProperties(); 206 | 207 | proxy.RefGetOnlyProperty.Should().Be(5555); 208 | 209 | 210 | refGet.Should().Be(5555).And.Be(refReadOnly); 211 | refReadOnly2.Should().Be(5000).And.Be(refRead); 212 | } 213 | 214 | [Fact] 215 | public void Method() 216 | { 217 | var target = new PrivateStructTarget(5000) { RequiredInitProperty = 0, RequiredProperty = 0 }; 218 | var proxy = target.AsPrivateProxy(); 219 | 220 | proxy.VoidMethod(); 221 | proxy.VoidMethod(); 222 | proxy.VoidMethod(); 223 | 224 | target.voidMethodCalledCount.Should().Be(3); 225 | 226 | 227 | proxy.ReturnMethod().Should().Be(10); 228 | proxy.ParameterMethod1(9999).Should().Be(9999); 229 | 230 | proxy.ParameterMethod2(10, 20).Should().Be(30); 231 | 232 | proxy.VoidParameter(10, 20); 233 | target.voidMethodCalledCount.Should().Be(30); 234 | 235 | var y = 0; 236 | var zz = 20; 237 | proxy.RefOutInMethod(10, ref y, out var z, in zz); 238 | y.Should().Be(40); 239 | z.Should().Be(9999); 240 | target.voidMethodCalledCount.Should().Be(30); // x + xyz 241 | 242 | proxy.RefReturnMethod() = 1111; 243 | PrivateStructTarget._refReturnMethodField.Should().Be(1111); 244 | 245 | PrivateStructTarget._refReadOnlyReturnMethodField = 99999; 246 | proxy.RefReadOnlyReturnMethod().Should().Be(99999); 247 | 248 | var zzzzz = 999; 249 | ref var xxxxx = ref proxy.RefReturnMethodParameter(ref zzzzz); 250 | xxxxx.Should().Be(999); 251 | xxxxx = 111; 252 | zzzzz.Should().Be(111); 253 | } 254 | 255 | [Fact] 256 | public void StaticField() 257 | { 258 | PrivateStructTargetStructProxy.staticField = 9999; 259 | PrivateStructTargetStructProxy.staticField.Should().Be(9999); 260 | PrivateStructTargetStructProxy.staticReadOnlyField.Should().Be(4444); 261 | } 262 | 263 | [Fact] 264 | public void StaticProperty() 265 | { 266 | PrivateStructTargetStructProxy.StaticProperty = 9999; 267 | PrivateStructTargetStructProxy.StaticGetOnlyPrivateProperty = 8888; 268 | PrivateStructTargetStructProxy.StaticSetOnlyPrivateProperty = 7777; 269 | 270 | PrivateStructTargetStructProxy.StaticSetOnlyProperty = 6666; 271 | 272 | ref var refGet = ref PrivateStructTargetStructProxy.StaticRefGetOnlyProperty; 273 | ref readonly var refRead = ref PrivateStructTargetStructProxy.StaticRefReadOnlyGetOnlyProperty; 274 | 275 | refGet = 5555; 276 | 277 | PrivateStructTargetStructProxy._StaticrefReadOnlyGetOnlyPropertyField = 5000; 278 | 279 | PrivateStructTargetStructProxy.StaticProperty.Should().Be(9999); 280 | PrivateStructTargetStructProxy.StaticGetOnlyPrivateProperty.Should().Be(8888); 281 | PrivateStructTargetStructProxy.StaticSetOnlyPrivateProperty.Should().Be(7777); 282 | PrivateStructTargetStructProxy.StaticGetOnlyProperty.Should().Be(5000); 283 | 284 | PrivateStructTargetStructProxy._StaticsetOnlyPropertyField.Should().Be(6666); 285 | 286 | PrivateStructTargetStructProxy.StaticRefGetOnlyProperty.Should().Be(5555); 287 | 288 | PrivateStructTargetStructProxy._StaticrefGetOnlyPropertyField.Should().Be(5555); 289 | PrivateStructTargetStructProxy._StaticrefReadOnlyGetOnlyPropertyField.Should().Be(5000).And.Be(refRead); 290 | } 291 | 292 | [Fact] 293 | public void StaticMethod() 294 | { 295 | PrivateStructTargetStructProxy.StaticVoidMethod(); 296 | PrivateStructTargetStructProxy.StaticVoidMethod(); 297 | PrivateStructTargetStructProxy.StaticVoidMethod(); 298 | 299 | PrivateStructTarget.staticvoidMethodCalledCount.Should().Be(3); 300 | 301 | 302 | PrivateStructTargetStructProxy.StaticReturnMethod().Should().Be(10); 303 | PrivateStructTargetStructProxy.StaticParameterMethod1(9999).Should().Be(9999); 304 | 305 | PrivateStructTargetStructProxy.StaticParameterMethod2(10, 20).Should().Be(30); 306 | 307 | PrivateStructTargetStructProxy.StaticVoidParameter(10, 20); 308 | PrivateStructTarget.staticvoidMethodCalledCount.Should().Be(30); 309 | 310 | var y = 0; 311 | var zz = 20; 312 | PrivateStructTargetStructProxy.StaticRefOutInMethod(10, ref y, out var z, in zz); 313 | y.Should().Be(40); 314 | z.Should().Be(9999); 315 | PrivateStructTarget.staticvoidMethodCalledCount.Should().Be(30); // x + xyz 316 | 317 | PrivateStructTargetStructProxy.StaticRefReturnMethod() = 1111; 318 | PrivateStructTargetStructProxy._StaticrefReturnMethodField.Should().Be(1111); 319 | 320 | PrivateStructTargetStructProxy._StaticrefReadOnlyReturnMethodField = 99999; 321 | PrivateStructTargetStructProxy.StaticRefReadOnlyReturnMethod().Should().Be(99999); 322 | 323 | var zzzzz = 999; 324 | ref var xxxxx = ref PrivateStructTargetStructProxy.StaticRefReturnMethodParameter(ref zzzzz); 325 | xxxxx.Should().Be(999); 326 | xxxxx = 111; 327 | zzzzz.Should().Be(111); 328 | } 329 | } -------------------------------------------------------------------------------- /tests/PrivateProxy.Tests/Utils/CsharpGeneratorRunner.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp; 3 | using Microsoft.CodeAnalysis.Diagnostics; 4 | using PrivateProxy.Generator; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace PrivateProxy.Tests; 8 | 9 | public static class CSharpGeneratorRunner 10 | { 11 | static Compilation baseCompilation = default!; 12 | 13 | [ModuleInitializer] 14 | public static void InitializeCompilation() 15 | { 16 | // running .NET Core system assemblies dir path 17 | var baseAssemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location)!; 18 | var systemAssemblies = Directory.GetFiles(baseAssemblyPath) 19 | .Where(x => 20 | { 21 | var fileName = Path.GetFileName(x); 22 | if (fileName.EndsWith("Native.dll")) return false; 23 | return fileName.StartsWith("System") || (fileName is "mscorlib.dll" or "netstandard.dll"); 24 | }); 25 | 26 | var references = systemAssemblies 27 | .Select(x => MetadataReference.CreateFromFile(x)) 28 | .ToArray(); 29 | 30 | var compilation = CSharpCompilation.Create("generatortest", 31 | references: references, 32 | options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); 33 | 34 | baseCompilation = compilation; 35 | } 36 | 37 | public static Diagnostic[] RunGenerator(string source, string[]? preprocessorSymbols = null, AnalyzerConfigOptionsProvider? options = null) 38 | { 39 | if (preprocessorSymbols == null) 40 | { 41 | preprocessorSymbols = new[] { "NET8_0_OR_GREATER" }; 42 | } 43 | var parseOptions = new CSharpParseOptions(LanguageVersion.CSharp11, preprocessorSymbols: preprocessorSymbols); 44 | 45 | var driver = CSharpGeneratorDriver.Create(new PrivateProxyGenerator()).WithUpdatedParseOptions(parseOptions); 46 | if (options != null) 47 | { 48 | driver = (Microsoft.CodeAnalysis.CSharp.CSharpGeneratorDriver)driver.WithUpdatedAnalyzerConfigOptions(options); 49 | } 50 | 51 | var compilation = baseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(source, parseOptions)); 52 | 53 | driver.RunGeneratorsAndUpdateCompilation(compilation, out var newCompilation, out var diagnostics); 54 | 55 | // combine diagnostics as result.(ignore warning) 56 | var compilationDiagnostics = newCompilation.GetDiagnostics(); 57 | return diagnostics.Concat(compilationDiagnostics).Where(x => x.Severity == DiagnosticSeverity.Error).ToArray(); 58 | } 59 | 60 | public static (string FilePath, string Code)[] RunGeneratorCode(string source, string[]? preprocessorSymbols = null, AnalyzerConfigOptionsProvider? options = null) 61 | { 62 | if (preprocessorSymbols == null) 63 | { 64 | preprocessorSymbols = new[] { "NET8_0_OR_GREATER" }; 65 | } 66 | var parseOptions = new CSharpParseOptions(LanguageVersion.CSharp11, preprocessorSymbols: preprocessorSymbols); 67 | 68 | var driver = CSharpGeneratorDriver.Create(new PrivateProxyGenerator()).WithUpdatedParseOptions(parseOptions); 69 | if (options != null) 70 | { 71 | driver = (Microsoft.CodeAnalysis.CSharp.CSharpGeneratorDriver)driver.WithUpdatedAnalyzerConfigOptions(options); 72 | } 73 | 74 | var compilation = baseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(source, parseOptions)); 75 | 76 | driver.RunGeneratorsAndUpdateCompilation(compilation, out var newCompilation, out var diagnostics); 77 | 78 | return newCompilation.SyntaxTrees.Select(x => (x.FilePath, x.ToString())).ToArray(); 79 | } 80 | } --------------------------------------------------------------------------------