├── .appveyor.yml ├── .gitattributes ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── .vsts-pipelines └── builds │ ├── ci-internal.yml │ └── ci-public.yml ├── CONTRIBUTING.md ├── Directory.Build.props ├── Directory.Build.targets ├── EventNotification.sln ├── LICENSE.txt ├── NuGet.config ├── NuGetPackageVerifier.json ├── README.md ├── build.cmd ├── build.sh ├── build ├── Key.snk ├── dependencies.props ├── repo.props └── sources.props ├── korebuild-lock.txt ├── korebuild.json ├── run.cmd ├── run.ps1 ├── run.sh ├── src ├── Directory.Build.props └── Microsoft.Extensions.DiagnosticAdapter │ ├── DiagnosticListenerExtensions.cs │ ├── DiagnosticNameAttribute.cs │ ├── DiagnosticSourceAdapter.cs │ ├── IDiagnosticSourceMethodAdapter.cs │ ├── Infrastructure │ ├── IProxy.cs │ └── IProxyFactory.cs │ ├── Internal │ ├── InvalidProxyOperationException.cs │ ├── ProxyAssembly.cs │ ├── ProxyBase.cs │ ├── ProxyBaseOfT.cs │ ├── ProxyEnumerable.cs │ ├── ProxyFactory.cs │ ├── ProxyList.cs │ ├── ProxyMethodEmitter.cs │ ├── ProxyTypeCache.cs │ ├── ProxyTypeCacheResult.cs │ └── ProxyTypeEmitter.cs │ ├── Microsoft.Extensions.DiagnosticAdapter.csproj │ ├── Properties │ ├── AssemblyInfo.cs │ └── Resources.Designer.cs │ ├── ProxyDiagnosticSourceMethodAdapter.cs │ ├── Resources.resx │ ├── baseline.net461.json │ ├── baseline.netcore.json │ ├── baseline.netframework.json │ └── breakingchanges.netcore.json ├── test ├── Directory.Build.props └── Microsoft.Extensions.DiagnosticAdapter.Test │ ├── DiagnosticSourceAdapterTest.cs │ ├── Internal │ ├── ProxyFactoryTest.cs │ └── ProxyTypeEmitterTest.cs │ ├── Microsoft.Extensions.DiagnosticAdapter.Test.csproj │ └── ProxyDiagnosticSourceMethodAdapterTest.cs └── version.props /.appveyor.yml: -------------------------------------------------------------------------------- 1 | init: 2 | - git config --global core.autocrlf true 3 | branches: 4 | only: 5 | - master 6 | - /^release\/.*$/ 7 | - /^(.*\/)?ci-.*$/ 8 | build_script: 9 | - ps: .\run.ps1 default-build 10 | clone_depth: 1 11 | environment: 12 | global: 13 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 14 | DOTNET_CLI_TELEMETRY_OPTOUT: 1 15 | test: 'off' 16 | deploy: 'off' 17 | os: Visual Studio 2017 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.doc diff=astextplain 2 | *.DOC diff=astextplain 3 | *.docx diff=astextplain 4 | *.DOCX diff=astextplain 5 | *.dot diff=astextplain 6 | *.DOT diff=astextplain 7 | *.pdf diff=astextplain 8 | *.PDF diff=astextplain 9 | *.rtf diff=astextplain 10 | *.RTF diff=astextplain 11 | 12 | *.jpg binary 13 | *.png binary 14 | *.gif binary 15 | 16 | *.cs text=auto diff=csharp 17 | *.vb text=auto 18 | *.resx text=auto 19 | *.c text=auto 20 | *.cpp text=auto 21 | *.cxx text=auto 22 | *.h text=auto 23 | *.hxx text=auto 24 | *.py text=auto 25 | *.rb text=auto 26 | *.java text=auto 27 | *.html text=auto 28 | *.htm text=auto 29 | *.css text=auto 30 | *.scss text=auto 31 | *.sass text=auto 32 | *.less text=auto 33 | *.js text=auto 34 | *.lisp text=auto 35 | *.clj text=auto 36 | *.sql text=auto 37 | *.php text=auto 38 | *.lua text=auto 39 | *.m text=auto 40 | *.asm text=auto 41 | *.erl text=auto 42 | *.fs text=auto 43 | *.fsx text=auto 44 | *.hs text=auto 45 | 46 | *.csproj text=auto 47 | *.vbproj text=auto 48 | *.fsproj text=auto 49 | *.dbproj text=auto 50 | *.sln text=auto eol=crlf 51 | 52 | *.sh eol=lf -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | THIS ISSUE TRACKER IS CLOSED - please log new issues here: https://github.com/aspnet/Home/issues 2 | 3 | For information about this change, see https://github.com/aspnet/Announcements/issues/283 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | [Oo]bj/ 2 | [Bb]in/ 3 | TestResults/ 4 | .nuget/ 5 | .build/ 6 | .testPublish/ 7 | *.sln.ide/ 8 | _ReSharper.*/ 9 | packages/ 10 | artifacts/ 11 | PublishProfiles/ 12 | .vs/ 13 | bower_components/ 14 | node_modules/ 15 | debugSettings.json 16 | project.lock.json 17 | *.user 18 | *.suo 19 | *.cache 20 | *.docstates 21 | _ReSharper.* 22 | nuget.exe 23 | *net45.csproj 24 | *net451.csproj 25 | *k10.csproj 26 | *.psess 27 | *.vsp 28 | *.pidb 29 | *.userprefs 30 | *DS_Store 31 | *.ncrunchsolution 32 | *.*sdf 33 | *.ipch 34 | .settings 35 | *.sln.ide 36 | node_modules 37 | *launchSettings.json 38 | *.orig 39 | global.json 40 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | sudo: false 3 | dist: trusty 4 | env: 5 | global: 6 | - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 7 | - DOTNET_CLI_TELEMETRY_OPTOUT: 1 8 | mono: none 9 | os: 10 | - linux 11 | - osx 12 | osx_image: xcode8.2 13 | addons: 14 | apt: 15 | packages: 16 | - libunwind8 17 | branches: 18 | only: 19 | - master 20 | - /^release\/.*$/ 21 | - /^(.*\/)?ci-.*$/ 22 | before_install: 23 | - if test "$TRAVIS_OS_NAME" == "osx"; then brew update; brew install openssl; ln -s 24 | /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/; ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib 25 | /usr/local/lib/; fi 26 | script: 27 | - ./build.sh 28 | -------------------------------------------------------------------------------- /.vsts-pipelines/builds/ci-internal.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - master 3 | - release/* 4 | 5 | resources: 6 | repositories: 7 | - repository: buildtools 8 | type: git 9 | name: aspnet-BuildTools 10 | ref: refs/heads/master 11 | 12 | phases: 13 | - template: .vsts-pipelines/templates/project-ci.yml@buildtools 14 | -------------------------------------------------------------------------------- /.vsts-pipelines/builds/ci-public.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - master 3 | - release/* 4 | 5 | # See https://github.com/aspnet/BuildTools 6 | resources: 7 | repositories: 8 | - repository: buildtools 9 | type: github 10 | endpoint: DotNet-Bot GitHub Connection 11 | name: aspnet/BuildTools 12 | ref: refs/heads/master 13 | 14 | phases: 15 | - template: .vsts-pipelines/templates/project-ci.yml@buildtools 16 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ====== 3 | 4 | Information on contributing to this repo is in the [Contributing Guide](https://github.com/aspnet/Home/blob/master/CONTRIBUTING.md) in the Home repo. 5 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 |  2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Microsoft ASP.NET Core 12 | https://github.com/aspnet/eventnotification 13 | git 14 | $(MSBuildThisFileDirectory) 15 | $(MSBuildThisFileDirectory)build\Key.snk 16 | true 17 | true 18 | 19 | 20 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | $(MicrosoftNETCoreApp20PackageVersion) 4 | $(MicrosoftNETCoreApp21PackageVersion) 5 | $(MicrosoftNETCoreApp22PackageVersion) 6 | $(NETStandardLibrary20PackageVersion) 7 | 8 | 99.9 9 | 10 | 11 | -------------------------------------------------------------------------------- /EventNotification.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 15 3 | VisualStudioVersion = 15.0.26815.3 4 | MinimumVisualStudioVersion = 15.0.26730.03 5 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8B66E199-1AFE-4B68-AC71-4521C46EC4CD}" 6 | ProjectSection(SolutionItems) = preProject 7 | src\Directory.Build.props = src\Directory.Build.props 8 | EndProjectSection 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{D75011A4-DEEE-48DE-BB83-CE042F2AC05B}" 11 | ProjectSection(SolutionItems) = preProject 12 | test\Directory.Build.props = test\Directory.Build.props 13 | EndProjectSection 14 | EndProject 15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.DiagnosticAdapter", "src\Microsoft.Extensions.DiagnosticAdapter\Microsoft.Extensions.DiagnosticAdapter.csproj", "{87808F3C-362E-4261-AA4A-EE40BA39D64E}" 16 | EndProject 17 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.DiagnosticAdapter.Test", "test\Microsoft.Extensions.DiagnosticAdapter.Test\Microsoft.Extensions.DiagnosticAdapter.Test.csproj", "{02754747-C385-4662-AD58-49CE54C3D7AF}" 18 | EndProject 19 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3000A721-AF6D-46D5-839C-60A2EC4A9693}" 20 | ProjectSection(SolutionItems) = preProject 21 | .appveyor.yml = .appveyor.yml 22 | .gitattributes = .gitattributes 23 | .gitignore = .gitignore 24 | .travis.yml = .travis.yml 25 | build.cmd = build.cmd 26 | build.ps1 = build.ps1 27 | build.sh = build.sh 28 | CONTRIBUTING.md = CONTRIBUTING.md 29 | Directory.Build.props = Directory.Build.props 30 | Directory.Build.targets = Directory.Build.targets 31 | LICENSE.txt = LICENSE.txt 32 | NuGet.config = NuGet.config 33 | NuGetPackageVerifier.json = NuGetPackageVerifier.json 34 | README.md = README.md 35 | version.xml = version.xml 36 | EndProjectSection 37 | EndProject 38 | Global 39 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 40 | Debug|Any CPU = Debug|Any CPU 41 | Release|Any CPU = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 44 | {87808F3C-362E-4261-AA4A-EE40BA39D64E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {87808F3C-362E-4261-AA4A-EE40BA39D64E}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {87808F3C-362E-4261-AA4A-EE40BA39D64E}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {87808F3C-362E-4261-AA4A-EE40BA39D64E}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {02754747-C385-4662-AD58-49CE54C3D7AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {02754747-C385-4662-AD58-49CE54C3D7AF}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {02754747-C385-4662-AD58-49CE54C3D7AF}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {02754747-C385-4662-AD58-49CE54C3D7AF}.Release|Any CPU.Build.0 = Release|Any CPU 52 | EndGlobalSection 53 | GlobalSection(SolutionProperties) = preSolution 54 | HideSolutionNode = FALSE 55 | EndGlobalSection 56 | GlobalSection(NestedProjects) = preSolution 57 | {87808F3C-362E-4261-AA4A-EE40BA39D64E} = {8B66E199-1AFE-4B68-AC71-4521C46EC4CD} 58 | {02754747-C385-4662-AD58-49CE54C3D7AF} = {D75011A4-DEEE-48DE-BB83-CE042F2AC05B} 59 | EndGlobalSection 60 | GlobalSection(ExtensibilityGlobals) = postSolution 61 | SolutionGuid = {AB0EDBB3-E00B-4334-A17A-054D3771E912} 62 | EndGlobalSection 63 | EndGlobal 64 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright (c) .NET Foundation and Contributors 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /NuGetPackageVerifier.json: -------------------------------------------------------------------------------- 1 | { 2 | "Default": { 3 | "rules": [ 4 | "DefaultCompositeRule" 5 | ] 6 | } 7 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | EventNotification [Archived] 2 | ============================ 3 | 4 | **This GitHub project has been archived.** Ongoing development on this project can be found in . 5 | 6 | Notice 7 | ------- 8 | 9 | The infrastructure for publishing notifications has moved to the .NET Framework. See the new [`DiagnosticSource`](https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSource.cs) and [`DiagnosticListener`](https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs) APIs in the `System.Diagnostics.DiagnosticSource` package. The infrastructure provided here is for subscribing to events using runtime-generated proxies. 10 | 11 | This project is part of .NET Extensions. You can find samples, documentation and getting started instructions at the [Extensions](https://github.com/aspnet/Extensions) repo. 12 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0run.ps1' default-build %*; exit $LASTEXITCODE" 3 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 5 | 6 | # Call "sync" between "chmod" and execution to prevent "text file busy" error in Docker (aufs) 7 | chmod +x "$DIR/run.sh"; sync 8 | "$DIR/run.sh" default-build "$@" 9 | -------------------------------------------------------------------------------- /build/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspnet/EventNotification/faf917b447af15d41b48ffacb7882377deae3caa/build/Key.snk -------------------------------------------------------------------------------- /build/dependencies.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 4 | 5 | 6 | 3.0.0-alpha1-20180919.1 7 | 3.0.0-alpha1-10549 8 | 2.0.9 9 | 2.1.3 10 | 2.2.0-preview2-26905-02 11 | 15.6.1 12 | 2.0.3 13 | 4.6.0-preview1-26907-04 14 | 0.10.0 15 | 2.3.1 16 | 2.4.0 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /build/repo.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Internal.AspNetCore.Universe.Lineup 7 | https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /build/sources.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(DotNetRestoreSources) 6 | 7 | $(RestoreSources); 8 | https://dotnet.myget.org/F/dotnet-core/api/v3/index.json; 9 | https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json; 10 | https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json; 11 | 12 | 13 | $(RestoreSources); 14 | https://api.nuget.org/v3/index.json; 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /korebuild-lock.txt: -------------------------------------------------------------------------------- 1 | version:3.0.0-alpha1-20180919.1 2 | commithash:3066ae0a230870ea07e3f132605b5e5493f8bbd4 3 | -------------------------------------------------------------------------------- /korebuild.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/master/tools/korebuild.schema.json", 3 | "channel": "master" 4 | } 5 | -------------------------------------------------------------------------------- /run.cmd: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0run.ps1' %*; exit $LASTEXITCODE" 3 | -------------------------------------------------------------------------------- /run.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env powershell 2 | #requires -version 4 3 | 4 | <# 5 | .SYNOPSIS 6 | Executes KoreBuild commands. 7 | 8 | .DESCRIPTION 9 | Downloads korebuild if required. Then executes the KoreBuild command. To see available commands, execute with `-Command help`. 10 | 11 | .PARAMETER Command 12 | The KoreBuild command to run. 13 | 14 | .PARAMETER Path 15 | The folder to build. Defaults to the folder containing this script. 16 | 17 | .PARAMETER Channel 18 | The channel of KoreBuild to download. Overrides the value from the config file. 19 | 20 | .PARAMETER DotNetHome 21 | The directory where .NET Core tools will be stored. 22 | 23 | .PARAMETER ToolsSource 24 | The base url where build tools can be downloaded. Overrides the value from the config file. 25 | 26 | .PARAMETER Update 27 | Updates KoreBuild to the latest version even if a lock file is present. 28 | 29 | .PARAMETER Reinstall 30 | Re-installs KoreBuild 31 | 32 | .PARAMETER ConfigFile 33 | The path to the configuration file that stores values. Defaults to korebuild.json. 34 | 35 | .PARAMETER ToolsSourceSuffix 36 | The Suffix to append to the end of the ToolsSource. Useful for query strings in blob stores. 37 | 38 | .PARAMETER CI 39 | Sets up CI specific settings and variables. 40 | 41 | .PARAMETER Arguments 42 | Arguments to be passed to the command 43 | 44 | .NOTES 45 | This function will create a file $PSScriptRoot/korebuild-lock.txt. This lock file can be committed to source, but does not have to be. 46 | When the lockfile is not present, KoreBuild will create one using latest available version from $Channel. 47 | 48 | The $ConfigFile is expected to be an JSON file. It is optional, and the configuration values in it are optional as well. Any options set 49 | in the file are overridden by command line parameters. 50 | 51 | .EXAMPLE 52 | Example config file: 53 | ```json 54 | { 55 | "$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/master/tools/korebuild.schema.json", 56 | "channel": "master", 57 | "toolsSource": "https://aspnetcore.blob.core.windows.net/buildtools" 58 | } 59 | ``` 60 | #> 61 | [CmdletBinding(PositionalBinding = $false)] 62 | param( 63 | [Parameter(Mandatory = $true, Position = 0)] 64 | [string]$Command, 65 | [string]$Path = $PSScriptRoot, 66 | [Alias('c')] 67 | [string]$Channel, 68 | [Alias('d')] 69 | [string]$DotNetHome, 70 | [Alias('s')] 71 | [string]$ToolsSource, 72 | [Alias('u')] 73 | [switch]$Update, 74 | [switch]$Reinstall, 75 | [string]$ToolsSourceSuffix, 76 | [string]$ConfigFile = $null, 77 | [switch]$CI, 78 | [Parameter(ValueFromRemainingArguments = $true)] 79 | [string[]]$Arguments 80 | ) 81 | 82 | Set-StrictMode -Version 2 83 | $ErrorActionPreference = 'Stop' 84 | 85 | # 86 | # Functions 87 | # 88 | 89 | function Get-KoreBuild { 90 | 91 | $lockFile = Join-Path $Path 'korebuild-lock.txt' 92 | 93 | if (!(Test-Path $lockFile) -or $Update) { 94 | Get-RemoteFile "$ToolsSource/korebuild/channels/$Channel/latest.txt" $lockFile $ToolsSourceSuffix 95 | } 96 | 97 | $version = Get-Content $lockFile | Where-Object { $_ -like 'version:*' } | Select-Object -first 1 98 | if (!$version) { 99 | Write-Error "Failed to parse version from $lockFile. Expected a line that begins with 'version:'" 100 | } 101 | $version = $version.TrimStart('version:').Trim() 102 | $korebuildPath = Join-Paths $DotNetHome ('buildtools', 'korebuild', $version) 103 | 104 | if ($Reinstall -and (Test-Path $korebuildPath)) { 105 | Remove-Item -Force -Recurse $korebuildPath 106 | } 107 | 108 | if (!(Test-Path $korebuildPath)) { 109 | Write-Host -ForegroundColor Magenta "Downloading KoreBuild $version" 110 | New-Item -ItemType Directory -Path $korebuildPath | Out-Null 111 | $remotePath = "$ToolsSource/korebuild/artifacts/$version/korebuild.$version.zip" 112 | 113 | try { 114 | $tmpfile = Join-Path ([IO.Path]::GetTempPath()) "KoreBuild-$([guid]::NewGuid()).zip" 115 | Get-RemoteFile $remotePath $tmpfile $ToolsSourceSuffix 116 | if (Get-Command -Name 'Microsoft.PowerShell.Archive\Expand-Archive' -ErrorAction Ignore) { 117 | # Use built-in commands where possible as they are cross-plat compatible 118 | Microsoft.PowerShell.Archive\Expand-Archive -Path $tmpfile -DestinationPath $korebuildPath 119 | } 120 | else { 121 | # Fallback to old approach for old installations of PowerShell 122 | Add-Type -AssemblyName System.IO.Compression.FileSystem 123 | [System.IO.Compression.ZipFile]::ExtractToDirectory($tmpfile, $korebuildPath) 124 | } 125 | } 126 | catch { 127 | Remove-Item -Recurse -Force $korebuildPath -ErrorAction Ignore 128 | throw 129 | } 130 | finally { 131 | Remove-Item $tmpfile -ErrorAction Ignore 132 | } 133 | } 134 | 135 | return $korebuildPath 136 | } 137 | 138 | function Join-Paths([string]$path, [string[]]$childPaths) { 139 | $childPaths | ForEach-Object { $path = Join-Path $path $_ } 140 | return $path 141 | } 142 | 143 | function Get-RemoteFile([string]$RemotePath, [string]$LocalPath, [string]$RemoteSuffix) { 144 | if ($RemotePath -notlike 'http*') { 145 | Copy-Item $RemotePath $LocalPath 146 | return 147 | } 148 | 149 | $retries = 10 150 | while ($retries -gt 0) { 151 | $retries -= 1 152 | try { 153 | Invoke-WebRequest -UseBasicParsing -Uri $($RemotePath + $RemoteSuffix) -OutFile $LocalPath 154 | return 155 | } 156 | catch { 157 | Write-Verbose "Request failed. $retries retries remaining" 158 | } 159 | } 160 | 161 | Write-Error "Download failed: '$RemotePath'." 162 | } 163 | 164 | # 165 | # Main 166 | # 167 | 168 | # Load configuration or set defaults 169 | 170 | $Path = Resolve-Path $Path 171 | if (!$ConfigFile) { $ConfigFile = Join-Path $Path 'korebuild.json' } 172 | 173 | if (Test-Path $ConfigFile) { 174 | try { 175 | $config = Get-Content -Raw -Encoding UTF8 -Path $ConfigFile | ConvertFrom-Json 176 | if ($config) { 177 | if (!($Channel) -and (Get-Member -Name 'channel' -InputObject $config)) { [string] $Channel = $config.channel } 178 | if (!($ToolsSource) -and (Get-Member -Name 'toolsSource' -InputObject $config)) { [string] $ToolsSource = $config.toolsSource} 179 | } 180 | } 181 | catch { 182 | Write-Host -ForegroundColor Red $Error[0] 183 | Write-Error "$ConfigFile contains invalid JSON." 184 | exit 1 185 | } 186 | } 187 | 188 | if (!$DotNetHome) { 189 | $DotNetHome = if ($env:DOTNET_HOME) { $env:DOTNET_HOME } ` 190 | elseif ($env:USERPROFILE) { Join-Path $env:USERPROFILE '.dotnet'} ` 191 | elseif ($env:HOME) {Join-Path $env:HOME '.dotnet'}` 192 | else { Join-Path $PSScriptRoot '.dotnet'} 193 | } 194 | 195 | if (!$Channel) { $Channel = 'master' } 196 | if (!$ToolsSource) { $ToolsSource = 'https://aspnetcore.blob.core.windows.net/buildtools' } 197 | 198 | # Execute 199 | 200 | $korebuildPath = Get-KoreBuild 201 | Import-Module -Force -Scope Local (Join-Path $korebuildPath 'KoreBuild.psd1') 202 | 203 | try { 204 | Set-KoreBuildSettings -ToolsSource $ToolsSource -DotNetHome $DotNetHome -RepoPath $Path -ConfigFile $ConfigFile -CI:$CI 205 | Invoke-KoreBuildCommand $Command @Arguments 206 | } 207 | finally { 208 | Remove-Module 'KoreBuild' -ErrorAction Ignore 209 | } 210 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | # 6 | # variables 7 | # 8 | 9 | RESET="\033[0m" 10 | RED="\033[0;31m" 11 | YELLOW="\033[0;33m" 12 | MAGENTA="\033[0;95m" 13 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 14 | [ -z "${DOTNET_HOME:-}" ] && DOTNET_HOME="$HOME/.dotnet" 15 | verbose=false 16 | update=false 17 | reinstall=false 18 | repo_path="$DIR" 19 | channel='' 20 | tools_source='' 21 | tools_source_suffix='' 22 | ci=false 23 | 24 | # 25 | # Functions 26 | # 27 | __usage() { 28 | echo "Usage: $(basename "${BASH_SOURCE[0]}") command [options] [[--] ...]" 29 | echo "" 30 | echo "Arguments:" 31 | echo " command The command to be run." 32 | echo " ... Arguments passed to the command. Variable number of arguments allowed." 33 | echo "" 34 | echo "Options:" 35 | echo " --verbose Show verbose output." 36 | echo " -c|--channel The channel of KoreBuild to download. Overrides the value from the config file.." 37 | echo " --config-file The path to the configuration file that stores values. Defaults to korebuild.json." 38 | echo " -d|--dotnet-home The directory where .NET Core tools will be stored. Defaults to '\$DOTNET_HOME' or '\$HOME/.dotnet." 39 | echo " --path The directory to build. Defaults to the directory containing the script." 40 | echo " -s|--tools-source|-ToolsSource The base url where build tools can be downloaded. Overrides the value from the config file." 41 | echo " --tools-source-suffix|-ToolsSourceSuffix The suffix to append to tools-source. Useful for query strings." 42 | echo " -u|--update Update to the latest KoreBuild even if the lock file is present." 43 | echo " --reinstall Reinstall KoreBuild." 44 | echo " --ci Apply CI specific settings and environment variables." 45 | echo "" 46 | echo "Description:" 47 | echo " This function will create a file \$DIR/korebuild-lock.txt. This lock file can be committed to source, but does not have to be." 48 | echo " When the lockfile is not present, KoreBuild will create one using latest available version from \$channel." 49 | 50 | if [[ "${1:-}" != '--no-exit' ]]; then 51 | exit 2 52 | fi 53 | } 54 | 55 | get_korebuild() { 56 | local version 57 | local lock_file="$repo_path/korebuild-lock.txt" 58 | if [ ! -f "$lock_file" ] || [ "$update" = true ]; then 59 | __get_remote_file "$tools_source/korebuild/channels/$channel/latest.txt" "$lock_file" "$tools_source_suffix" 60 | fi 61 | version="$(grep 'version:*' -m 1 "$lock_file")" 62 | if [[ "$version" == '' ]]; then 63 | __error "Failed to parse version from $lock_file. Expected a line that begins with 'version:'" 64 | return 1 65 | fi 66 | version="$(echo "${version#version:}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" 67 | local korebuild_path="$DOTNET_HOME/buildtools/korebuild/$version" 68 | 69 | if [ "$reinstall" = true ] && [ -d "$korebuild_path" ]; then 70 | rm -rf "$korebuild_path" 71 | fi 72 | 73 | { 74 | if [ ! -d "$korebuild_path" ]; then 75 | mkdir -p "$korebuild_path" 76 | local remote_path="$tools_source/korebuild/artifacts/$version/korebuild.$version.zip" 77 | tmpfile="$(mktemp)" 78 | echo -e "${MAGENTA}Downloading KoreBuild ${version}${RESET}" 79 | if __get_remote_file "$remote_path" "$tmpfile" "$tools_source_suffix"; then 80 | unzip -q -d "$korebuild_path" "$tmpfile" 81 | fi 82 | rm "$tmpfile" || true 83 | fi 84 | 85 | source "$korebuild_path/KoreBuild.sh" 86 | } || { 87 | if [ -d "$korebuild_path" ]; then 88 | echo "Cleaning up after failed installation" 89 | rm -rf "$korebuild_path" || true 90 | fi 91 | return 1 92 | } 93 | } 94 | 95 | __error() { 96 | echo -e "${RED}error: $*${RESET}" 1>&2 97 | } 98 | 99 | __warn() { 100 | echo -e "${YELLOW}warning: $*${RESET}" 101 | } 102 | 103 | __machine_has() { 104 | hash "$1" > /dev/null 2>&1 105 | return $? 106 | } 107 | 108 | __get_remote_file() { 109 | local remote_path=$1 110 | local local_path=$2 111 | local remote_path_suffix=$3 112 | 113 | if [[ "$remote_path" != 'http'* ]]; then 114 | cp "$remote_path" "$local_path" 115 | return 0 116 | fi 117 | 118 | local failed=false 119 | if __machine_has wget; then 120 | wget --tries 10 --quiet -O "$local_path" "${remote_path}${remote_path_suffix}" || failed=true 121 | else 122 | failed=true 123 | fi 124 | 125 | if [ "$failed" = true ] && __machine_has curl; then 126 | failed=false 127 | curl --retry 10 -sSL -f --create-dirs -o "$local_path" "${remote_path}${remote_path_suffix}" || failed=true 128 | fi 129 | 130 | if [ "$failed" = true ]; then 131 | __error "Download failed: $remote_path" 1>&2 132 | return 1 133 | fi 134 | } 135 | 136 | # 137 | # main 138 | # 139 | 140 | command="${1:-}" 141 | shift 142 | 143 | while [[ $# -gt 0 ]]; do 144 | case $1 in 145 | -\?|-h|--help) 146 | __usage --no-exit 147 | exit 0 148 | ;; 149 | -c|--channel|-Channel) 150 | shift 151 | channel="${1:-}" 152 | [ -z "$channel" ] && __usage 153 | ;; 154 | --config-file|-ConfigFile) 155 | shift 156 | config_file="${1:-}" 157 | [ -z "$config_file" ] && __usage 158 | if [ ! -f "$config_file" ]; then 159 | __error "Invalid value for --config-file. $config_file does not exist." 160 | exit 1 161 | fi 162 | ;; 163 | -d|--dotnet-home|-DotNetHome) 164 | shift 165 | DOTNET_HOME="${1:-}" 166 | [ -z "$DOTNET_HOME" ] && __usage 167 | ;; 168 | --path|-Path) 169 | shift 170 | repo_path="${1:-}" 171 | [ -z "$repo_path" ] && __usage 172 | ;; 173 | -s|--tools-source|-ToolsSource) 174 | shift 175 | tools_source="${1:-}" 176 | [ -z "$tools_source" ] && __usage 177 | ;; 178 | --tools-source-suffix|-ToolsSourceSuffix) 179 | shift 180 | tools_source_suffix="${1:-}" 181 | [ -z "$tools_source_suffix" ] && __usage 182 | ;; 183 | -u|--update|-Update) 184 | update=true 185 | ;; 186 | --reinstall|-[Rr]einstall) 187 | reinstall=true 188 | ;; 189 | --ci|-[Cc][Ii]) 190 | ci=true 191 | ;; 192 | --verbose|-Verbose) 193 | verbose=true 194 | ;; 195 | --) 196 | shift 197 | break 198 | ;; 199 | *) 200 | break 201 | ;; 202 | esac 203 | shift 204 | done 205 | 206 | if ! __machine_has unzip; then 207 | __error 'Missing required command: unzip' 208 | exit 1 209 | fi 210 | 211 | if ! __machine_has curl && ! __machine_has wget; then 212 | __error 'Missing required command. Either wget or curl is required.' 213 | exit 1 214 | fi 215 | 216 | [ -z "${config_file:-}" ] && config_file="$repo_path/korebuild.json" 217 | if [ -f "$config_file" ]; then 218 | if __machine_has jq ; then 219 | if jq '.' "$config_file" >/dev/null ; then 220 | config_channel="$(jq -r 'select(.channel!=null) | .channel' "$config_file")" 221 | config_tools_source="$(jq -r 'select(.toolsSource!=null) | .toolsSource' "$config_file")" 222 | else 223 | __error "$config_file contains invalid JSON." 224 | exit 1 225 | fi 226 | elif __machine_has python ; then 227 | if python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'))" >/dev/null ; then 228 | config_channel="$(python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['channel'] if 'channel' in obj else '')")" 229 | config_tools_source="$(python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['toolsSource'] if 'toolsSource' in obj else '')")" 230 | else 231 | __error "$config_file contains invalid JSON." 232 | exit 1 233 | fi 234 | elif __machine_has python3 ; then 235 | if python3 -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'))" >/dev/null ; then 236 | config_channel="$(python3 -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['channel'] if 'channel' in obj else '')")" 237 | config_tools_source="$(python3 -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['toolsSource'] if 'toolsSource' in obj else '')")" 238 | else 239 | __error "$config_file contains invalid JSON." 240 | exit 1 241 | fi 242 | else 243 | __error 'Missing required command: jq or python. Could not parse the JSON file.' 244 | exit 1 245 | fi 246 | 247 | [ ! -z "${config_channel:-}" ] && channel="$config_channel" 248 | [ ! -z "${config_tools_source:-}" ] && tools_source="$config_tools_source" 249 | fi 250 | 251 | [ -z "$channel" ] && channel='master' 252 | [ -z "$tools_source" ] && tools_source='https://aspnetcore.blob.core.windows.net/buildtools' 253 | 254 | get_korebuild 255 | set_korebuildsettings "$tools_source" "$DOTNET_HOME" "$repo_path" "$config_file" "$ci" 256 | invoke_korebuild_command "$command" "$@" 257 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/DiagnosticListenerExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using Microsoft.Extensions.DiagnosticAdapter; 5 | 6 | namespace System.Diagnostics 7 | { 8 | public static class DiagnosticListenerExtensions 9 | { 10 | public static IDisposable SubscribeWithAdapter(this DiagnosticListener diagnostic, object target) 11 | { 12 | var adapter = new DiagnosticSourceAdapter(target); 13 | return diagnostic.Subscribe(adapter, (Predicate)adapter.IsEnabled); 14 | } 15 | 16 | public static IDisposable SubscribeWithAdapter( 17 | this DiagnosticListener diagnostic, 18 | object target, 19 | Func isEnabled) 20 | { 21 | var adapter = new DiagnosticSourceAdapter(target, isEnabled); 22 | return diagnostic.Subscribe(adapter, (Predicate)adapter.IsEnabled); 23 | } 24 | 25 | public static IDisposable SubscribeWithAdapter( 26 | this DiagnosticListener diagnostic, 27 | object target, 28 | Func isEnabled) 29 | { 30 | var adapter = new DiagnosticSourceAdapter(target, isEnabled); 31 | return diagnostic.Subscribe(adapter, (Predicate)adapter.IsEnabled); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/DiagnosticNameAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | 6 | namespace Microsoft.Extensions.DiagnosticAdapter 7 | { 8 | public class DiagnosticNameAttribute : Attribute 9 | { 10 | public DiagnosticNameAttribute(string name) 11 | { 12 | Name = name; 13 | } 14 | 15 | public string Name { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/DiagnosticSourceAdapter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Collections.Concurrent; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | using System.Reflection; 9 | using Microsoft.Extensions.DiagnosticAdapter.Internal; 10 | 11 | namespace Microsoft.Extensions.DiagnosticAdapter 12 | { 13 | public class DiagnosticSourceAdapter : IObserver> 14 | { 15 | private readonly Listener _listener; 16 | private readonly IDiagnosticSourceMethodAdapter _methodAdapter; 17 | 18 | public DiagnosticSourceAdapter(object target) 19 | : this(target, (Func)null, new ProxyDiagnosticSourceMethodAdapter()) 20 | { 21 | } 22 | 23 | public DiagnosticSourceAdapter(object target, Func isEnabled) 24 | : this(target, isEnabled, methodAdapter: new ProxyDiagnosticSourceMethodAdapter()) 25 | { 26 | } 27 | 28 | public DiagnosticSourceAdapter(object target, Func isEnabled) 29 | : this(target, isEnabled, methodAdapter: new ProxyDiagnosticSourceMethodAdapter()) 30 | { 31 | } 32 | 33 | public DiagnosticSourceAdapter( 34 | object target, 35 | Func isEnabled, 36 | IDiagnosticSourceMethodAdapter methodAdapter) 37 | : this(target, isEnabled: (isEnabled == null) ? (Func)null : (a, b, c) => isEnabled(a), methodAdapter: methodAdapter) 38 | { 39 | } 40 | 41 | public DiagnosticSourceAdapter( 42 | object target, 43 | Func isEnabled, 44 | IDiagnosticSourceMethodAdapter methodAdapter) 45 | { 46 | _methodAdapter = methodAdapter; 47 | _listener = EnlistTarget(target, isEnabled); 48 | } 49 | 50 | private static Listener EnlistTarget(object target, Func isEnabled) 51 | { 52 | var listener = new Listener(target, isEnabled); 53 | 54 | var typeInfo = target.GetType().GetTypeInfo(); 55 | var methodInfos = typeInfo.DeclaredMethods; 56 | foreach (var methodInfo in methodInfos) 57 | { 58 | var diagnosticNameAttribute = methodInfo.GetCustomAttribute(); 59 | if (diagnosticNameAttribute != null) 60 | { 61 | var subscription = new Subscription(methodInfo); 62 | listener.Subscriptions.Add(diagnosticNameAttribute.Name, subscription); 63 | } 64 | } 65 | 66 | return listener; 67 | } 68 | 69 | public bool IsEnabled(string diagnosticName) 70 | { 71 | return IsEnabled(diagnosticName, null); 72 | } 73 | 74 | public bool IsEnabled(string diagnosticName, object arg1, object arg2 = null) 75 | { 76 | if (_listener.Subscriptions.Count == 0) 77 | { 78 | return false; 79 | } 80 | 81 | return 82 | _listener.Subscriptions.ContainsKey(diagnosticName) && 83 | (_listener.IsEnabled == null || _listener.IsEnabled(diagnosticName, arg1, arg2)); 84 | } 85 | 86 | public void Write(string diagnosticName, object parameters) 87 | { 88 | if (parameters == null) 89 | { 90 | return; 91 | } 92 | 93 | Subscription subscription; 94 | if (!_listener.Subscriptions.TryGetValue(diagnosticName, out subscription)) 95 | { 96 | return; 97 | } 98 | 99 | var succeeded = false; 100 | foreach (var adapter in subscription.Adapters) 101 | { 102 | if (adapter(_listener.Target, parameters)) 103 | { 104 | succeeded = true; 105 | break; 106 | } 107 | } 108 | 109 | if (!succeeded) 110 | { 111 | var newAdapter = _methodAdapter.Adapt(subscription.MethodInfo, parameters.GetType()); 112 | try 113 | { 114 | succeeded = newAdapter(_listener.Target, parameters); 115 | } 116 | catch (InvalidProxyOperationException ex) 117 | { 118 | throw new InvalidOperationException( 119 | Resources.FormatConverter_UnableToGenerateProxy(subscription.MethodInfo.Name), 120 | ex); 121 | } 122 | Debug.Assert(succeeded); 123 | 124 | subscription.Adapters.Add(newAdapter); 125 | } 126 | } 127 | 128 | void IObserver>.OnNext(KeyValuePair value) 129 | { 130 | Write(value.Key, value.Value); 131 | } 132 | 133 | void IObserver>.OnError(Exception error) 134 | { 135 | // Do nothing 136 | } 137 | 138 | void IObserver>.OnCompleted() 139 | { 140 | // Do nothing 141 | } 142 | 143 | private class Listener 144 | { 145 | public Listener(object target, Func isEnabled) 146 | { 147 | Target = target; 148 | IsEnabled = isEnabled; 149 | 150 | Subscriptions = new Dictionary(StringComparer.Ordinal); 151 | } 152 | 153 | public Func IsEnabled { get; } 154 | 155 | public object Target { get; } 156 | 157 | public Dictionary Subscriptions { get; } 158 | } 159 | 160 | private class Subscription 161 | { 162 | public Subscription(MethodInfo methodInfo) 163 | { 164 | MethodInfo = methodInfo; 165 | 166 | Adapters = new ConcurrentBag>(); 167 | } 168 | 169 | public ConcurrentBag> Adapters { get; } 170 | 171 | public MethodInfo MethodInfo { get; } 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/IDiagnosticSourceMethodAdapter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Reflection; 6 | 7 | namespace Microsoft.Extensions.DiagnosticAdapter 8 | { 9 | public interface IDiagnosticSourceMethodAdapter 10 | { 11 | Func Adapt(MethodInfo method, Type inputType); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/Infrastructure/IProxy.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | namespace Microsoft.Extensions.DiagnosticAdapter.Infrastructure 5 | { 6 | /// 7 | /// An interface for unwrappable proxy objects. 8 | /// 9 | public interface IProxy 10 | { 11 | /// 12 | /// Unwraps the underlying object and performs a cast to . 13 | /// 14 | /// The type of the underlying object. 15 | /// The underlying object. 16 | T Upwrap(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/Infrastructure/IProxyFactory.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | namespace Microsoft.Extensions.DiagnosticAdapter.Infrastructure 5 | { 6 | /// 7 | /// A factory for runtime creation of proxy objects. 8 | /// 9 | public interface IProxyFactory 10 | { 11 | /// 12 | /// Creates a proxy object that is assignable to type 13 | /// 14 | /// The type of the proxy to create. 15 | /// The object to wrap in a proxy. 16 | /// A proxy object, or if a proxy is not needed. 17 | TProxy CreateProxy(object obj); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/Internal/InvalidProxyOperationException.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | 6 | namespace Microsoft.Extensions.DiagnosticAdapter.Internal 7 | { 8 | public class InvalidProxyOperationException : InvalidOperationException 9 | { 10 | public InvalidProxyOperationException(string message) : base(message) 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/Internal/ProxyAssembly.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | #if NETCOREAPP2_0 || NET461 5 | using System; 6 | using System.Reflection; 7 | using System.Reflection.Emit; 8 | 9 | namespace Microsoft.Extensions.DiagnosticAdapter.Internal 10 | { 11 | // To diagnose issues with the emitted code, define GENERATE_ASSEMBLIES and call Save() in the 12 | // immediate window after defining the problematic method. 13 | // 14 | // The use peverify or another tool to look at the generated code. 15 | public static class ProxyAssembly 16 | { 17 | private static volatile int Counter = 0; 18 | 19 | private static AssemblyBuilder AssemblyBuilder; 20 | private static ModuleBuilder ModuleBuilder; 21 | 22 | static ProxyAssembly() 23 | { 24 | var assemblyName = new AssemblyName("Microsoft.Extensions.DiagnosticAdapter.ProxyAssembly"); 25 | #if GENERATE_ASSEMBLIES 26 | var access = AssemblyBuilderAccess.RunAndSave; 27 | #else 28 | var access = AssemblyBuilderAccess.Run; 29 | #endif 30 | 31 | AssemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, access); 32 | ModuleBuilder = AssemblyBuilder.DefineDynamicModule("Microsoft.Extensions.DiagnosticAdapter.ProxyAssembly.dll"); 33 | } 34 | 35 | public static TypeBuilder DefineType( 36 | string name, 37 | TypeAttributes attributes, 38 | Type baseType, 39 | Type[] interfaces) 40 | { 41 | name = name + "_" + Counter++; 42 | return ModuleBuilder.DefineType(name, attributes, baseType, interfaces); 43 | } 44 | 45 | #if GENERATE_ASSEMBLIES 46 | public static string Save() 47 | { 48 | AssemblyBuilder.Save(ModuleBuilder.ScopeName); 49 | return ModuleBuilder.FullyQualifiedName; 50 | } 51 | #endif 52 | } 53 | } 54 | #elif NETSTANDARD2_0 55 | #else 56 | #error Target frameworks should be updated 57 | #endif 58 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/Internal/ProxyBase.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using Microsoft.Extensions.DiagnosticAdapter.Infrastructure; 6 | 7 | namespace Microsoft.Extensions.DiagnosticAdapter.Internal 8 | { 9 | public abstract class ProxyBase : IProxy 10 | { 11 | public readonly Type WrappedType; 12 | 13 | protected ProxyBase(Type wrappedType) 14 | { 15 | WrappedType = wrappedType; 16 | } 17 | 18 | // Used by reflection, don't rename. 19 | public abstract object UnderlyingInstanceAsObject 20 | { 21 | get; 22 | } 23 | 24 | public T Upwrap() 25 | { 26 | return (T)UnderlyingInstanceAsObject; 27 | } 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/Internal/ProxyBaseOfT.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | 6 | namespace Microsoft.Extensions.DiagnosticAdapter.Internal 7 | { 8 | public class ProxyBase : ProxyBase where T : class 9 | { 10 | // Used by reflection, don't rename. 11 | public readonly T Instance; 12 | 13 | public ProxyBase(T instance) 14 | : base(typeof(T)) 15 | { 16 | if (instance == null) 17 | { 18 | throw new ArgumentNullException(nameof(instance)); 19 | } 20 | 21 | Instance = instance; 22 | } 23 | 24 | public T UnderlyingInstance 25 | { 26 | get 27 | { 28 | return Instance; 29 | } 30 | } 31 | 32 | public override object UnderlyingInstanceAsObject 33 | { 34 | get 35 | { 36 | return Instance; 37 | } 38 | } 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/Internal/ProxyEnumerable.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | 8 | namespace Microsoft.Extensions.DiagnosticAdapter.Internal 9 | { 10 | public class ProxyEnumerable : IEnumerable 11 | { 12 | private readonly IEnumerable _source; 13 | private readonly Type _proxyType; 14 | 15 | public ProxyEnumerable(IEnumerable source, Type proxyType) 16 | { 17 | _source = source; 18 | _proxyType = proxyType; 19 | } 20 | 21 | public IEnumerator GetEnumerator() 22 | { 23 | return new ProxyEnumerator(_source.GetEnumerator(), _proxyType); 24 | } 25 | 26 | IEnumerator IEnumerable.GetEnumerator() 27 | { 28 | return GetEnumerator(); 29 | } 30 | 31 | public class ProxyEnumerator : IEnumerator 32 | { 33 | private readonly IEnumerator _source; 34 | private readonly Type _proxyType; 35 | 36 | public ProxyEnumerator(IEnumerator source, Type proxyType) 37 | { 38 | _source = source; 39 | 40 | _proxyType = proxyType; 41 | } 42 | 43 | public TTargetElement Current 44 | { 45 | get 46 | { 47 | var element = _source.Current; 48 | return MakeProxy(element); 49 | } 50 | } 51 | 52 | object IEnumerator.Current 53 | { 54 | get 55 | { 56 | return Current; 57 | } 58 | } 59 | 60 | public void Dispose() 61 | { 62 | _source.Dispose(); 63 | } 64 | 65 | public bool MoveNext() 66 | { 67 | return _source.MoveNext(); 68 | } 69 | 70 | public void Reset() 71 | { 72 | _source.Reset(); 73 | } 74 | 75 | private TTargetElement MakeProxy(TSourceElement element) 76 | { 77 | if (_proxyType == null) 78 | { 79 | return (TTargetElement)(object)element; 80 | } 81 | else if (element == null) 82 | { 83 | return default(TTargetElement); 84 | } 85 | else 86 | { 87 | return (TTargetElement)Activator.CreateInstance( 88 | _proxyType, 89 | new object[] { element }); 90 | } 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/Internal/ProxyFactory.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Reflection; 6 | using Microsoft.Extensions.DiagnosticAdapter.Infrastructure; 7 | 8 | namespace Microsoft.Extensions.DiagnosticAdapter.Internal 9 | { 10 | public class ProxyFactory : IProxyFactory 11 | { 12 | private readonly ProxyTypeCache _cache = new ProxyTypeCache(); 13 | 14 | public TProxy CreateProxy(object obj) 15 | { 16 | if (obj == null) 17 | { 18 | return default(TProxy); 19 | } 20 | else if (typeof(TProxy).GetTypeInfo().IsAssignableFrom(obj.GetType().GetTypeInfo())) 21 | { 22 | return (TProxy)obj; 23 | } 24 | 25 | #if NETCOREAPP2_0 || NET461 26 | var type = ProxyTypeEmitter.GetProxyType(_cache, typeof(TProxy), obj.GetType()); 27 | return (TProxy)Activator.CreateInstance(type, obj); 28 | #elif NETSTANDARD2_0 29 | throw new PlatformNotSupportedException("This platform does not support creating proxy types and methods."); 30 | #else 31 | #error Target frameworks should be updated 32 | #endif 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/Internal/ProxyList.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | 8 | namespace Microsoft.Extensions.DiagnosticAdapter.Internal 9 | { 10 | public class ProxyList : IReadOnlyList 11 | { 12 | private readonly IList _source; 13 | private readonly Type _proxyType; 14 | 15 | public ProxyList(IList source) 16 | : this(source, null) 17 | { 18 | } 19 | 20 | protected ProxyList(IList source, Type proxyType) 21 | { 22 | if (source == null) 23 | { 24 | throw new ArgumentNullException(nameof(source)); 25 | } 26 | 27 | _source = source; 28 | _proxyType = proxyType; 29 | } 30 | 31 | public TTargetElement this[int index] 32 | { 33 | get 34 | { 35 | var element = _source[index]; 36 | return MakeProxy(element); 37 | } 38 | } 39 | 40 | public int Count 41 | { 42 | get 43 | { 44 | return _source.Count; 45 | } 46 | } 47 | 48 | public IEnumerator GetEnumerator() 49 | { 50 | return new ProxyEnumerable.ProxyEnumerator(_source.GetEnumerator(), _proxyType); 51 | } 52 | 53 | IEnumerator IEnumerable.GetEnumerator() 54 | { 55 | return GetEnumerator(); 56 | } 57 | 58 | private TTargetElement MakeProxy(TSourceElement element) 59 | { 60 | if (_proxyType == null) 61 | { 62 | return (TTargetElement)(object)element; 63 | } 64 | else if (element == null) 65 | { 66 | return default(TTargetElement); 67 | } 68 | else 69 | { 70 | return (TTargetElement)Activator.CreateInstance( 71 | _proxyType, 72 | new object[] { element }); 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/Internal/ProxyMethodEmitter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | #if NETCOREAPP2_0 || NET461 5 | using System; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Reflection.Emit; 9 | using Microsoft.Extensions.DiagnosticAdapter.Infrastructure; 10 | 11 | namespace Microsoft.Extensions.DiagnosticAdapter.Internal 12 | { 13 | // To diagnose issues with the emitted code, define GENERATE_ASSEMBLIES and call Save() in the 14 | // immediate window after defining the problematic method. 15 | // 16 | // The use peverify or another tool to look at the generated code. 17 | public static class ProxyMethodEmitter 18 | { 19 | #if GENERATE_ASSEMBLIES 20 | private static volatile int Counter = 0; 21 | 22 | private static readonly AssemblyBuilder AssemblyBuilder; 23 | private static readonly ModuleBuilder ModuleBuilder; 24 | 25 | static ProxyMethodEmitter() 26 | { 27 | var name = new AssemblyName("Microsoft.Extensions.DiagnosticAdapter.ProxyMethodAssembly"); 28 | AssemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave); 29 | ModuleBuilder = AssemblyBuilder.DefineDynamicModule(name.Name + ".dll"); 30 | } 31 | #endif 32 | 33 | private static readonly MethodInfo ProxyFactoryGenericMethod = 34 | typeof(IProxyFactory).GetTypeInfo().GetDeclaredMethod(nameof(IProxyFactory.CreateProxy)); 35 | 36 | public static Func CreateProxyMethod(MethodInfo method, Type inputType) 37 | { 38 | var name = string.Format("Proxy_Method_From_{0}_To_{1}", inputType.Name, method); 39 | 40 | // Define the method-adapter as Func<'listener', 'data', 'proxy factory', bool>, we'll do casts inside. 41 | var dynamicMethod = new DynamicMethod( 42 | name, 43 | returnType: typeof(bool), 44 | parameterTypes: new Type[] { typeof(object), typeof(object), typeof(IProxyFactory) }, 45 | restrictedSkipVisibility: true); 46 | 47 | var parameters = method.GetParameters(); 48 | var mappings = GetPropertyToParameterMappings(inputType, parameters); 49 | EmitMethod(dynamicMethod.GetILGenerator(), inputType, mappings, method, parameters); 50 | 51 | #if GENERATE_ASSEMBLIES 52 | AddToAssembly(inputType, mappings, method, parameters); 53 | #endif 54 | var @delegate = dynamicMethod.CreateDelegate(typeof(Func)); 55 | return (Func)@delegate; 56 | } 57 | 58 | private static PropertyInfo[] GetPropertyToParameterMappings(Type inputType, ParameterInfo[] parameters) 59 | { 60 | var properties = inputType.GetTypeInfo().DeclaredProperties.ToArray(); 61 | var mappings = new PropertyInfo[parameters.Length]; 62 | 63 | for (var i = 0; i < parameters.Length; i++) 64 | { 65 | var parameter = parameters[i]; 66 | for (var j = 0; j < properties.Length; j++) 67 | { 68 | var property = properties[j]; 69 | if (string.Equals(property.Name, parameter.Name, StringComparison.OrdinalIgnoreCase)) 70 | { 71 | if (mappings[i] == null) 72 | { 73 | mappings[i] = property; 74 | } 75 | else 76 | { 77 | // If the mapping is not-null, we've already found a property matching this parameter name. 78 | // This is an ambiguity that must be caused by properties with different casings. 79 | throw new InvalidOperationException( 80 | Resources.FormatConverter_TypeMustNotHavePropertiesThatVaryByCase( 81 | inputType.FullName, 82 | property.Name.ToLowerInvariant())); // ToLower for testability 83 | } 84 | } 85 | } 86 | } 87 | 88 | return mappings; 89 | } 90 | 91 | private static void EmitMethod( 92 | ILGenerator il, 93 | Type inputType, 94 | PropertyInfo[] mappings, 95 | MethodInfo method, 96 | ParameterInfo[] parameters) 97 | { 98 | // Define a local for each method parameters. This is needed when the parameter is 99 | // a value type, but we'll do it for all for simplicity. 100 | for (var i = 0; i < parameters.Length; i++) 101 | { 102 | il.DeclareLocal(parameters[i].ParameterType); 103 | } 104 | 105 | var endLabel = il.DefineLabel(); // Marks the 'return' 106 | var happyPathLabel = il.DefineLabel(); // Marks the 'happy path' - wherein we dispatch to the listener. 107 | 108 | //// Check if the input value is of the type we can handle. 109 | il.Emit(OpCodes.Ldarg_1); // The event-data 110 | il.Emit(OpCodes.Isinst, inputType); 111 | il.Emit(OpCodes.Brtrue, happyPathLabel); 112 | 113 | // We get here if the event-data doesn't match the type we can handle. 114 | // 115 | // Push 'false' onto the stack and return. 116 | il.Emit(OpCodes.Ldc_I4_0); 117 | il.Emit(OpCodes.Br, endLabel); 118 | 119 | // We get here if the event-data matches the type we can handle. 120 | il.MarkLabel(happyPathLabel); 121 | 122 | // Initialize locals to hold each parameter value. 123 | for (var i = 0; i < parameters.Length; i++) 124 | { 125 | var parameter = parameters[i]; 126 | if (parameter.ParameterType.GetTypeInfo().IsValueType) 127 | { 128 | // default-initialize each value type. 129 | il.Emit(OpCodes.Ldloca_S, i); 130 | il.Emit(OpCodes.Initobj, parameter.ParameterType); 131 | } 132 | else 133 | { 134 | // null-initialize each reference type. 135 | il.Emit(OpCodes.Ldnull); 136 | il.Emit(OpCodes.Stloc_S, i); 137 | } 138 | } 139 | 140 | // Evaluate all properties and store them in the locals. 141 | for (var i = 0; i < parameters.Length; i++) 142 | { 143 | var parameter = parameters[i]; 144 | var mapping = mappings[i]; 145 | if (mapping != null) 146 | { 147 | // No proxy required, just load the value. 148 | if (parameter.ParameterType.GetTypeInfo().IsAssignableFrom(mapping.PropertyType.GetTypeInfo())) 149 | { 150 | il.Emit(OpCodes.Ldarg_1); // The event-data 151 | il.Emit(OpCodes.Castclass, inputType); 152 | il.Emit(OpCodes.Callvirt, mapping.GetMethod); 153 | } 154 | else 155 | { 156 | il.Emit(OpCodes.Ldarg_2); // The proxy-factory 157 | 158 | il.Emit(OpCodes.Ldarg_1); // The event-data 159 | il.Emit(OpCodes.Castclass, inputType); 160 | il.Emit(OpCodes.Callvirt, mapping.GetMethod); 161 | 162 | // If we have a value type box it to the target type. 163 | if (mapping.PropertyType.GetTypeInfo().IsValueType) 164 | { 165 | il.Emit(OpCodes.Box, mapping.PropertyType); 166 | } 167 | 168 | var factoryMethod = ProxyFactoryGenericMethod.MakeGenericMethod(parameter.ParameterType); 169 | il.Emit(OpCodes.Callvirt, factoryMethod); 170 | } 171 | 172 | il.Emit(OpCodes.Stloc_S, i); 173 | } 174 | } 175 | 176 | // Set up the call to the listener. 177 | // 178 | // Push the listener object, and then all of the argument values. 179 | il.Emit(OpCodes.Ldarg_0); // The listener 180 | il.Emit(OpCodes.Castclass, method.DeclaringType); 181 | 182 | // Push arguments onto the stack 183 | for (var i = 0; i < parameters.Length; i++) 184 | { 185 | il.Emit(OpCodes.Ldloc_S, i); 186 | } 187 | 188 | // Call the method in the listener 189 | il.Emit(OpCodes.Callvirt, method); 190 | 191 | // Success! 192 | // 193 | // Push 'true' onto the stack and return. 194 | il.Emit(OpCodes.Ldc_I4_1); 195 | il.Emit(OpCodes.Br, endLabel); 196 | 197 | // We expect that whoever branched to here put a boolean value (I4_0, I4_1) on top of the stack. 198 | il.MarkLabel(endLabel); 199 | il.Emit(OpCodes.Ret); 200 | } 201 | 202 | #if GENERATE_ASSEMBLIES 203 | private static void AddToAssembly( 204 | Type inputType, 205 | PropertyInfo[] mappings, 206 | MethodInfo method, 207 | ParameterInfo[] parameters) 208 | { 209 | var typeName = $"Type_For_Proxy_Method_From_{inputType.Name}_To_{method}_{Counter++}"; 210 | 211 | var typeBuilder = ModuleBuilder.DefineType(typeName, TypeAttributes.Abstract); 212 | var methodBuilder = typeBuilder.DefineMethod( 213 | "Proxy", 214 | MethodAttributes.Public, 215 | CallingConventions.Standard, 216 | returnType: typeof(bool), 217 | parameterTypes: new Type[] { typeof(object), typeof(object), typeof(IProxyFactory) }); 218 | 219 | var il = methodBuilder.GetILGenerator(); 220 | EmitMethod(il, inputType, mappings, method, parameters); 221 | 222 | typeBuilder.CreateType(); 223 | } 224 | 225 | public static string Save() 226 | { 227 | ProxyAssembly.Save(); 228 | 229 | AssemblyBuilder.Save(ModuleBuilder.ScopeName); 230 | return ModuleBuilder.FullyQualifiedName; 231 | } 232 | #endif 233 | } 234 | } 235 | #elif NETSTANDARD2_0 236 | #else 237 | #error Target frameworks should be updated 238 | #endif 239 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/Internal/ProxyTypeCache.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Collections.Concurrent; 6 | 7 | namespace Microsoft.Extensions.DiagnosticAdapter.Internal 8 | { 9 | public class ProxyTypeCache : ConcurrentDictionary, ProxyTypeCacheResult> 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/Internal/ProxyTypeCacheResult.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Reflection; 6 | 7 | namespace Microsoft.Extensions.DiagnosticAdapter.Internal 8 | { 9 | public class ProxyTypeCacheResult 10 | { 11 | public static ProxyTypeCacheResult FromError(Tuple key, string error) 12 | { 13 | return new ProxyTypeCacheResult() 14 | { 15 | Key = key, 16 | Error = error, 17 | }; 18 | } 19 | 20 | public static ProxyTypeCacheResult FromType( 21 | Tuple key, 22 | Type type, 23 | ConstructorInfo constructor) 24 | { 25 | return new ProxyTypeCacheResult() 26 | { 27 | Key = key, 28 | Type = type, 29 | Constructor = constructor, 30 | }; 31 | } 32 | 33 | public ConstructorInfo Constructor { get; private set; } 34 | 35 | public string Error { get; private set; } 36 | 37 | public bool IsError => Error != null; 38 | 39 | public Tuple Key { get; private set; } 40 | 41 | public Type Type { get; private set; } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/Internal/ProxyTypeEmitter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | #if NETCOREAPP2_0 || NET461 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | using System.Linq; 9 | using System.Reflection; 10 | using System.Reflection.Emit; 11 | 12 | namespace Microsoft.Extensions.DiagnosticAdapter.Internal 13 | { 14 | public static class ProxyTypeEmitter 15 | { 16 | private static readonly Type[] EmptyTypes = new Type[0]; 17 | private static object _lock = new object(); 18 | 19 | public static Type GetProxyType(ProxyTypeCache cache, Type targetType, Type sourceType) 20 | { 21 | if (targetType.GetTypeInfo().IsAssignableFrom(sourceType.GetTypeInfo())) 22 | { 23 | return null; 24 | } 25 | 26 | var key = new Tuple(sourceType, targetType); 27 | 28 | ProxyTypeCacheResult result; 29 | if (!cache.TryGetValue(key, out result)) 30 | { 31 | var context = new ProxyBuilderContext(cache, targetType, sourceType); 32 | 33 | // Check that all required types are proxy-able - this will create the TypeBuilder, Constructor, 34 | // and property mappings. 35 | // 36 | // We need to create the TypeBuilder and Constructor up front to deal with cycles that can occur 37 | // when generating the proxy properties. 38 | if (!VerifyProxySupport(context, context.Key)) 39 | { 40 | var error = cache[key]; 41 | Debug.Assert(error != null && error.IsError); 42 | throw new InvalidProxyOperationException(error.Error); 43 | } 44 | 45 | Debug.Assert(context.Visited.ContainsKey(context.Key)); 46 | 47 | // Now that we've generated all of the constructors for the proxies, we can generate the properties. 48 | foreach (var verificationResult in context.Visited) 49 | { 50 | if (verificationResult.Value.Mappings != null) 51 | { 52 | AddProperties( 53 | context, 54 | verificationResult.Value.TypeBuilder, 55 | verificationResult.Value.Mappings); 56 | } 57 | } 58 | 59 | // Now generate the type 60 | foreach (var verificationResult in context.Visited) 61 | { 62 | if (verificationResult.Value.TypeBuilder != null) 63 | { 64 | verificationResult.Value.Type = verificationResult.Value.TypeBuilder.CreateTypeInfo().AsType(); 65 | } 66 | } 67 | 68 | // We only want to publish the results after all of the proxies are totally generated. 69 | foreach (var verificationResult in context.Visited) 70 | { 71 | cache.TryAdd(verificationResult.Key, ProxyTypeCacheResult.FromType( 72 | verificationResult.Key, 73 | verificationResult.Value.Type, 74 | verificationResult.Value.Constructor)); 75 | } 76 | 77 | return context.Visited[context.Key].Type; 78 | } 79 | else if (result.IsError) 80 | { 81 | throw new InvalidOperationException(result.Error); 82 | } 83 | else if (result.Type == null) 84 | { 85 | // This is an identity conversion 86 | return null; 87 | } 88 | else 89 | { 90 | return result.Type; 91 | } 92 | } 93 | 94 | internal static bool VerifyProxySupport(ProxyBuilderContext context, Tuple key) 95 | { 96 | var sourceType = key.Item1; 97 | var targetType = key.Item2; 98 | 99 | if (context.Visited.ContainsKey(key)) 100 | { 101 | // We've already seen this combination and so far so good. 102 | return true; 103 | } 104 | 105 | ProxyTypeCacheResult cacheResult; 106 | var verificationResult = new VerificationResult(); 107 | if (context.Cache.TryGetValue(key, out cacheResult)) 108 | { 109 | // There may be a possible race condition, which adds the type we are generating to the cache 110 | // before we verify it. This ensures that the result is stored in context.Visited in that scenario. 111 | verificationResult.Constructor = cacheResult.Constructor; 112 | verificationResult.Type = cacheResult.Type; 113 | context.Visited.Add(key, verificationResult); 114 | 115 | // If we get here we've got a published conversion or error, so we can stop searching. 116 | return !cacheResult.IsError; 117 | } 118 | 119 | if (targetType == sourceType || targetType.GetTypeInfo().IsAssignableFrom(sourceType.GetTypeInfo())) 120 | { 121 | // If we find a trivial conversion, then that will work. 122 | return true; 123 | } 124 | 125 | if (!targetType.GetTypeInfo().IsInterface) 126 | { 127 | var message = Resources.FormatConverter_TypeMustBeInterface(targetType.FullName, sourceType.FullName); 128 | context.Cache[key] = ProxyTypeCacheResult.FromError(key, message); 129 | 130 | return false; 131 | } 132 | 133 | // This is a combination we haven't seen before, and it *might* support proxy generation, so let's 134 | // start trying. 135 | context.Visited.Add(key, verificationResult); 136 | 137 | // We support conversions from IList -> IReadOnlyList and IReadOnlyList -> IReadOnlyList 138 | if (targetType.GetTypeInfo().IsGenericType && 139 | targetType.GetTypeInfo().GetGenericTypeDefinition() == typeof(IReadOnlyList<>)) 140 | { 141 | var sourceInterfaceType = GetGenericImplementation(sourceType, typeof(IList<>)); 142 | if (sourceInterfaceType != null) 143 | { 144 | var targetElementType = targetType.GetTypeInfo().GenericTypeArguments[0]; 145 | var sourceElementType = sourceInterfaceType.GetTypeInfo().GenericTypeArguments[0]; 146 | 147 | var elementKey = new Tuple(sourceElementType, targetElementType); 148 | if (!VerifyProxySupport(context, elementKey)) 149 | { 150 | var error = context.Cache[elementKey]; 151 | Debug.Assert(error != null && error.IsError); 152 | context.Cache[key] = error; 153 | return false; 154 | } 155 | 156 | VerificationResult elementResult; 157 | context.Visited.TryGetValue(elementKey, out elementResult); 158 | 159 | var proxyType = elementResult?.Type ?? (Type)elementResult?.TypeBuilder; 160 | if (proxyType == null) 161 | { 162 | // No proxy needed for elements. 163 | verificationResult.Type = typeof(ProxyList<,>).MakeGenericType(elementKey.Item1, elementKey.Item2); 164 | verificationResult.Constructor = verificationResult.Type.GetTypeInfo().DeclaredConstructors.First(); 165 | } 166 | else 167 | { 168 | lock (_lock) 169 | { 170 | // We need to proxy each of the elements. Let's generate a type. 171 | GenerateProxyTypeForList(elementKey.Item1, elementKey.Item2, proxyType, verificationResult); 172 | } 173 | } 174 | 175 | return true; 176 | } 177 | } 178 | 179 | // This doesn't match any of our interface conversions, so we'll codegen a proxy. 180 | var propertyMappings = new List>(); 181 | 182 | var sourceProperties = sourceType.GetRuntimeProperties(); 183 | var targetProperties = targetType 184 | .GetTypeInfo() 185 | .ImplementedInterfaces 186 | .SelectMany(i => i.GetRuntimeProperties()) 187 | .Concat(targetType.GetRuntimeProperties()); 188 | foreach (var targetProperty in targetProperties) 189 | { 190 | if (!targetProperty.CanRead) 191 | { 192 | var message = Resources.FormatConverter_PropertyMustHaveGetter( 193 | targetProperty.Name, 194 | targetType.FullName); 195 | context.Cache[key] = ProxyTypeCacheResult.FromError(key, message); 196 | 197 | return false; 198 | } 199 | 200 | if (targetProperty.CanWrite) 201 | { 202 | var message = Resources.FormatConverter_PropertyMustNotHaveSetter( 203 | targetProperty.Name, 204 | targetType.FullName); 205 | context.Cache[key] = ProxyTypeCacheResult.FromError(key, message); 206 | 207 | return false; 208 | } 209 | 210 | if (targetProperty.GetIndexParameters()?.Length > 0) 211 | { 212 | var message = Resources.FormatConverter_PropertyMustNotHaveIndexParameters( 213 | targetProperty.Name, 214 | targetType.FullName); 215 | context.Cache[key] = ProxyTypeCacheResult.FromError(key, message); 216 | 217 | return false; 218 | } 219 | 220 | // To allow for flexible versioning, we want to allow missing properties in the source. 221 | // 222 | // For now we'll just store null, and later generate a stub getter that returns default(T). 223 | var sourceProperty = sourceProperties.Where(p => p.Name == targetProperty.Name).FirstOrDefault(); 224 | if (sourceProperty != null && 225 | sourceProperty.CanRead && 226 | sourceProperty.GetMethod?.IsPublic == true) 227 | { 228 | var propertyKey = new Tuple(sourceProperty.PropertyType, targetProperty.PropertyType); 229 | if (!VerifyProxySupport(context, propertyKey)) 230 | { 231 | // There's an error here, so bubble it up and cache it. 232 | var error = context.Cache[propertyKey]; 233 | Debug.Assert(error != null && error.IsError); 234 | 235 | context.Cache[key] = ProxyTypeCacheResult.FromError(key, error.Error); 236 | return false; 237 | } 238 | 239 | propertyMappings.Add(new KeyValuePair(targetProperty, sourceProperty)); 240 | } 241 | else 242 | { 243 | propertyMappings.Add(new KeyValuePair(targetProperty, null)); 244 | } 245 | } 246 | 247 | verificationResult.Mappings = propertyMappings; 248 | lock (_lock) 249 | { 250 | GenerateProxyTypeFromProperties(sourceType, targetType, verificationResult); 251 | } 252 | 253 | return true; 254 | } 255 | 256 | private static void GenerateProxyTypeForList( 257 | Type sourceElementType, 258 | Type targetElementType, 259 | Type proxyType, 260 | VerificationResult verificationResult) 261 | { 262 | var baseType = typeof(ProxyList<,>).MakeGenericType(sourceElementType, targetElementType); 263 | var baseConstructor = baseType.GetTypeInfo().DeclaredConstructors.FirstOrDefault(ctor => !ctor.IsPublic && !ctor.IsStatic); 264 | 265 | var typeBuilder = ProxyAssembly.DefineType( 266 | string.Format("Proxy_From_IList<{0}>_To_IReadOnlyList<{1}>", sourceElementType.Name, targetElementType.Name), 267 | TypeAttributes.Class, 268 | baseType, 269 | new Type[] { typeof(IReadOnlyList<>).MakeGenericType(targetElementType) }); 270 | 271 | var constructorBuilder = typeBuilder.DefineConstructor( 272 | MethodAttributes.Public, 273 | CallingConventions.Standard, 274 | new Type[] { typeof(IList<>).MakeGenericType(sourceElementType) }); 275 | 276 | var il = constructorBuilder.GetILGenerator(); 277 | il.Emit(OpCodes.Ldarg_0); 278 | il.Emit(OpCodes.Ldarg_1); 279 | 280 | // LdToken loads a RuntimeTypeHandle, while the constructor takes a Type, so we convert. 281 | // This is the same strategy the compiler uses when constructing this class. 282 | il.Emit(OpCodes.Ldtoken, proxyType); 283 | var getTypeFromHandle = typeof(Type).GetRuntimeMethods() 284 | .First(m => string.Equals(m.Name, nameof(Type.GetTypeFromHandle)) && m.IsStatic && m.IsPublic); 285 | il.EmitCall(OpCodes.Call, getTypeFromHandle, null); 286 | 287 | il.Emit(OpCodes.Call, baseConstructor); 288 | il.Emit(OpCodes.Ret); 289 | 290 | verificationResult.Constructor = constructorBuilder; 291 | verificationResult.TypeBuilder = typeBuilder; 292 | } 293 | 294 | private static void GenerateProxyTypeFromProperties(Type sourceType, Type targetType, VerificationResult verificationResult) 295 | { 296 | var baseType = typeof(ProxyBase<>).MakeGenericType(sourceType); 297 | var typeBuilder = ProxyAssembly.DefineType( 298 | string.Format("Proxy_From_{0}_To_{1}", sourceType.Name, targetType.Name), 299 | TypeAttributes.Class, 300 | baseType, 301 | new Type[] { targetType }); 302 | 303 | var constructorBuilder = typeBuilder.DefineConstructor( 304 | MethodAttributes.Public, 305 | CallingConventions.Standard, 306 | new Type[] { sourceType }); 307 | 308 | var il = constructorBuilder.GetILGenerator(); 309 | il.Emit(OpCodes.Ldarg_0); 310 | il.Emit(OpCodes.Ldarg_1); 311 | il.Emit(OpCodes.Castclass, sourceType); 312 | il.Emit(OpCodes.Call, baseType.GetTypeInfo() 313 | .DeclaredConstructors 314 | .First(c => 315 | { 316 | var parameters = c.GetParameters(); 317 | return parameters.Length == 1 && parameters[0].ParameterType == sourceType; 318 | })); 319 | il.Emit(OpCodes.Ret); 320 | 321 | verificationResult.Constructor = constructorBuilder; 322 | verificationResult.TypeBuilder = typeBuilder; 323 | } 324 | 325 | private static void AddProperties( 326 | ProxyBuilderContext context, 327 | TypeBuilder typeBuilder, 328 | IEnumerable> properties) 329 | { 330 | foreach (var property in properties) 331 | { 332 | var targetProperty = property.Key; 333 | var sourceProperty = property.Value; 334 | 335 | var propertyBuilder = typeBuilder.DefineProperty( 336 | targetProperty.Name, 337 | PropertyAttributes.None, 338 | targetProperty.PropertyType, 339 | EmptyTypes); 340 | 341 | var methodBuilder = typeBuilder.DefineMethod( 342 | targetProperty.GetMethod.Name, 343 | targetProperty.GetMethod.Attributes & ~MethodAttributes.Abstract, 344 | targetProperty.GetMethod.CallingConvention, 345 | targetProperty.GetMethod.ReturnType, 346 | EmptyTypes); 347 | propertyBuilder.SetGetMethod(methodBuilder); 348 | typeBuilder.DefineMethodOverride(methodBuilder, targetProperty.GetMethod); 349 | 350 | var il = methodBuilder.GetILGenerator(); 351 | if (sourceProperty == null) 352 | { 353 | // Return a default(T) value. 354 | il.DeclareLocal(targetProperty.PropertyType); 355 | 356 | il.Emit(OpCodes.Ldloca_S, 0); 357 | il.Emit(OpCodes.Initobj, targetProperty.PropertyType); 358 | 359 | il.Emit(OpCodes.Ldloc_S, 0); 360 | il.Emit(OpCodes.Ret); 361 | continue; 362 | } 363 | 364 | il.DeclareLocal(targetProperty.PropertyType); 365 | il.DeclareLocal(sourceProperty.PropertyType); 366 | 367 | // Init variables with default(T) 368 | il.Emit(OpCodes.Ldloca_S, 0); 369 | il.Emit(OpCodes.Initobj, targetProperty.PropertyType); 370 | 371 | il.Emit(OpCodes.Ldloca_S, 1); 372 | il.Emit(OpCodes.Initobj, sourceProperty.PropertyType); 373 | 374 | // Push 'this' and get the underlying instance. 375 | il.Emit(OpCodes.Ldarg_0); 376 | il.Emit(OpCodes.Ldfld, 377 | typeBuilder.BaseType.GetRuntimeFields().First( 378 | f => string.Equals(f.Name, "Instance", StringComparison.Ordinal) && 379 | !f.IsStatic && 380 | f.IsPublic)); 381 | 382 | // Call the source property. 383 | il.EmitCall(OpCodes.Callvirt, sourceProperty.GetMethod, null); 384 | il.Emit(OpCodes.Stloc_S, 1); 385 | 386 | var @return = il.DefineLabel(); 387 | 388 | // BrFalse can't handle all value types (Decimal for example), so we skip it for value types 389 | // since we're using it as a null-check anyway. 390 | if (!sourceProperty.PropertyType.GetTypeInfo().IsValueType) 391 | { 392 | // At this point the value on the stack is the return value of the the source-type property. 393 | // If it returned null, we want to just jump to return. 394 | il.Emit(OpCodes.Ldloc_S, 1); 395 | il.Emit(OpCodes.Brfalse, @return); 396 | } 397 | 398 | // Create a proxy for the value returned by source property (if necessary). 399 | il.Emit(OpCodes.Ldloc_S, 1); 400 | EmitProxy(context, il, targetProperty.PropertyType, sourceProperty.PropertyType); 401 | il.Emit(OpCodes.Stloc_S, 0); 402 | 403 | il.MarkLabel(@return); 404 | il.Emit(OpCodes.Ldloc_S, 0); 405 | il.Emit(OpCodes.Ret); 406 | } 407 | } 408 | 409 | private static void EmitProxy(ProxyBuilderContext context, ILGenerator il, Type targetType, Type sourceType) 410 | { 411 | if (sourceType == targetType) 412 | { 413 | // Do nothing. 414 | return; 415 | } 416 | else if (targetType.GetTypeInfo().IsAssignableFrom(sourceType.GetTypeInfo())) 417 | { 418 | il.Emit(OpCodes.Castclass, targetType); 419 | return; 420 | } 421 | 422 | // If we get here, then we actually need a proxy. 423 | var key = new Tuple(sourceType, targetType); 424 | 425 | ConstructorInfo constructor = null; 426 | ProxyTypeCacheResult cacheResult; 427 | VerificationResult verificationResult; 428 | if (context.Cache.TryGetValue(key, out cacheResult)) 429 | { 430 | Debug.Assert(!cacheResult.IsError); 431 | Debug.Assert(cacheResult.Constructor != null); 432 | 433 | // This means we've got a fully-built (published) type. 434 | constructor = cacheResult.Constructor; 435 | } 436 | else if (context.Visited.TryGetValue(key, out verificationResult)) 437 | { 438 | if (verificationResult.Constructor != null) 439 | { 440 | constructor = verificationResult.Constructor; 441 | } 442 | } 443 | 444 | Debug.Assert(constructor != null); 445 | 446 | // Create the proxy. 447 | il.Emit(OpCodes.Newobj, constructor); 448 | } 449 | 450 | private static Type GetGenericImplementation(Type type, Type openGenericInterfaceType) 451 | { 452 | if (type.GetTypeInfo().IsGenericType && 453 | type.GetTypeInfo().GetGenericTypeDefinition() == openGenericInterfaceType) 454 | { 455 | return type; 456 | } 457 | 458 | foreach (var interfaceType in type.GetTypeInfo().ImplementedInterfaces) 459 | { 460 | if (interfaceType.GetTypeInfo().IsGenericType && 461 | interfaceType.GetTypeInfo().GetGenericTypeDefinition() == openGenericInterfaceType) 462 | { 463 | return interfaceType; 464 | } 465 | } 466 | 467 | return null; 468 | } 469 | 470 | internal class ProxyBuilderContext 471 | { 472 | public ProxyBuilderContext(ProxyTypeCache cache, Type targetType, Type sourceType) 473 | { 474 | Cache = cache; 475 | 476 | Key = new Tuple(sourceType, targetType); 477 | Visited = new Dictionary, VerificationResult>(); 478 | } 479 | 480 | public ProxyTypeCache Cache { get; } 481 | 482 | public Tuple Key { get; } 483 | 484 | public Type SourceType => Key.Item1; 485 | 486 | public Type TargetType => Key.Item2; 487 | 488 | public Dictionary, VerificationResult> Visited { get; } 489 | } 490 | 491 | internal class VerificationResult 492 | { 493 | public ConstructorInfo Constructor { get; set; } 494 | 495 | public IEnumerable> Mappings { get; set; } 496 | 497 | public TypeBuilder TypeBuilder { get; set; } 498 | 499 | public Type Type { get; set; } 500 | } 501 | } 502 | } 503 | #elif NETSTANDARD2_0 504 | #else 505 | #error Target frameworks should be updated 506 | #endif 507 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/Microsoft.Extensions.DiagnosticAdapter.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Microsoft extension adapter feature to extend DiagnosticListener. Contains extension methods that extend System.Diagnostics.DiagnosticListener, and enables duck-typing based event handling by using dynamically generated proxy types. 5 | netstandard2.0;netcoreapp2.0;net461 6 | $(NoWarn);CS1591 7 | true 8 | diagnosticadapter;diagnosticlistener;diagnostics 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Runtime.CompilerServices; 5 | 6 | [assembly: InternalsVisibleTo("Microsoft.Extensions.DiagnosticAdapter.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] 7 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] 8 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace Microsoft.Extensions.DiagnosticAdapter 3 | { 4 | using System.Globalization; 5 | using System.Reflection; 6 | using System.Resources; 7 | 8 | internal static class Resources 9 | { 10 | private static readonly ResourceManager _resourceManager 11 | = new ResourceManager("Microsoft.Extensions.DiagnosticAdapter.Resources", typeof(Resources).GetTypeInfo().Assembly); 12 | 13 | /// 14 | /// The property '{0}' on type '{1}' must define a getter to support proxy generation. 15 | /// 16 | internal static string Converter_PropertyMustHaveGetter 17 | { 18 | get { return GetString("Converter_PropertyMustHaveGetter"); } 19 | } 20 | 21 | /// 22 | /// The property '{0}' on type '{1}' must define a getter to support proxy generation. 23 | /// 24 | internal static string FormatConverter_PropertyMustHaveGetter(object p0, object p1) 25 | { 26 | return string.Format(CultureInfo.CurrentCulture, GetString("Converter_PropertyMustHaveGetter"), p0, p1); 27 | } 28 | 29 | /// 30 | /// The property '{0}' on type '{1}' must not use index parameters to support proxy generation. 31 | /// 32 | internal static string Converter_PropertyMustNotHaveIndexParameters 33 | { 34 | get { return GetString("Converter_PropertyMustNotHaveIndexParameters"); } 35 | } 36 | 37 | /// 38 | /// The property '{0}' on type '{1}' must not use index parameters to support proxy generation. 39 | /// 40 | internal static string FormatConverter_PropertyMustNotHaveIndexParameters(object p0, object p1) 41 | { 42 | return string.Format(CultureInfo.CurrentCulture, GetString("Converter_PropertyMustNotHaveIndexParameters"), p0, p1); 43 | } 44 | 45 | /// 46 | /// The property '{0}' on type '{1}' must not define a setter to support proxy generation. 47 | /// 48 | internal static string Converter_PropertyMustNotHaveSetter 49 | { 50 | get { return GetString("Converter_PropertyMustNotHaveSetter"); } 51 | } 52 | 53 | /// 54 | /// The property '{0}' on type '{1}' must not define a setter to support proxy generation. 55 | /// 56 | internal static string FormatConverter_PropertyMustNotHaveSetter(object p0, object p1) 57 | { 58 | return string.Format(CultureInfo.CurrentCulture, GetString("Converter_PropertyMustNotHaveSetter"), p0, p1); 59 | } 60 | 61 | /// 62 | /// Type '{0}' must be an interface in order to support proxy generation from source type '{1}'. 63 | /// 64 | internal static string Converter_TypeMustBeInterface 65 | { 66 | get { return GetString("Converter_TypeMustBeInterface"); } 67 | } 68 | 69 | /// 70 | /// Type '{0}' must be an interface in order to support proxy generation from source type '{1}'. 71 | /// 72 | internal static string FormatConverter_TypeMustBeInterface(object p0, object p1) 73 | { 74 | return string.Format(CultureInfo.CurrentCulture, GetString("Converter_TypeMustBeInterface"), p0, p1); 75 | } 76 | 77 | /// 78 | /// Unable to generate a proxy for method '{0}'. See Inner Exception for details. 79 | /// 80 | internal static string Converter_UnableToGenerateProxy 81 | { 82 | get { return GetString("Converter_UnableToGenerateProxy"); } 83 | } 84 | 85 | /// 86 | /// Unable to generate a proxy for method '{0}'. See Inner Exception for details. 87 | /// 88 | internal static string FormatConverter_UnableToGenerateProxy(object p0) 89 | { 90 | return string.Format(CultureInfo.CurrentCulture, GetString("Converter_UnableToGenerateProxy"), p0); 91 | } 92 | 93 | /// 94 | /// Proxy method generation doesn't support types with properties that vary only by case. The type '{0}' defines multiple properties named '{1}' that vary only by case. 95 | /// 96 | internal static string Converter_TypeMustNotHavePropertiesThatVaryByCase 97 | { 98 | get { return GetString("Converter_TypeMustNotHavePropertiesThatVaryByCase"); } 99 | } 100 | 101 | /// 102 | /// Proxy method generation doesn't support types with properties that vary only by case. The type '{0}' defines multiple properties named '{1}' that vary only by case. 103 | /// 104 | internal static string FormatConverter_TypeMustNotHavePropertiesThatVaryByCase(object p0, object p1) 105 | { 106 | return string.Format(CultureInfo.CurrentCulture, GetString("Converter_TypeMustNotHavePropertiesThatVaryByCase"), p0, p1); 107 | } 108 | 109 | private static string GetString(string name, params string[] formatterNames) 110 | { 111 | var value = _resourceManager.GetString(name); 112 | 113 | System.Diagnostics.Debug.Assert(value != null); 114 | 115 | if (formatterNames != null) 116 | { 117 | for (var i = 0; i < formatterNames.Length; i++) 118 | { 119 | value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); 120 | } 121 | } 122 | 123 | return value; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/ProxyDiagnosticSourceMethodAdapter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Reflection; 6 | using Microsoft.Extensions.DiagnosticAdapter.Infrastructure; 7 | using Microsoft.Extensions.DiagnosticAdapter.Internal; 8 | 9 | namespace Microsoft.Extensions.DiagnosticAdapter 10 | { 11 | public class ProxyDiagnosticSourceMethodAdapter : IDiagnosticSourceMethodAdapter 12 | { 13 | private readonly IProxyFactory _factory = new ProxyFactory(); 14 | 15 | public Func Adapt(MethodInfo method, Type inputType) 16 | { 17 | #if NETCOREAPP2_0 || NET461 18 | var proxyMethod = ProxyMethodEmitter.CreateProxyMethod(method, inputType); 19 | return (listener, data) => proxyMethod(listener, data, _factory); 20 | #elif NETSTANDARD2_0 21 | throw new PlatformNotSupportedException("This platform does not support creating proxy types and methods."); 22 | #else 23 | #error Target frameworks should be updated 24 | #endif 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | The property '{0}' on type '{1}' must define a getter to support proxy generation. 122 | 123 | 124 | The property '{0}' on type '{1}' must not use index parameters to support proxy generation. 125 | 126 | 127 | The property '{0}' on type '{1}' must not define a setter to support proxy generation. 128 | 129 | 130 | Type '{0}' must be an interface in order to support proxy generation from source type '{1}'. 131 | 132 | 133 | Proxy method generation doesn't support types with properties that vary only by case. The type '{0}' defines multiple properties named '{1}' that vary only by case. 134 | 135 | 136 | Unable to generate a proxy for method '{0}'. See Inner Exception for details. 137 | 138 | -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/baseline.net461.json: -------------------------------------------------------------------------------- 1 | { 2 | "AssemblyIdentity": "Microsoft.Extensions.DiagnosticAdapter, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", 3 | "Types": [ 4 | { 5 | "Name": "Microsoft.Extensions.DiagnosticAdapter.DiagnosticNameAttribute", 6 | "Visibility": "Public", 7 | "Kind": "Class", 8 | "BaseType": "System.Attribute", 9 | "ImplementedInterfaces": [], 10 | "Members": [ 11 | { 12 | "Kind": "Method", 13 | "Name": "get_Name", 14 | "Parameters": [], 15 | "ReturnType": "System.String", 16 | "Visibility": "Public", 17 | "GenericParameter": [] 18 | }, 19 | { 20 | "Kind": "Constructor", 21 | "Name": ".ctor", 22 | "Parameters": [ 23 | { 24 | "Name": "name", 25 | "Type": "System.String" 26 | } 27 | ], 28 | "Visibility": "Public", 29 | "GenericParameter": [] 30 | } 31 | ], 32 | "GenericParameters": [] 33 | }, 34 | { 35 | "Name": "Microsoft.Extensions.DiagnosticAdapter.DiagnosticSourceAdapter", 36 | "Visibility": "Public", 37 | "Kind": "Class", 38 | "ImplementedInterfaces": [ 39 | "System.IObserver>" 40 | ], 41 | "Members": [ 42 | { 43 | "Kind": "Method", 44 | "Name": "IsEnabled", 45 | "Parameters": [ 46 | { 47 | "Name": "diagnosticName", 48 | "Type": "System.String" 49 | } 50 | ], 51 | "ReturnType": "System.Boolean", 52 | "Visibility": "Public", 53 | "GenericParameter": [] 54 | }, 55 | { 56 | "Kind": "Method", 57 | "Name": "IsEnabled", 58 | "Parameters": [ 59 | { 60 | "Name": "diagnosticName", 61 | "Type": "System.String" 62 | }, 63 | { 64 | "Name": "arg1", 65 | "Type": "System.Object" 66 | }, 67 | { 68 | "Name": "arg2", 69 | "Type": "System.Object", 70 | "DefaultValue": "null" 71 | } 72 | ], 73 | "ReturnType": "System.Boolean", 74 | "Visibility": "Public", 75 | "GenericParameter": [] 76 | }, 77 | { 78 | "Kind": "Method", 79 | "Name": "Write", 80 | "Parameters": [ 81 | { 82 | "Name": "diagnosticName", 83 | "Type": "System.String" 84 | }, 85 | { 86 | "Name": "parameters", 87 | "Type": "System.Object" 88 | } 89 | ], 90 | "ReturnType": "System.Void", 91 | "Visibility": "Public", 92 | "GenericParameter": [] 93 | }, 94 | { 95 | "Kind": "Constructor", 96 | "Name": ".ctor", 97 | "Parameters": [ 98 | { 99 | "Name": "target", 100 | "Type": "System.Object" 101 | } 102 | ], 103 | "Visibility": "Public", 104 | "GenericParameter": [] 105 | }, 106 | { 107 | "Kind": "Constructor", 108 | "Name": ".ctor", 109 | "Parameters": [ 110 | { 111 | "Name": "target", 112 | "Type": "System.Object" 113 | }, 114 | { 115 | "Name": "isEnabled", 116 | "Type": "System.Func" 117 | } 118 | ], 119 | "Visibility": "Public", 120 | "GenericParameter": [] 121 | }, 122 | { 123 | "Kind": "Constructor", 124 | "Name": ".ctor", 125 | "Parameters": [ 126 | { 127 | "Name": "target", 128 | "Type": "System.Object" 129 | }, 130 | { 131 | "Name": "isEnabled", 132 | "Type": "System.Func" 133 | } 134 | ], 135 | "Visibility": "Public", 136 | "GenericParameter": [] 137 | }, 138 | { 139 | "Kind": "Constructor", 140 | "Name": ".ctor", 141 | "Parameters": [ 142 | { 143 | "Name": "target", 144 | "Type": "System.Object" 145 | }, 146 | { 147 | "Name": "isEnabled", 148 | "Type": "System.Func" 149 | }, 150 | { 151 | "Name": "methodAdapter", 152 | "Type": "Microsoft.Extensions.DiagnosticAdapter.IDiagnosticSourceMethodAdapter" 153 | } 154 | ], 155 | "Visibility": "Public", 156 | "GenericParameter": [] 157 | }, 158 | { 159 | "Kind": "Constructor", 160 | "Name": ".ctor", 161 | "Parameters": [ 162 | { 163 | "Name": "target", 164 | "Type": "System.Object" 165 | }, 166 | { 167 | "Name": "isEnabled", 168 | "Type": "System.Func" 169 | }, 170 | { 171 | "Name": "methodAdapter", 172 | "Type": "Microsoft.Extensions.DiagnosticAdapter.IDiagnosticSourceMethodAdapter" 173 | } 174 | ], 175 | "Visibility": "Public", 176 | "GenericParameter": [] 177 | } 178 | ], 179 | "GenericParameters": [] 180 | }, 181 | { 182 | "Name": "Microsoft.Extensions.DiagnosticAdapter.IDiagnosticSourceMethodAdapter", 183 | "Visibility": "Public", 184 | "Kind": "Interface", 185 | "Abstract": true, 186 | "ImplementedInterfaces": [], 187 | "Members": [ 188 | { 189 | "Kind": "Method", 190 | "Name": "Adapt", 191 | "Parameters": [ 192 | { 193 | "Name": "method", 194 | "Type": "System.Reflection.MethodInfo" 195 | }, 196 | { 197 | "Name": "inputType", 198 | "Type": "System.Type" 199 | } 200 | ], 201 | "ReturnType": "System.Func", 202 | "GenericParameter": [] 203 | } 204 | ], 205 | "GenericParameters": [] 206 | }, 207 | { 208 | "Name": "Microsoft.Extensions.DiagnosticAdapter.ProxyDiagnosticSourceMethodAdapter", 209 | "Visibility": "Public", 210 | "Kind": "Class", 211 | "ImplementedInterfaces": [ 212 | "Microsoft.Extensions.DiagnosticAdapter.IDiagnosticSourceMethodAdapter" 213 | ], 214 | "Members": [ 215 | { 216 | "Kind": "Method", 217 | "Name": "Adapt", 218 | "Parameters": [ 219 | { 220 | "Name": "method", 221 | "Type": "System.Reflection.MethodInfo" 222 | }, 223 | { 224 | "Name": "inputType", 225 | "Type": "System.Type" 226 | } 227 | ], 228 | "ReturnType": "System.Func", 229 | "Sealed": true, 230 | "Virtual": true, 231 | "ImplementedInterface": "Microsoft.Extensions.DiagnosticAdapter.IDiagnosticSourceMethodAdapter", 232 | "Visibility": "Public", 233 | "GenericParameter": [] 234 | }, 235 | { 236 | "Kind": "Constructor", 237 | "Name": ".ctor", 238 | "Parameters": [], 239 | "Visibility": "Public", 240 | "GenericParameter": [] 241 | } 242 | ], 243 | "GenericParameters": [] 244 | }, 245 | { 246 | "Name": "Microsoft.Extensions.DiagnosticAdapter.Infrastructure.IProxy", 247 | "Visibility": "Public", 248 | "Kind": "Interface", 249 | "Abstract": true, 250 | "ImplementedInterfaces": [], 251 | "Members": [ 252 | { 253 | "Kind": "Method", 254 | "Name": "Upwrap", 255 | "Parameters": [], 256 | "ReturnType": "T0", 257 | "GenericParameter": [ 258 | { 259 | "ParameterName": "T", 260 | "ParameterPosition": 0, 261 | "BaseTypeOrInterfaces": [] 262 | } 263 | ] 264 | } 265 | ], 266 | "GenericParameters": [] 267 | }, 268 | { 269 | "Name": "Microsoft.Extensions.DiagnosticAdapter.Infrastructure.IProxyFactory", 270 | "Visibility": "Public", 271 | "Kind": "Interface", 272 | "Abstract": true, 273 | "ImplementedInterfaces": [], 274 | "Members": [ 275 | { 276 | "Kind": "Method", 277 | "Name": "CreateProxy", 278 | "Parameters": [ 279 | { 280 | "Name": "obj", 281 | "Type": "System.Object" 282 | } 283 | ], 284 | "ReturnType": "T0", 285 | "GenericParameter": [ 286 | { 287 | "ParameterName": "TProxy", 288 | "ParameterPosition": 0, 289 | "BaseTypeOrInterfaces": [] 290 | } 291 | ] 292 | } 293 | ], 294 | "GenericParameters": [] 295 | }, 296 | { 297 | "Name": "System.Diagnostics.DiagnosticListenerExtensions", 298 | "Visibility": "Public", 299 | "Kind": "Class", 300 | "Abstract": true, 301 | "Static": true, 302 | "Sealed": true, 303 | "ImplementedInterfaces": [], 304 | "Members": [ 305 | { 306 | "Kind": "Method", 307 | "Name": "SubscribeWithAdapter", 308 | "Parameters": [ 309 | { 310 | "Name": "diagnostic", 311 | "Type": "System.Diagnostics.DiagnosticListener" 312 | }, 313 | { 314 | "Name": "target", 315 | "Type": "System.Object" 316 | } 317 | ], 318 | "ReturnType": "System.IDisposable", 319 | "Static": true, 320 | "Extension": true, 321 | "Visibility": "Public", 322 | "GenericParameter": [] 323 | }, 324 | { 325 | "Kind": "Method", 326 | "Name": "SubscribeWithAdapter", 327 | "Parameters": [ 328 | { 329 | "Name": "diagnostic", 330 | "Type": "System.Diagnostics.DiagnosticListener" 331 | }, 332 | { 333 | "Name": "target", 334 | "Type": "System.Object" 335 | }, 336 | { 337 | "Name": "isEnabled", 338 | "Type": "System.Func" 339 | } 340 | ], 341 | "ReturnType": "System.IDisposable", 342 | "Static": true, 343 | "Extension": true, 344 | "Visibility": "Public", 345 | "GenericParameter": [] 346 | }, 347 | { 348 | "Kind": "Method", 349 | "Name": "SubscribeWithAdapter", 350 | "Parameters": [ 351 | { 352 | "Name": "diagnostic", 353 | "Type": "System.Diagnostics.DiagnosticListener" 354 | }, 355 | { 356 | "Name": "target", 357 | "Type": "System.Object" 358 | }, 359 | { 360 | "Name": "isEnabled", 361 | "Type": "System.Func" 362 | } 363 | ], 364 | "ReturnType": "System.IDisposable", 365 | "Static": true, 366 | "Extension": true, 367 | "Visibility": "Public", 368 | "GenericParameter": [] 369 | } 370 | ], 371 | "GenericParameters": [] 372 | } 373 | ] 374 | } -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/baseline.netcore.json: -------------------------------------------------------------------------------- 1 | { 2 | "AssemblyIdentity": "Microsoft.Extensions.DiagnosticAdapter, Version=2.0.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", 3 | "Types": [ 4 | { 5 | "Name": "Microsoft.Extensions.DiagnosticAdapter.DiagnosticNameAttribute", 6 | "Visibility": "Public", 7 | "Kind": "Class", 8 | "BaseType": "System.Attribute", 9 | "ImplementedInterfaces": [], 10 | "Members": [ 11 | { 12 | "Kind": "Method", 13 | "Name": "get_Name", 14 | "Parameters": [], 15 | "ReturnType": "System.String", 16 | "Visibility": "Public", 17 | "GenericParameter": [] 18 | }, 19 | { 20 | "Kind": "Constructor", 21 | "Name": ".ctor", 22 | "Parameters": [ 23 | { 24 | "Name": "name", 25 | "Type": "System.String" 26 | } 27 | ], 28 | "Visibility": "Public", 29 | "GenericParameter": [] 30 | } 31 | ], 32 | "GenericParameters": [] 33 | }, 34 | { 35 | "Name": "Microsoft.Extensions.DiagnosticAdapter.DiagnosticSourceAdapter", 36 | "Visibility": "Public", 37 | "Kind": "Class", 38 | "ImplementedInterfaces": [ 39 | "System.IObserver>" 40 | ], 41 | "Members": [ 42 | { 43 | "Kind": "Method", 44 | "Name": "IsEnabled", 45 | "Parameters": [ 46 | { 47 | "Name": "diagnosticName", 48 | "Type": "System.String" 49 | } 50 | ], 51 | "ReturnType": "System.Boolean", 52 | "Visibility": "Public", 53 | "GenericParameter": [] 54 | }, 55 | { 56 | "Kind": "Method", 57 | "Name": "IsEnabled", 58 | "Parameters": [ 59 | { 60 | "Name": "diagnosticName", 61 | "Type": "System.String" 62 | }, 63 | { 64 | "Name": "arg1", 65 | "Type": "System.Object" 66 | }, 67 | { 68 | "Name": "arg2", 69 | "Type": "System.Object", 70 | "DefaultValue": "null" 71 | } 72 | ], 73 | "ReturnType": "System.Boolean", 74 | "Visibility": "Public", 75 | "GenericParameter": [] 76 | }, 77 | { 78 | "Kind": "Method", 79 | "Name": "Write", 80 | "Parameters": [ 81 | { 82 | "Name": "diagnosticName", 83 | "Type": "System.String" 84 | }, 85 | { 86 | "Name": "arg1", 87 | "Type": "System.Object" 88 | }, 89 | { 90 | "Name": "arg2", 91 | "Type": "System.Object", 92 | "DefaultValue": "null" 93 | } 94 | ], 95 | "ReturnType": "System.Boolean", 96 | "Visibility": "Public", 97 | "GenericParameter": [] 98 | }, 99 | { 100 | "Kind": "Method", 101 | "Name": "Write", 102 | "Parameters": [ 103 | { 104 | "Name": "diagnosticName", 105 | "Type": "System.String" 106 | }, 107 | { 108 | "Name": "parameters", 109 | "Type": "System.Object" 110 | } 111 | ], 112 | "ReturnType": "System.Void", 113 | "Visibility": "Public", 114 | "GenericParameter": [] 115 | }, 116 | { 117 | "Kind": "Constructor", 118 | "Name": ".ctor", 119 | "Parameters": [ 120 | { 121 | "Name": "target", 122 | "Type": "System.Object" 123 | } 124 | ], 125 | "Visibility": "Public", 126 | "GenericParameter": [] 127 | }, 128 | { 129 | "Kind": "Constructor", 130 | "Name": ".ctor", 131 | "Parameters": [ 132 | { 133 | "Name": "target", 134 | "Type": "System.Object" 135 | }, 136 | { 137 | "Name": "isEnabled", 138 | "Type": "System.Func" 139 | } 140 | ], 141 | "Visibility": "Public", 142 | "GenericParameter": [] 143 | }, 144 | { 145 | "Kind": "Constructor", 146 | "Name": ".ctor", 147 | "Parameters": [ 148 | { 149 | "Name": "target", 150 | "Type": "System.Object" 151 | }, 152 | { 153 | "Name": "isEnabled", 154 | "Type": "System.Func" 155 | } 156 | ], 157 | "Visibility": "Public", 158 | "GenericParameter": [] 159 | }, 160 | { 161 | "Kind": "Constructor", 162 | "Name": ".ctor", 163 | "Parameters": [ 164 | { 165 | "Name": "target", 166 | "Type": "System.Object" 167 | }, 168 | { 169 | "Name": "isEnabled", 170 | "Type": "System.Func" 171 | }, 172 | { 173 | "Name": "methodAdapter", 174 | "Type": "Microsoft.Extensions.DiagnosticAdapter.IDiagnosticSourceMethodAdapter" 175 | } 176 | ], 177 | "Visibility": "Public", 178 | "GenericParameter": [] 179 | } 180 | ], 181 | "GenericParameters": [] 182 | }, 183 | { 184 | "Name": "Microsoft.Extensions.DiagnosticAdapter.IDiagnosticSourceMethodAdapter", 185 | "Visibility": "Public", 186 | "Kind": "Interface", 187 | "Abstract": true, 188 | "ImplementedInterfaces": [], 189 | "Members": [ 190 | { 191 | "Kind": "Method", 192 | "Name": "Adapt", 193 | "Parameters": [ 194 | { 195 | "Name": "method", 196 | "Type": "System.Reflection.MethodInfo" 197 | }, 198 | { 199 | "Name": "inputType", 200 | "Type": "System.Type" 201 | } 202 | ], 203 | "ReturnType": "System.Func", 204 | "GenericParameter": [] 205 | } 206 | ], 207 | "GenericParameters": [] 208 | }, 209 | { 210 | "Name": "Microsoft.Extensions.DiagnosticAdapter.ProxyDiagnosticSourceMethodAdapter", 211 | "Visibility": "Public", 212 | "Kind": "Class", 213 | "ImplementedInterfaces": [ 214 | "Microsoft.Extensions.DiagnosticAdapter.IDiagnosticSourceMethodAdapter" 215 | ], 216 | "Members": [ 217 | { 218 | "Kind": "Method", 219 | "Name": "Adapt", 220 | "Parameters": [ 221 | { 222 | "Name": "method", 223 | "Type": "System.Reflection.MethodInfo" 224 | }, 225 | { 226 | "Name": "inputType", 227 | "Type": "System.Type" 228 | } 229 | ], 230 | "ReturnType": "System.Func", 231 | "Sealed": true, 232 | "Virtual": true, 233 | "ImplementedInterface": "Microsoft.Extensions.DiagnosticAdapter.IDiagnosticSourceMethodAdapter", 234 | "Visibility": "Public", 235 | "GenericParameter": [] 236 | }, 237 | { 238 | "Kind": "Constructor", 239 | "Name": ".ctor", 240 | "Parameters": [], 241 | "Visibility": "Public", 242 | "GenericParameter": [] 243 | } 244 | ], 245 | "GenericParameters": [] 246 | }, 247 | { 248 | "Name": "Microsoft.Extensions.DiagnosticAdapter.Internal.InvalidProxyOperationException", 249 | "Visibility": "Public", 250 | "Kind": "Class", 251 | "BaseType": "System.InvalidOperationException", 252 | "ImplementedInterfaces": [], 253 | "Members": [ 254 | { 255 | "Kind": "Constructor", 256 | "Name": ".ctor", 257 | "Parameters": [ 258 | { 259 | "Name": "message", 260 | "Type": "System.String" 261 | } 262 | ], 263 | "Visibility": "Public", 264 | "GenericParameter": [] 265 | } 266 | ], 267 | "GenericParameters": [] 268 | }, 269 | { 270 | "Name": "Microsoft.Extensions.DiagnosticAdapter.Internal.ProxyAssembly", 271 | "Visibility": "Public", 272 | "Kind": "Class", 273 | "Abstract": true, 274 | "Static": true, 275 | "Sealed": true, 276 | "ImplementedInterfaces": [], 277 | "Members": [ 278 | { 279 | "Kind": "Method", 280 | "Name": "DefineType", 281 | "Parameters": [ 282 | { 283 | "Name": "target", 284 | "Type": "System.Object" 285 | }, 286 | { 287 | "Name": "isEnabled", 288 | "Type": "System.Func" 289 | } 290 | ], 291 | "Visibility": "Public", 292 | "GenericParameter": [] 293 | }, 294 | { 295 | "Kind": "Constructor", 296 | "Name": ".ctor", 297 | "Parameters": [ 298 | { 299 | "Name": "target", 300 | "Type": "System.Object" 301 | }, 302 | { 303 | "Name": "isEnabled", 304 | "Type": "System.Func" 305 | }, 306 | { 307 | "Name": "methodAdapter", 308 | "Type": "Microsoft.Extensions.DiagnosticAdapter.IDiagnosticSourceMethodAdapter" 309 | } 310 | ], 311 | "Visibility": "Public", 312 | "GenericParameter": [] 313 | }, 314 | { 315 | "Kind": "Constructor", 316 | "Name": ".ctor", 317 | "Parameters": [ 318 | { 319 | "Name": "target", 320 | "Type": "System.Object" 321 | }, 322 | { 323 | "Name": "isEnabled", 324 | "Type": "System.Func" 325 | }, 326 | { 327 | "Name": "methodAdapter", 328 | "Type": "Microsoft.Extensions.DiagnosticAdapter.IDiagnosticSourceMethodAdapter" 329 | } 330 | ], 331 | "Visibility": "Public", 332 | "GenericParameter": [] 333 | } 334 | ], 335 | "GenericParameters": [] 336 | }, 337 | { 338 | "Name": "Microsoft.Extensions.DiagnosticAdapter.IDiagnosticSourceMethodAdapter", 339 | "Visibility": "Public", 340 | "Kind": "Interface", 341 | "Abstract": true, 342 | "ImplementedInterfaces": [], 343 | "Members": [ 344 | { 345 | "Kind": "Method", 346 | "Name": "Adapt", 347 | "Parameters": [ 348 | { 349 | "Name": "method", 350 | "Type": "System.Reflection.MethodInfo" 351 | }, 352 | { 353 | "Name": "inputType", 354 | "Type": "System.Type" 355 | } 356 | ], 357 | "ReturnType": "System.Func", 358 | "GenericParameter": [] 359 | } 360 | ], 361 | "GenericParameters": [] 362 | }, 363 | { 364 | "Name": "Microsoft.Extensions.DiagnosticAdapter.ProxyDiagnosticSourceMethodAdapter", 365 | "Visibility": "Public", 366 | "Kind": "Class", 367 | "ImplementedInterfaces": [ 368 | "Microsoft.Extensions.DiagnosticAdapter.IDiagnosticSourceMethodAdapter" 369 | ], 370 | "Members": [ 371 | { 372 | "Kind": "Method", 373 | "Name": "Adapt", 374 | "Parameters": [ 375 | { 376 | "Name": "method", 377 | "Type": "System.Reflection.MethodInfo" 378 | }, 379 | { 380 | "Name": "inputType", 381 | "Type": "System.Type" 382 | } 383 | ], 384 | "ReturnType": "System.Func", 385 | "Sealed": true, 386 | "Virtual": true, 387 | "ImplementedInterface": "Microsoft.Extensions.DiagnosticAdapter.IDiagnosticSourceMethodAdapter", 388 | "Visibility": "Public", 389 | "GenericParameter": [] 390 | }, 391 | { 392 | "Kind": "Constructor", 393 | "Name": ".ctor", 394 | "Parameters": [], 395 | "Visibility": "Public", 396 | "GenericParameter": [] 397 | } 398 | ], 399 | "GenericParameters": [] 400 | }, 401 | { 402 | "Name": "Microsoft.Extensions.DiagnosticAdapter.Infrastructure.IProxy", 403 | "Visibility": "Public", 404 | "Kind": "Interface", 405 | "Abstract": true, 406 | "ImplementedInterfaces": [], 407 | "Members": [ 408 | { 409 | "Kind": "Method", 410 | "Name": "Upwrap", 411 | "Parameters": [], 412 | "ReturnType": "T0", 413 | "GenericParameter": [ 414 | { 415 | "ParameterName": "T", 416 | "ParameterPosition": 0, 417 | "BaseTypeOrInterfaces": [] 418 | } 419 | ] 420 | } 421 | ], 422 | "GenericParameters": [] 423 | }, 424 | { 425 | "Name": "Microsoft.Extensions.DiagnosticAdapter.Infrastructure.IProxyFactory", 426 | "Visibility": "Public", 427 | "Kind": "Interface", 428 | "Abstract": true, 429 | "ImplementedInterfaces": [], 430 | "Members": [ 431 | { 432 | "Kind": "Method", 433 | "Name": "CreateProxy", 434 | "Parameters": [ 435 | { 436 | "Name": "obj", 437 | "Type": "System.Object" 438 | } 439 | ], 440 | "ReturnType": "T0", 441 | "GenericParameter": [ 442 | { 443 | "ParameterName": "TProxy", 444 | "ParameterPosition": 0, 445 | "BaseTypeOrInterfaces": [] 446 | } 447 | ] 448 | } 449 | ], 450 | "GenericParameters": [] 451 | }, 452 | { 453 | "Name": "System.Diagnostics.DiagnosticListenerExtensions", 454 | "Visibility": "Public", 455 | "Kind": "Class", 456 | "Abstract": true, 457 | "Static": true, 458 | "Sealed": true, 459 | "ImplementedInterfaces": [], 460 | "Members": [ 461 | { 462 | "Kind": "Method", 463 | "Name": "SubscribeWithAdapter", 464 | "Parameters": [ 465 | { 466 | "Name": "diagnostic", 467 | "Type": "System.Diagnostics.DiagnosticListener" 468 | }, 469 | { 470 | "Name": "target", 471 | "Type": "System.Object" 472 | } 473 | ], 474 | "ReturnType": "System.IDisposable", 475 | "Static": true, 476 | "Extension": true, 477 | "Visibility": "Public", 478 | "GenericParameter": [] 479 | }, 480 | { 481 | "Kind": "Method", 482 | "Name": "SubscribeWithAdapter", 483 | "Parameters": [ 484 | { 485 | "Name": "diagnostic", 486 | "Type": "System.Diagnostics.DiagnosticListener" 487 | }, 488 | { 489 | "Name": "target", 490 | "Type": "System.Object" 491 | }, 492 | { 493 | "Name": "isEnabled", 494 | "Type": "System.Func" 495 | } 496 | ], 497 | "ReturnType": "System.IDisposable", 498 | "Static": true, 499 | "Extension": true, 500 | "Visibility": "Public", 501 | "GenericParameter": [] 502 | }, 503 | { 504 | "Kind": "Method", 505 | "Name": "SubscribeWithAdapter", 506 | "Parameters": [ 507 | { 508 | "Name": "diagnostic", 509 | "Type": "System.Diagnostics.DiagnosticListener" 510 | }, 511 | { 512 | "Name": "target", 513 | "Type": "System.Object" 514 | }, 515 | { 516 | "Name": "isEnabled", 517 | "Type": "System.Func" 518 | } 519 | ], 520 | "ReturnType": "System.IDisposable", 521 | "Static": true, 522 | "Extension": true, 523 | "Visibility": "Public", 524 | "GenericParameter": [] 525 | } 526 | ], 527 | "GenericParameters": [] 528 | }, 529 | { 530 | "Name": "Microsoft.Extensions.DiagnosticAdapter.Internal.ProxyEnumerable+ProxyEnumerator", 531 | "Visibility": "Public", 532 | "Kind": "Class", 533 | "ImplementedInterfaces": [ 534 | "System.Collections.Generic.IEnumerator" 535 | ], 536 | "Members": [ 537 | { 538 | "Kind": "Method", 539 | "Name": "get_Current", 540 | "Parameters": [], 541 | "ReturnType": "T1", 542 | "Sealed": true, 543 | "Virtual": true, 544 | "ImplementedInterface": "System.Collections.Generic.IEnumerator", 545 | "Visibility": "Public", 546 | "GenericParameter": [] 547 | }, 548 | { 549 | "Kind": "Method", 550 | "Name": "SubscribeWithAdapter", 551 | "Parameters": [ 552 | { 553 | "Name": "diagnostic", 554 | "Type": "System.Diagnostics.DiagnosticListener" 555 | }, 556 | { 557 | "Name": "target", 558 | "Type": "System.Object" 559 | }, 560 | { 561 | "Name": "isEnabled", 562 | "Type": "System.Func" 563 | } 564 | ], 565 | "ReturnType": "System.IDisposable", 566 | "Static": true, 567 | "Extension": true, 568 | "Visibility": "Public", 569 | "GenericParameter": [] 570 | } 571 | ], 572 | "GenericParameters": [] 573 | } 574 | ] 575 | } -------------------------------------------------------------------------------- /src/Microsoft.Extensions.DiagnosticAdapter/breakingchanges.netcore.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "TypeId": "public class Microsoft.Extensions.DiagnosticAdapter.ProxyDiagnosticSourceMethodAdapter : Microsoft.Extensions.DiagnosticAdapter.IDiagnosticSourceMethodAdapter", 4 | "Kind": "Removal" 5 | }, 6 | { 7 | "TypeId": "public interface Microsoft.Extensions.DiagnosticAdapter.IDiagnosticSourceMethodAdapter", 8 | "Kind": "Removal" 9 | }, 10 | { 11 | "TypeId": "public class Microsoft.Extensions.DiagnosticAdapter.DiagnosticSourceAdapter : System.IObserver>", 12 | "MemberId": "public System.Boolean Write(System.String diagnosticName, System.Object arg1, System.Object arg2 = null)", 13 | "Kind": "Removal" 14 | } 15 | ] -------------------------------------------------------------------------------- /test/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | netcoreapp2.2 6 | $(DeveloperBuildTestTfms) 7 | $(StandardTestTfms) 8 | $(StandardTestTfms);net461 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/Microsoft.Extensions.DiagnosticAdapter.Test/DiagnosticSourceAdapterTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using Microsoft.AspNetCore.Testing.xunit; 6 | using Xunit; 7 | 8 | namespace Microsoft.Extensions.DiagnosticAdapter 9 | { 10 | public class DiagnosticSourceAdapterTest 11 | { 12 | public class Crash 13 | { 14 | public int CallCount { get; private set; } 15 | 16 | public int Baz { get; private set; } 17 | 18 | [DiagnosticName("Foo")] 19 | public void OnFoo(string bar) 20 | { 21 | CallCount++; 22 | } 23 | 24 | [DiagnosticName("Bar")] 25 | public void OnBar(int baz) 26 | { 27 | Baz = baz; 28 | CallCount++; 29 | } 30 | } 31 | 32 | public class OneTarget 33 | { 34 | public int OneCallCount { get; private set; } 35 | 36 | [DiagnosticName("One")] 37 | public void One() 38 | { 39 | ++OneCallCount; 40 | } 41 | } 42 | 43 | [Fact] 44 | public void IsEnabled_TrueForEnlistedEvent() 45 | { 46 | // Arrange 47 | var adapter = CreateAdapter(new OneTarget()); 48 | 49 | // Act & Assert 50 | Assert.True(adapter.IsEnabled("One")); 51 | } 52 | 53 | [Fact] 54 | public void IsEnabled_True_PredicateCalledForIsEnabled() 55 | { 56 | // Arrange 57 | var callCount = 0; 58 | Func isEnabled = (name) => 59 | { 60 | Assert.Equal("One", name); 61 | callCount++; 62 | return true; 63 | }; 64 | 65 | var adapter = CreateAdapter(new OneTarget(), isEnabled); 66 | 67 | // Act & Assert 68 | Assert.True(adapter.IsEnabled("One")); 69 | Assert.Equal(1, callCount); 70 | } 71 | 72 | [Fact] 73 | public void IsEnabled_True_PredicateCalledForIsEnabled_WithContext() 74 | { 75 | // Arrange 76 | var callCount = 0; 77 | Func isEnabled = (name, arg1, arg2) => 78 | { 79 | Assert.Equal("One", name); 80 | Assert.Equal("Target info", arg1); 81 | callCount++; 82 | return true; 83 | }; 84 | 85 | var adapter = CreateAdapter(new OneTarget(), isEnabled); 86 | 87 | // Act & Assert 88 | Assert.True(adapter.IsEnabled("One", "Target info")); 89 | Assert.Equal(1, callCount); 90 | } 91 | 92 | [Fact] 93 | public void IsEnabled_False_PredicateCalledForIsEnabled() 94 | { 95 | // Arrange 96 | var callCount = 0; 97 | Func isEnabled = (name) => 98 | { 99 | Assert.Equal("One", name); 100 | callCount++; 101 | return false; 102 | }; 103 | 104 | var adapter = CreateAdapter(new OneTarget(), isEnabled); 105 | 106 | // Act & Assert 107 | Assert.False(adapter.IsEnabled("One")); 108 | Assert.Equal(1, callCount); 109 | } 110 | 111 | [Fact] 112 | public void IsEnabled_False_PredicateCalledForIsEnabled_WithContext() 113 | { 114 | // Arrange 115 | var callCount = 0; 116 | Func isEnabled = (name, arg1, arg2) => 117 | { 118 | Assert.Equal("One", name); 119 | Assert.Equal("Target info", arg1); 120 | callCount++; 121 | return false; 122 | }; 123 | 124 | var adapter = CreateAdapter(new OneTarget(), isEnabled); 125 | 126 | // Act & Assert 127 | Assert.False(adapter.IsEnabled("One", "Target info")); 128 | Assert.Equal(1, callCount); 129 | } 130 | 131 | [Fact] 132 | public void IsEnabled_RegisterWithoutContext_CallIsEnabledWithContext() 133 | { 134 | // Arrange 135 | var callCount = 0; 136 | Func isEnabled = (name) => 137 | { 138 | Assert.Equal("One", name); 139 | callCount++; 140 | return false; 141 | }; 142 | 143 | var adapter = CreateAdapter(new OneTarget(), (a, b, c) => isEnabled(a)); 144 | 145 | // Act & Assert 146 | Assert.False(adapter.IsEnabled("One", new object(), new object())); 147 | Assert.Equal(1, callCount); 148 | } 149 | 150 | [Fact] 151 | public void IsEnabled_FalseForNonenlistedEvent() 152 | { 153 | // Arrange 154 | var adapter = CreateAdapter(new OneTarget()); 155 | 156 | // Act & Assert 157 | Assert.False(adapter.IsEnabled("Two")); 158 | } 159 | 160 | [Fact] 161 | public void IsEnabledWithContext_FalseForNonenlistedEvent() 162 | { 163 | // Arrange 164 | var adapter = CreateAdapter(new OneTarget(), (Func)null); 165 | 166 | // Act & Assert 167 | Assert.False(adapter.IsEnabled("Two", "Target info")); 168 | } 169 | 170 | [Fact] 171 | public void IsEnabledWithContext_TrueForListedEvent() 172 | { 173 | // Arrange 174 | var adapter = CreateAdapter(new OneTarget(), (Func)null); 175 | 176 | // Act & Assert 177 | Assert.True(adapter.IsEnabled("One", "Target info")); 178 | } 179 | 180 | [Fact] 181 | public void CallingWriteWithNullForNonNullableConverts() 182 | { 183 | // Arrange 184 | var target = new Crash(); 185 | var adapter = CreateAdapter(target); 186 | 187 | // Act & Assert 188 | Assert.Equal(0, target.CallCount); 189 | adapter.Write("Bar", new { baz = (int?)null }); 190 | Assert.Equal(1, target.CallCount); 191 | Assert.Equal(0, target.Baz); 192 | } 193 | 194 | [Fact] 195 | public void CallingWriteWithNonConvertableTypeThrows() 196 | { 197 | // Arrange 198 | var target = new Crash(); 199 | var adapter = CreateAdapter(target); 200 | 201 | // Act & Assert 202 | Assert.Equal(0, target.CallCount); 203 | var exception = Assert.Throws(() => adapter.Write("Bar", new { baz = 1.12 })); 204 | Assert.Equal("Unable to generate a proxy for method 'OnBar'. See Inner Exception for details.", exception.Message); 205 | Assert.Equal( 206 | "Type 'System.Int32' must be an interface in order to support proxy generation from source type 'System.Double'.", 207 | exception.InnerException.Message); 208 | Assert.Equal(0, target.CallCount); 209 | } 210 | 211 | [Fact] 212 | public void CallingWriteWithProxyTypeThrows() 213 | { 214 | // Arrange 215 | var target = new Crash(); 216 | var adapter = CreateAdapter(target); 217 | 218 | // Act & Assert 219 | Assert.Equal(0, target.CallCount); 220 | var exception = Assert.Throws(() => adapter.Write("Foo", new { bar = new Guid() })); 221 | Assert.Equal("Unable to generate a proxy for method 'OnFoo'. See Inner Exception for details.", exception.Message); 222 | Assert.Equal( 223 | "Type 'System.String' must be an interface in order to support proxy generation from source type 'System.Guid'.", 224 | exception.InnerException.Message); 225 | Assert.Equal(0, target.CallCount); 226 | } 227 | 228 | [Fact] 229 | public void CallingWriteWillInvokeMethod() 230 | { 231 | // Arrange 232 | var target = new OneTarget(); 233 | var adapter = CreateAdapter(target); 234 | 235 | // Act & Assert 236 | Assert.Equal(0, target.OneCallCount); 237 | adapter.Write("One", new { }); 238 | Assert.Equal(1, target.OneCallCount); 239 | } 240 | 241 | [Fact] 242 | public void CallingWriteForNonEnlistedNameIsHarmless() 243 | { 244 | // Arrange 245 | var target = new OneTarget(); 246 | var adapter = CreateAdapter(target); 247 | 248 | // Act & Assert 249 | Assert.Equal(0, target.OneCallCount); 250 | adapter.Write("Two", new { }); 251 | Assert.Equal(0, target.OneCallCount); 252 | } 253 | 254 | [Fact] 255 | public void Write_EnlistedDiagnosticName_DoesNotCallIsEnabled() 256 | { 257 | // Arrange 258 | var callCount = 0; 259 | Func isEnabled = (name, arg1, arg2) => 260 | { 261 | callCount++; 262 | return true; 263 | }; 264 | 265 | var target = new OneTarget(); 266 | var adapter = CreateAdapter(target, isEnabled); 267 | 268 | // Act 269 | adapter.Write("One", new { }); 270 | 271 | // Assert 272 | Assert.Equal(0, callCount); 273 | Assert.Equal(1, target.OneCallCount); 274 | } 275 | 276 | [Fact] 277 | public void Write_NonEnlistedDiagnosticName_DoesNotCallIsEnabled() 278 | { 279 | // Arrange 280 | var callCount = 0; 281 | Func isEnabled = (name, arg1, arg2) => 282 | { 283 | callCount++; 284 | return false; 285 | }; 286 | 287 | var target = new OneTarget(); 288 | var adapter = CreateAdapter(target, isEnabled); 289 | 290 | // Act 291 | adapter.Write("Two", new { }); 292 | 293 | // Assert 294 | Assert.Equal(0, callCount); 295 | Assert.Equal(0, target.OneCallCount); 296 | } 297 | 298 | private class TwoTarget 299 | { 300 | public string Alpha { get; private set; } 301 | public string Beta { get; private set; } 302 | public int Delta { get; private set; } 303 | 304 | [DiagnosticName("Two")] 305 | public void Two(string alpha, string beta, int delta) 306 | { 307 | Alpha = alpha; 308 | Beta = beta; 309 | Delta = delta; 310 | } 311 | } 312 | 313 | [Fact] 314 | public void ParametersWillSplatFromObjectByName() 315 | { 316 | // Arrange 317 | var target = new TwoTarget(); 318 | var adapter = CreateAdapter(target); 319 | 320 | // Act 321 | adapter.Write("Two", new { alpha = "ALPHA", beta = "BETA", delta = -1 }); 322 | 323 | // Assert 324 | Assert.Equal("ALPHA", target.Alpha); 325 | Assert.Equal("BETA", target.Beta); 326 | Assert.Equal(-1, target.Delta); 327 | } 328 | 329 | [Fact] 330 | public void ExtraParametersAreHarmless() 331 | { 332 | // Arrange 333 | var target = new TwoTarget(); 334 | var adapter = CreateAdapter(target); 335 | 336 | // Act 337 | adapter.Write("Two", new { alpha = "ALPHA", beta = "BETA", delta = -1, extra = this }); 338 | 339 | // Assert 340 | Assert.Equal("ALPHA", target.Alpha); 341 | Assert.Equal("BETA", target.Beta); 342 | Assert.Equal(-1, target.Delta); 343 | } 344 | 345 | [Fact] 346 | public void MissingParametersArriveAsNull() 347 | { 348 | // Arrange 349 | var target = new TwoTarget(); 350 | var adapter = CreateAdapter(target); 351 | 352 | // Act 353 | adapter.Write("Two", new { alpha = "ALPHA", delta = -1 }); 354 | 355 | // Assert 356 | Assert.Equal("ALPHA", target.Alpha); 357 | Assert.Null(target.Beta); 358 | Assert.Equal(-1, target.Delta); 359 | } 360 | 361 | [Fact] 362 | public void Write_CanDuckType() 363 | { 364 | // Arrange 365 | var target = new ThreeTarget(); 366 | var adapter = CreateAdapter(target); 367 | 368 | // Act 369 | adapter.Write("Three", new 370 | { 371 | person = new Person 372 | { 373 | FirstName = "Alpha", 374 | Address = new Address 375 | { 376 | City = "Beta", 377 | State = "Gamma", 378 | Zip = 98028 379 | } 380 | } 381 | }); 382 | 383 | // Assert 384 | Assert.Equal("Alpha", target.Person.FirstName); 385 | Assert.Equal("Beta", target.Person.Address.City); 386 | Assert.Equal("Gamma", target.Person.Address.State); 387 | Assert.Equal(98028, target.Person.Address.Zip); 388 | } 389 | 390 | [Fact] 391 | public void Write_CanDuckType_RuntimeType() 392 | { 393 | // Arrange 394 | var target = new FourTarget(); 395 | var adapter = CreateAdapter(target); 396 | 397 | // Act 398 | adapter.Write("Four", new 399 | { 400 | person = (Person)new CoolPerson 401 | { 402 | FirstName = "Alpha", 403 | Address = new Address 404 | { 405 | City = "Beta", 406 | State = "Gamma", 407 | Zip = 98028 408 | }, 409 | Coolness = 5.7m, 410 | } 411 | }); 412 | 413 | // Assert 414 | Assert.Equal("Alpha", target.Person.FirstName); 415 | Assert.Equal("Beta", target.Person.Address.City); 416 | Assert.Equal("Gamma", target.Person.Address.State); 417 | Assert.Equal(98028, target.Person.Address.Zip); 418 | Assert.Equal(5.7m, target.Person.Coolness); 419 | } 420 | 421 | [Fact] 422 | public void Write_CanDuckType_Null() 423 | { 424 | // Arrange 425 | var target = new ThreeTarget(); 426 | var adapter = CreateAdapter(target); 427 | 428 | // Act 429 | adapter.Write("Three", new 430 | { 431 | person = (Person)null, 432 | }); 433 | 434 | // Assert 435 | Assert.Null(target.Person); 436 | } 437 | 438 | [Fact] 439 | public void Write_NominialType() 440 | { 441 | // Arrange 442 | var target = new ThreeTarget(); 443 | var adapter = CreateAdapter(target); 444 | 445 | // Act 446 | adapter.Write("Three", new NominalType() 447 | { 448 | Person = new Person 449 | { 450 | FirstName = "Alpha", 451 | Address = new Address 452 | { 453 | City = "Beta", 454 | State = "Gamma", 455 | Zip = 98028 456 | } 457 | } 458 | }); 459 | 460 | // Assert 461 | Assert.Equal("Alpha", target.Person.FirstName); 462 | Assert.Equal("Beta", target.Person.Address.City); 463 | Assert.Equal("Gamma", target.Person.Address.State); 464 | Assert.Equal(98028, target.Person.Address.Zip); 465 | } 466 | 467 | public class ThreeTarget 468 | { 469 | public IPerson Person { get; private set; } 470 | 471 | [DiagnosticName("Three")] 472 | public void Three(IPerson person) 473 | { 474 | Person = person; 475 | } 476 | } 477 | 478 | public class FourTarget 479 | { 480 | public ICoolPerson Person { get; private set; } 481 | 482 | [DiagnosticName("Four")] 483 | public void Three(ICoolPerson person) 484 | { 485 | Person = person; 486 | } 487 | } 488 | 489 | public interface IPerson 490 | { 491 | string FirstName { get; } 492 | string LastName { get; } 493 | IAddress Address { get; } 494 | } 495 | 496 | public interface IAddress 497 | { 498 | string City { get; } 499 | string State { get; } 500 | int Zip { get; } 501 | } 502 | 503 | public interface ICoolPerson : IPerson 504 | { 505 | decimal Coolness { get; } 506 | } 507 | 508 | public class Person 509 | { 510 | public string FirstName { get; set; } 511 | public string LastName { get; set; } 512 | public Address Address { get; set; } 513 | } 514 | 515 | public class CoolPerson : Person 516 | { 517 | public decimal Coolness { get; set; } 518 | } 519 | 520 | public class NominalType 521 | { 522 | public Person Person { get; set; } 523 | } 524 | 525 | public class DerivedPerson : Person 526 | { 527 | public double CoolnessFactor { get; set; } 528 | } 529 | 530 | public class Address 531 | { 532 | public string City { get; set; } 533 | public string State { get; set; } 534 | public int Zip { get; set; } 535 | } 536 | 537 | public class SomeValueType 538 | { 539 | public SomeValueType(int value) 540 | { 541 | Value = value; 542 | } 543 | 544 | public int Value { get; private set; } 545 | } 546 | 547 | private static DiagnosticSourceAdapter CreateAdapter(object target, Func isEnabled = null) 548 | { 549 | return new DiagnosticSourceAdapter(target, isEnabled, new ProxyDiagnosticSourceMethodAdapter()); 550 | } 551 | 552 | private static DiagnosticSourceAdapter CreateAdapter(object target, Func isEnabled) 553 | { 554 | return new DiagnosticSourceAdapter(target, isEnabled, new ProxyDiagnosticSourceMethodAdapter()); 555 | } 556 | } 557 | } 558 | -------------------------------------------------------------------------------- /test/Microsoft.Extensions.DiagnosticAdapter.Test/Internal/ProxyFactoryTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using Microsoft.Extensions.DiagnosticAdapter.Infrastructure; 5 | using Xunit; 6 | 7 | namespace Microsoft.Extensions.DiagnosticAdapter.Internal 8 | { 9 | public class ProxyFactoryTest 10 | { 11 | [Fact] 12 | public void CreateProxy_Null() 13 | { 14 | // Arrange 15 | var factory = new ProxyFactory(); 16 | 17 | // Act 18 | var result = factory.CreateProxy(null); 19 | 20 | // Assert 21 | Assert.Null(result); 22 | } 23 | 24 | [Fact] 25 | public void CreateProxy_ValueType_Null() 26 | { 27 | // Arrange 28 | var factory = new ProxyFactory(); 29 | 30 | // Act 31 | var result = factory.CreateProxy(null); 32 | 33 | // Assert 34 | Assert.Equal(default(int), result); 35 | } 36 | 37 | [Fact] 38 | public void CreateProxy_Assignable() 39 | { 40 | // Arrange 41 | var factory = new ProxyFactory(); 42 | var value = new Person() { Name = "Joey" }; 43 | 44 | // Act 45 | var result = factory.CreateProxy(value); 46 | 47 | // Assert 48 | Assert.Same(value, result); 49 | } 50 | 51 | [Fact] 52 | public void CreateProxy_Proxy() 53 | { 54 | // Arrange 55 | var factory = new ProxyFactory(); 56 | var value = new Person() { Name = "Joey" }; 57 | 58 | // Act 59 | var result = factory.CreateProxy(value); 60 | 61 | // Assert 62 | Assert.Same(value.Name, result.Name); 63 | } 64 | 65 | [Fact] 66 | public void CreateProxy_Proxy_CanUnwrap() 67 | { 68 | // Arrange 69 | var factory = new ProxyFactory(); 70 | var value = new Person() { Name = "Joey" }; 71 | 72 | // Act 73 | var result = (IProxy)factory.CreateProxy(value); 74 | 75 | // Assert 76 | Assert.Same(value, result.Upwrap()); 77 | } 78 | 79 | public class Person 80 | { 81 | public string Name { get; set; } 82 | } 83 | 84 | public interface IPerson 85 | { 86 | string Name { get; } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /test/Microsoft.Extensions.DiagnosticAdapter.Test/Internal/ProxyTypeEmitterTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using Xunit; 9 | 10 | namespace Microsoft.Extensions.DiagnosticAdapter.Internal 11 | { 12 | public class ProxyTypeEmitterTest 13 | { 14 | public static TheoryData GetProxyType_Identity_Data 15 | { 16 | get 17 | { 18 | return new TheoryData() 19 | { 20 | { typeof(string) }, 21 | { typeof(Person) }, 22 | { typeof(int) }, 23 | { typeof(SomeValueType) }, 24 | }; 25 | } 26 | } 27 | 28 | [Theory] 29 | [MemberData(nameof(GetProxyType_Identity_Data))] 30 | public void GetProxyType_Identity(Type type) 31 | { 32 | // Arrange 33 | var cache = new ProxyTypeCache(); 34 | 35 | // Act 36 | var result = ProxyTypeEmitter.GetProxyType(cache, type, type); 37 | 38 | // Assert 39 | Assert.Null(result); 40 | } 41 | 42 | public static TheoryData GetProxyType_Assignable_Data 43 | { 44 | get 45 | { 46 | return new TheoryData() 47 | { 48 | { typeof(int), typeof(IConvertible) }, // Interface assignment 49 | { typeof(DerivedPerson), typeof(Person) }, // Base-class assignment 50 | { typeof(decimal), typeof(decimal?) }, // value-type to nullable assignment 51 | }; 52 | } 53 | } 54 | 55 | [Theory] 56 | [MemberData(nameof(GetProxyType_Assignable_Data))] 57 | public void GetProxyType_Assignable(Type sourceType, Type targetType) 58 | { 59 | // Arrange 60 | var cache = new ProxyTypeCache(); 61 | 62 | // Act 63 | var result = ProxyTypeEmitter.GetProxyType(cache, targetType, sourceType); 64 | 65 | // Assert 66 | Assert.Null(result); 67 | } 68 | 69 | [Fact] 70 | public void GetProxyType_IfAlreadyInCache_AlsoAddedToVisited_FromType() 71 | { 72 | // Arrange 73 | var targetType = typeof(IPerson); 74 | var sourceType = typeof(Person); 75 | 76 | var key = new Tuple(sourceType, targetType); 77 | var cache = new ProxyTypeCache(); 78 | cache[key] = ProxyTypeCacheResult.FromType(key, sourceType, sourceType.GetConstructor(Array.Empty())); 79 | 80 | var context = new ProxyTypeEmitter.ProxyBuilderContext(cache, targetType, sourceType); 81 | 82 | // Act 83 | var result = ProxyTypeEmitter.VerifyProxySupport(context, key); 84 | var result2 = ProxyTypeEmitter.VerifyProxySupport(context, key); 85 | 86 | // Assert 87 | Assert.True(result); 88 | Assert.True(result2); 89 | Assert.Single(context.Visited); 90 | Assert.Equal(key, context.Visited.Single().Key); 91 | } 92 | 93 | [Fact] 94 | public void GetProxyType_IfAlreadyInCache_AlsoAddedToVisited_FromError() 95 | { 96 | // Arrange 97 | var targetType = typeof(IPerson); 98 | var sourceType = typeof(Person); 99 | 100 | var key = new Tuple(sourceType, targetType); 101 | var cache = new ProxyTypeCache(); 102 | cache[key] = ProxyTypeCacheResult.FromError(key, "Test Error"); 103 | 104 | var context = new ProxyTypeEmitter.ProxyBuilderContext(cache, targetType, sourceType); 105 | 106 | // Act 107 | var result = ProxyTypeEmitter.VerifyProxySupport(context, key); 108 | 109 | // Assert 110 | Assert.False(result); 111 | Assert.Equal(key, context.Visited.Single().Key); 112 | } 113 | 114 | [Fact] 115 | public void GetProxyType_Fails_DestinationIsNotInterface() 116 | { 117 | // Arrange 118 | var sourceType = typeof(Person); 119 | var targetType = typeof(string); 120 | 121 | var expectedMessage = string.Format( 122 | "Type '{0}' must be an interface in order to support proxy generation from source type '{1}'.", 123 | targetType.FullName, 124 | sourceType.FullName); 125 | 126 | // Act 127 | var exception = Assert.Throws( 128 | () => ProxyTypeEmitter.GetProxyType(new ProxyTypeCache(), targetType, sourceType)); 129 | 130 | // Assert 131 | Assert.Equal(expectedMessage, exception.Message); 132 | } 133 | 134 | [Fact] 135 | public void Adapt_Proxy_InvalidProperty_DestinationIsNotInterface() 136 | { 137 | // Arrange 138 | var sourceType = typeof(Person); 139 | var targetType = typeof(IBadPerson); 140 | 141 | var expectedMessage = string.Format( 142 | "Type '{0}' must be an interface in order to support proxy generation from source type '{1}'.", 143 | typeof(string), 144 | typeof(Address).FullName); 145 | 146 | // Act 147 | var exception = Assert.Throws( 148 | () => ProxyTypeEmitter.GetProxyType(new ProxyTypeCache(), targetType, sourceType)); 149 | 150 | // Assert 151 | Assert.Equal(expectedMessage, exception.Message); 152 | } 153 | 154 | [Fact] 155 | public void Adapt_Proxy() 156 | { 157 | // Arrange 158 | var value = new Person() 159 | { 160 | Address = new Address() 161 | { 162 | City = "Redmond", 163 | State = "WA", 164 | Zip = 98002, 165 | }, 166 | FirstName = "Bill", 167 | LastName = "Gates", 168 | }; 169 | 170 | var outputType = typeof(IPerson); 171 | 172 | // Act 173 | var result = ConvertTo(value); 174 | 175 | // Assert 176 | var person = Assert.IsAssignableFrom(result); 177 | Assert.Same(value.Address.City, person.Address.City); 178 | Assert.Same(value.Address.State, person.Address.State); 179 | Assert.Equal(value.Address.Zip, person.Address.Zip); 180 | 181 | // IPerson doesn't define the FirstName property. 182 | Assert.Same(value.LastName, person.LastName); 183 | } 184 | 185 | [Fact] 186 | public void Adapt_Proxy_NullProperty() 187 | { 188 | // Arrange 189 | var value = new Person() 190 | { 191 | Address = null, 192 | FirstName = null, 193 | LastName = null, 194 | }; 195 | 196 | var outputType = typeof(IPerson); 197 | 198 | // Act 199 | var result = ConvertTo(value); 200 | 201 | // Assert 202 | var person = Assert.IsAssignableFrom(result); 203 | Assert.Null(person.Address); 204 | Assert.Null(person.FirstName); 205 | Assert.Null(person.LastName); 206 | } 207 | 208 | [Fact] 209 | public void Adapt_Proxy_WithTypeCycle() 210 | { 211 | // Arrange 212 | var value = new C1() 213 | { 214 | C2 = new C2() 215 | { 216 | C1 = new C1() 217 | { 218 | C2 = new C2(), 219 | Tag = "C1.C2.C1", 220 | }, 221 | Tag = "C1.C2", 222 | }, 223 | Tag = "C1", 224 | }; 225 | 226 | // Act 227 | var result = ConvertTo(value); 228 | 229 | // Assert 230 | var c1 = Assert.IsAssignableFrom(result); 231 | Assert.Equal(value.C2.Tag, c1.C2.Tag); 232 | Assert.Equal(value.C2.C1.Tag, c1.C2.C1.Tag); 233 | Assert.Equal(value.C2.C1.C2.Tag, c1.C2.C1.C2.Tag); 234 | Assert.Null(value.C2.C1.C2.C1); 235 | } 236 | 237 | [Fact] 238 | public void Adapt_Proxy_WithPrivatePropertyGetterOnSourceType_ReferenceTypeProperty() 239 | { 240 | // Arrange 241 | var value = new PrivateGetter() 242 | { 243 | Ignored = "hi", 244 | }; 245 | 246 | // Act 247 | var result = ConvertTo(value); 248 | 249 | // Assert 250 | var proxy = Assert.IsAssignableFrom(result); 251 | Assert.Null(proxy.Ignored); 252 | } 253 | 254 | [Fact] 255 | public void Adapt_Proxy_WithPrivatePropertyGetterOnSourceType_ValueTypeProperty() 256 | { 257 | // Arrange 258 | var value = new PrivateGetter(); 259 | 260 | // Act 261 | var result = ConvertTo(value); 262 | 263 | // Assert 264 | var proxy = Assert.IsAssignableFrom(result); 265 | Assert.Equal(0, proxy.IgnoredAlso); 266 | } 267 | 268 | [Fact] 269 | public void Adapt_InvalidProxy_IndexerProperty() 270 | { 271 | // Arrange 272 | var sourceType = typeof(Indexer); 273 | var targetType = typeof(IIndexer); 274 | 275 | var expected = 276 | $"The property 'Item' on type '{targetType}' must not define a setter to support proxy generation."; 277 | 278 | // Act & Assert 279 | var exception = Assert.Throws( 280 | () => ProxyTypeEmitter.GetProxyType(new ProxyTypeCache(), targetType, sourceType)); 281 | Assert.Equal(expected, exception.Message); 282 | } 283 | 284 | [Fact] 285 | public void Adapt_List_ToReadOnlyList() 286 | { 287 | // Arrange 288 | var value = new List() 289 | { 290 | "Hello", 291 | "World", 292 | }; 293 | 294 | // Act 295 | var proxy = Convert, IReadOnlyList>(value); 296 | 297 | // Assert 298 | Assert.NotNull(proxy); 299 | Assert.Equal(2, proxy.Count); 300 | Assert.Equal("Hello", proxy[0]); 301 | Assert.Equal("World", proxy[1]); 302 | } 303 | 304 | [Fact] 305 | public void Adapt_List_ToReadOnlyList_Enumerator() 306 | { 307 | // Arrange 308 | var value = new List() 309 | { 310 | "Hello", 311 | "World", 312 | }; 313 | 314 | // Act 315 | var proxy = Convert, IReadOnlyList>(value); 316 | 317 | // Assert 318 | Assert.NotNull(proxy); 319 | 320 | var sequence = value.Zip(proxy, (i, j) => Tuple.Create(i, j)); 321 | foreach (var item in sequence) 322 | { 323 | Assert.Equal(item.Item1, item.Item2); 324 | } 325 | } 326 | 327 | [Fact] 328 | public void Adapt_ListWithProxy_ToReadOnlyList() 329 | { 330 | // Arrange 331 | var value = new List() 332 | { 333 | new Person() { FirstName = "Billy" }, 334 | new Person() { FirstName = "Joe" }, 335 | }; 336 | 337 | // Act 338 | var proxy = Convert, IReadOnlyList>(value); 339 | 340 | // Assert 341 | Assert.NotNull(proxy); 342 | Assert.Equal(2, proxy.Count); 343 | Assert.Equal("Billy", proxy[0].FirstName); 344 | Assert.Equal("Joe", proxy[1].FirstName); 345 | } 346 | 347 | [Fact] 348 | public void Adapt_ListWithProxy_ToReadOnlyList_Null() 349 | { 350 | // Arrange 351 | var value = new List() 352 | { 353 | new Person() { FirstName = "Billy" }, 354 | null, 355 | }; 356 | 357 | // Act 358 | var proxy = Convert, IReadOnlyList>(value); 359 | 360 | // Assert 361 | Assert.NotNull(proxy); 362 | Assert.Equal(2, proxy.Count); 363 | Assert.Equal("Billy", proxy[0].FirstName); 364 | Assert.Null(proxy[1]); 365 | } 366 | 367 | [Fact] 368 | public void Adapt_ListWithProxy_ToReadOnlyList_Enumerator() 369 | { 370 | // Arrange 371 | var value = new List() 372 | { 373 | new Person() { FirstName = "Billy" }, 374 | new Person() { FirstName = "Joe" }, 375 | }; 376 | 377 | // Act 378 | var proxy = Convert, IReadOnlyList>(value); 379 | 380 | // Assert 381 | Assert.NotNull(proxy); 382 | 383 | var sequence = value.Zip(proxy, (i, j) => Tuple.Create(i, j)); 384 | foreach (var item in sequence) 385 | { 386 | Assert.Equal(item.Item1.FirstName, item.Item2.FirstName); 387 | } 388 | } 389 | 390 | [Fact] 391 | public void Adapt_ListWithProxy_ToReadOnlyList_Enumerator_Null() 392 | { 393 | // Arrange 394 | var value = new List() 395 | { 396 | new Person() { FirstName = "Billy" }, 397 | null, 398 | }; 399 | 400 | // Act 401 | var proxy = Convert, IReadOnlyList>(value); 402 | 403 | // Assert 404 | Assert.NotNull(proxy); 405 | 406 | var sequence = value.Zip(proxy, (i, j) => Tuple.Create(i, j)); 407 | foreach (var item in sequence) 408 | { 409 | Assert.Equal(item.Item1?.FirstName, item.Item2?.FirstName); 410 | } 411 | } 412 | 413 | [Fact] 414 | public void Adapt_Array_ToReadOnlyList() 415 | { 416 | // Arrange 417 | var value = new string[] 418 | { 419 | "Hello", 420 | "World", 421 | }; 422 | 423 | // Act 424 | var proxy = Convert, IReadOnlyList>(value); 425 | 426 | // Assert 427 | Assert.NotNull(proxy); 428 | Assert.Equal(2, proxy.Count); 429 | Assert.Equal("Hello", proxy[0]); 430 | Assert.Equal("World", proxy[1]); 431 | } 432 | 433 | [Fact] 434 | public void Adapt_Array_ToReadOnlyList_Enumerator() 435 | { 436 | // Arrange 437 | var value = new string[] 438 | { 439 | "Hello", 440 | "World", 441 | }; 442 | 443 | // Act 444 | var proxy = Convert, IReadOnlyList>(value); 445 | 446 | // Assert 447 | Assert.NotNull(proxy); 448 | 449 | var sequence = value.Zip(proxy, (i, j) => Tuple.Create(i, j)); 450 | foreach (var item in sequence) 451 | { 452 | Assert.Equal(item.Item1, item.Item2); 453 | } 454 | } 455 | 456 | [Fact] 457 | public void Adapt_ListProperty_ToReadOnlyList() 458 | { 459 | // Arrange 460 | var value = new HasListProperty() 461 | { 462 | ListProperty = new List() 463 | { 464 | "Hello", 465 | "World", 466 | }, 467 | }; 468 | 469 | // Act 470 | var proxy = ConvertTo(value); 471 | 472 | // Assert 473 | Assert.NotNull(proxy.ListProperty); 474 | Assert.Equal(2, proxy.ListProperty.Count); 475 | Assert.Equal("Hello", proxy.ListProperty[0]); 476 | Assert.Equal("World", proxy.ListProperty[1]); 477 | } 478 | 479 | [Fact] 480 | public void Adapt_ListListProperty_ToReadOnlyList_Enumerator() 481 | { 482 | // Arrange 483 | var value = new HasListProperty() 484 | { 485 | ListProperty = new List() 486 | { 487 | "Hello", 488 | "World", 489 | }, 490 | }; 491 | 492 | // Act 493 | var proxy = ConvertTo(value); 494 | 495 | // Assert 496 | Assert.NotNull(proxy.ListProperty); 497 | 498 | var sequence = value.ListProperty.Zip(proxy.ListProperty, (i, j) => Tuple.Create(i, j)); 499 | foreach (var item in sequence) 500 | { 501 | Assert.Equal(item.Item1, item.Item2); 502 | } 503 | } 504 | 505 | [Fact] 506 | public void Adapt_ArrayListProperty_ToReadOnlyList() 507 | { 508 | // Arrange 509 | var value = new HasArrayProperty() 510 | { 511 | ListProperty = new string[] 512 | { 513 | "Hello", 514 | "World", 515 | }, 516 | }; 517 | 518 | // Act 519 | var proxy = ConvertTo(value); 520 | 521 | // Assert 522 | Assert.NotNull(proxy.ListProperty); 523 | Assert.Equal(2, proxy.ListProperty.Count); 524 | Assert.Equal("Hello", proxy.ListProperty[0]); 525 | Assert.Equal("World", proxy.ListProperty[1]); 526 | } 527 | 528 | [Fact] 529 | public void Adapt_ArrayListProperty_ToReadOnlyList_Enumerator() 530 | { 531 | // Arrange 532 | var value = new HasArrayProperty() 533 | { 534 | ListProperty = new string[] 535 | { 536 | "Hello", 537 | "World", 538 | }, 539 | }; 540 | 541 | // Act 542 | var proxy = ConvertTo(value); 543 | 544 | // Assert 545 | Assert.NotNull(proxy.ListProperty); 546 | 547 | var sequence = value.ListProperty.Zip(proxy.ListProperty, (i, j) => Tuple.Create(i, j)); 548 | foreach (var item in sequence) 549 | { 550 | Assert.Equal(item.Item1, item.Item2); 551 | } 552 | } 553 | 554 | [Fact] 555 | public void Adapt_ListPropertyWithProxy_ToReadOnlyList() 556 | { 557 | // Arrange 558 | var value = new HasListOfPersonProperty() 559 | { 560 | ListProperty = new List() 561 | { 562 | new Person() { FirstName = "Billy" }, 563 | new Person() { FirstName = "Joe" }, 564 | } 565 | }; 566 | 567 | // Act 568 | var proxy = ConvertTo(value); 569 | 570 | // Assert 571 | Assert.NotNull(proxy); 572 | Assert.Equal(2, proxy.ListProperty.Count); 573 | Assert.Equal("Billy", proxy.ListProperty[0].FirstName); 574 | Assert.Equal("Joe", proxy.ListProperty[1].FirstName); 575 | } 576 | 577 | [Fact] 578 | public void Adapt_ListPropertyWithProxy_ToReadOnlyList_Enumerator() 579 | { 580 | // Arrange 581 | var value = new HasListOfPersonProperty() 582 | { 583 | ListProperty = new List() 584 | { 585 | new Person() { FirstName = "Billy" }, 586 | new Person() { FirstName = "Joe" }, 587 | } 588 | }; 589 | 590 | // Act 591 | var proxy = ConvertTo(value); 592 | 593 | // Assert 594 | Assert.NotNull(proxy); 595 | 596 | var sequence = value.ListProperty.Zip(proxy.ListProperty, (i, j) => Tuple.Create(i, j)); 597 | foreach (var item in sequence) 598 | { 599 | Assert.Equal(item.Item1.FirstName, item.Item2.FirstName); 600 | } 601 | } 602 | 603 | [Fact] 604 | public void Adapt_NestedList() 605 | { 606 | // Arrange 607 | var value = new List>>() 608 | { 609 | new List>() 610 | { 611 | new List() 612 | { 613 | new Person() { FirstName = "Billy" }, 614 | }, 615 | }, 616 | }; 617 | 618 | // Act 619 | var proxy = Convert>>, IReadOnlyList>>>(value); 620 | 621 | // Assert 622 | Assert.NotNull(proxy); 623 | Assert.Equal(1, proxy[0][0].Count); 624 | Assert.Equal("Billy", proxy[0][0][0].FirstName); 625 | } 626 | 627 | [Fact] 628 | public void GetProxyType_LocksPreventDuplicateAssemblyNamesArgumentException_ForConcurrentThreads() 629 | { 630 | for (var i = 0; i < 5; i++) 631 | { 632 | Parallel.For( 633 | 0, 634 | 100, 635 | (j) => 636 | { 637 | var testObject = new Person(); 638 | ProxyTypeEmitter.GetProxyType(new ProxyTypeCache(), typeof(IPerson), testObject.GetType()); 639 | }); 640 | } 641 | } 642 | 643 | private object ConvertTo(object value, Type type) 644 | { 645 | var cache = new ProxyTypeCache(); 646 | var proxyType = ProxyTypeEmitter.GetProxyType(cache, type, value.GetType()); 647 | 648 | Assert.NotNull(proxyType); 649 | var proxy = Activator.CreateInstance(proxyType, value); 650 | 651 | Assert.IsAssignableFrom(type, proxy); 652 | return proxy; 653 | } 654 | 655 | private T ConvertTo(object value) 656 | { 657 | var cache = new ProxyTypeCache(); 658 | var proxyType = ProxyTypeEmitter.GetProxyType(cache, typeof(T), value.GetType()); 659 | 660 | Assert.NotNull(proxyType); 661 | var proxy = Activator.CreateInstance(proxyType, value); 662 | 663 | return Assert.IsAssignableFrom(proxy); 664 | } 665 | 666 | private U Convert(object value) 667 | { 668 | var cache = new ProxyTypeCache(); 669 | var proxyType = ProxyTypeEmitter.GetProxyType(cache, typeof(U), typeof(T)); 670 | 671 | Assert.NotNull(proxyType); 672 | var proxy = Activator.CreateInstance(proxyType, value); 673 | 674 | return Assert.IsAssignableFrom(proxy); 675 | } 676 | 677 | public interface IHasReadOnlyListProperty 678 | { 679 | IReadOnlyList ListProperty { get; } 680 | } 681 | 682 | public class HasListProperty 683 | { 684 | public IList ListProperty { get; set; } 685 | } 686 | 687 | public class HasArrayProperty 688 | { 689 | public string[] ListProperty { get; set; } 690 | } 691 | 692 | public class HasReadOnlyListProperty 693 | { 694 | public IReadOnlyList ListProperty { get; set; } 695 | } 696 | 697 | public interface IHasReadOnlyListOfPersonProperty 698 | { 699 | IReadOnlyList ListProperty { get; } 700 | } 701 | 702 | public class HasListOfPersonProperty 703 | { 704 | public IList ListProperty { get; set; } 705 | } 706 | 707 | public interface IPrivateGetter 708 | { 709 | string Ignored { get; } 710 | 711 | int IgnoredAlso { get; } 712 | } 713 | 714 | public class PrivateGetter 715 | { 716 | public string Ignored { private get; set; } 717 | 718 | private int IgnoredAlso { get; set; } = 17; 719 | } 720 | 721 | public interface IIndexer 722 | { 723 | int this[int key] { get; set; } 724 | } 725 | 726 | public class Indexer 727 | { 728 | public int this[int key] 729 | { 730 | get { return 0; } 731 | set { } 732 | } 733 | } 734 | 735 | public interface IC1 736 | { 737 | IC2 C2 { get; } 738 | string Tag { get; } 739 | } 740 | 741 | public interface IC2 742 | { 743 | IC1 C1 { get; } 744 | string Tag { get; } 745 | } 746 | 747 | public class C1 748 | { 749 | public C2 C2 { get; set; } 750 | public string Tag { get; set; } 751 | } 752 | 753 | public class C2 754 | { 755 | public C1 C1 { get; set; } 756 | public string Tag { get; set; } 757 | } 758 | 759 | public interface IPerson 760 | { 761 | string FirstName { get; } 762 | string LastName { get; } 763 | IAddress Address { get; } 764 | } 765 | 766 | public interface IAddress 767 | { 768 | string City { get; } 769 | string State { get; } 770 | int Zip { get; } 771 | } 772 | 773 | public class Person 774 | { 775 | public string FirstName { get; set; } 776 | public string LastName { get; set; } 777 | public Address Address { get; set; } 778 | } 779 | 780 | public interface IBadPerson 781 | { 782 | string FirstName { get; } 783 | string LastName { get; } 784 | string Address { get; } // doesn't match with Person 785 | } 786 | 787 | public class DerivedPerson : Person 788 | { 789 | public double CoolnessFactor { get; set; } 790 | } 791 | 792 | public class Address 793 | { 794 | public string City { get; set; } 795 | public string State { get; set; } 796 | public int Zip { get; set; } 797 | } 798 | 799 | public class SomeValueType 800 | { 801 | public SomeValueType(int value) 802 | { 803 | Value = value; 804 | } 805 | 806 | public int Value { get; private set; } 807 | } 808 | } 809 | } 810 | -------------------------------------------------------------------------------- /test/Microsoft.Extensions.DiagnosticAdapter.Test/Microsoft.Extensions.DiagnosticAdapter.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(StandardTestTfms) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/Microsoft.Extensions.DiagnosticAdapter.Test/ProxyDiagnosticSourceMethodAdapterTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | using Xunit; 8 | 9 | namespace Microsoft.Extensions.DiagnosticAdapter 10 | { 11 | public class ProxyDiagnosticSourceMethodAdapterTest 12 | { 13 | [Fact] 14 | public void Adapt_Throws_ForSameNamedPropertiesWithDifferentCasing() 15 | { 16 | // Arrange 17 | var adapter = new ProxyDiagnosticSourceMethodAdapter(); 18 | 19 | var listener = new Listener1(); 20 | var method = GetMethodInfo(l => l.Listen(5, "joey")); 21 | 22 | var value = new { Id = 5, id = 17 }; 23 | var type = value.GetType(); 24 | 25 | // Act 26 | var ex = Assert.Throws(() => adapter.Adapt(method, type)); 27 | 28 | // Assert 29 | Assert.Equal( 30 | $"Proxy method generation doesn't support types with properties that vary only by case. " + 31 | $"The type '{type.FullName}' defines multiple properties named 'id' that vary only by case.", 32 | ex.Message); 33 | } 34 | 35 | [Fact] 36 | public void Adapt_ReturnsTrueForTypeMatch() 37 | { 38 | // Arrange 39 | var adapter = new ProxyDiagnosticSourceMethodAdapter(); 40 | 41 | var listener = new Listener1(); 42 | var method = GetMethodInfo(l => l.Listen()); 43 | 44 | // Act 45 | var func = adapter.Adapt(method, new { }.GetType()); 46 | 47 | // Assert 48 | Assert.True(func(listener, new { })); 49 | } 50 | 51 | [Fact] 52 | public void Adapt_ReturnsFalseForTypeNotMatching() 53 | { 54 | // Arrange 55 | var adapter = new ProxyDiagnosticSourceMethodAdapter(); 56 | 57 | var listener = new Listener1(); 58 | var method = GetMethodInfo(l => l.Listen()); 59 | 60 | // Act 61 | var func = adapter.Adapt(method, new { }.GetType()); 62 | 63 | // Assert 64 | Assert.False(func(listener, "hello")); 65 | } 66 | 67 | [Fact] 68 | public void Adapt_SplatsParameters() 69 | { 70 | // Arrange 71 | var adapter = new ProxyDiagnosticSourceMethodAdapter(); 72 | 73 | var listener = new Listener2(); 74 | var value = new { id = 17, name = "Bill" }; 75 | var method = GetMethodInfo(l => l.Listen(0, "")); 76 | 77 | // Act 78 | var func = adapter.Adapt(method, value.GetType()); 79 | 80 | // Assert 81 | Assert.True(func(listener, value)); 82 | Assert.Equal(17, listener.Id); 83 | Assert.Equal("Bill", listener.Name); 84 | } 85 | 86 | [Fact] 87 | public void Adapt_SplatsParameters_CamelCase() 88 | { 89 | // Arrange 90 | var adapter = new ProxyDiagnosticSourceMethodAdapter(); 91 | 92 | var listener = new Listener4(); 93 | var value = new { Id = 17, Person = new Person() { Name = "Bill" } }; 94 | var method = GetMethodInfo(l => l.Listen(0, null)); 95 | 96 | // Act 97 | var func = adapter.Adapt(method, value.GetType()); 98 | 99 | // Assert 100 | Assert.True(func(listener, value)); 101 | Assert.Equal(17, listener.Id); 102 | Assert.Equal("Bill", listener.Name); 103 | } 104 | 105 | [Fact] 106 | public void Adapt_SplatsParameters_CaseInsensitive() 107 | { 108 | // Arrange 109 | var adapter = new ProxyDiagnosticSourceMethodAdapter(); 110 | 111 | var listener = new Listener4(); 112 | var value = new { ID = 17, PersOn = new Person() { Name = "Bill" }}; 113 | var method = GetMethodInfo(l => l.Listen(0, null)); 114 | 115 | // Act 116 | var func = adapter.Adapt(method, value.GetType()); 117 | 118 | // Assert 119 | Assert.True(func(listener, value)); 120 | Assert.Equal(17, listener.Id); 121 | Assert.Equal("Bill", listener.Name); 122 | } 123 | 124 | [Fact] 125 | public void Adapt_SplatsParameters_ExtraEventDataIgnored() 126 | { 127 | // Arrange 128 | var adapter = new ProxyDiagnosticSourceMethodAdapter(); 129 | 130 | var listener = new Listener2(); 131 | var value = new { id = 17, name = "Bill", ignored = "hi" }; 132 | var method = GetMethodInfo(l => l.Listen(0, "")); 133 | 134 | // Act 135 | var func = adapter.Adapt(method, value.GetType()); 136 | 137 | // Assert 138 | Assert.True(func(listener, value)); 139 | Assert.Equal(17, listener.Id); 140 | Assert.Equal("Bill", listener.Name); 141 | } 142 | 143 | [Fact] 144 | public void Adapt_SplatsParameters_ExtraParametersGetDefaultValues() 145 | { 146 | // Arrange 147 | var adapter = new ProxyDiagnosticSourceMethodAdapter(); 148 | 149 | var listener = new Listener2(); 150 | var value = new { }; 151 | var method = GetMethodInfo(l => l.Listen(0, "")); 152 | 153 | // Act 154 | var func = adapter.Adapt(method, value.GetType()); 155 | 156 | // Assert 157 | Assert.True(func(listener, value)); 158 | Assert.Equal(0, listener.Id); 159 | Assert.Null(listener.Name); 160 | } 161 | 162 | [Fact] 163 | public void Adapt_SplatsParameters_WithProxy() 164 | { 165 | // Arrange 166 | var adapter = new ProxyDiagnosticSourceMethodAdapter(); 167 | 168 | var listener = new Listener3(); 169 | var value = new { id = 17, person = new Person() { Name = "Bill" } }; 170 | var method = GetMethodInfo(l => l.Listen(0, null)); 171 | 172 | // Act 173 | var func = adapter.Adapt(method, value.GetType()); 174 | 175 | // Assert 176 | Assert.True(func(listener, value)); 177 | Assert.Equal(17, listener.Id); 178 | Assert.Equal("Bill", listener.Name); 179 | } 180 | 181 | [Fact] 182 | public void Adapt_SplatsParameters_WithNominalType() 183 | { 184 | // Arrange 185 | var adapter = new ProxyDiagnosticSourceMethodAdapter(); 186 | 187 | var listener = new Listener3(); 188 | var value = new NominalType() { Id = 17, Person = new Person() { Name = "Bill" } }; 189 | var method = GetMethodInfo(l => l.Listen(0, null)); 190 | 191 | // Act 192 | var func = adapter.Adapt(method, value.GetType()); 193 | 194 | // Assert 195 | Assert.True(func(listener, value)); 196 | Assert.Equal(17, listener.Id); 197 | Assert.Equal("Bill", listener.Name); 198 | } 199 | 200 | [Fact] 201 | public void CanCreateProxyMethodForBasicType() 202 | { 203 | // Arrange 204 | var target = new Listener5(); 205 | var source = new { name = "John", age = 1234 }; 206 | 207 | var targetMethodInfo = target.GetType().GetMethod(nameof(Listener5.TargetMethod)); 208 | 209 | // Act 210 | var adapter = new ProxyDiagnosticSourceMethodAdapter(); 211 | var callback = adapter.Adapt(targetMethodInfo, source.GetType()); 212 | 213 | var result = callback(target, source); 214 | 215 | // Assert 216 | Assert.True(result); 217 | Assert.Equal(target.Name, source.name); 218 | Assert.Equal(target.Age, source.age); 219 | } 220 | 221 | [Fact] 222 | public void CanCreateProxyMethodForBasicTypeWithUpperCasing() 223 | { 224 | // Arrange 225 | var target = new Listener6(); 226 | var source = new { Name = "John", Age = 1234 }; 227 | 228 | var targetMethodInfo = target.GetType().GetMethod(nameof(Listener6.Listen)); 229 | 230 | // Act 231 | var adapter = new ProxyDiagnosticSourceMethodAdapter(); 232 | var callback = adapter.Adapt(targetMethodInfo, source.GetType()); 233 | 234 | var result = callback(target, source); 235 | 236 | // Assert 237 | Assert.True(result); 238 | Assert.Equal(target.SafeName, source.Name); 239 | Assert.Equal(target.SafeAge, source.Age); 240 | } 241 | 242 | private MethodInfo GetMethodInfo(Expression> expression) 243 | { 244 | var body = (MethodCallExpression)expression.Body; 245 | return body.Method; 246 | } 247 | 248 | private class Listener1 249 | { 250 | public void Listen() 251 | { 252 | } 253 | } 254 | 255 | private class Listener2 256 | { 257 | public int Id { get; set; } = 5; 258 | 259 | public string Name { get; set; } = "shouldn't be seen"; 260 | 261 | public void Listen(int id, string name) 262 | { 263 | Id = id; 264 | Name = name; 265 | } 266 | } 267 | 268 | private class Listener3 269 | { 270 | public int Id { get; set; } = 5; 271 | 272 | public string Name { get; set; } = "shouldn't be seen"; 273 | 274 | public void Listen(int id, IPerson person) 275 | { 276 | Id = id; 277 | Name = person?.Name; 278 | } 279 | } 280 | 281 | private class Listener4 282 | { 283 | public int Id { get; set; } = 5; 284 | 285 | public string Name { get; set; } 286 | 287 | public void Listen(int Id, IPerson Person) 288 | { 289 | this.Id = Id; 290 | this.Name = Person?.Name; 291 | } 292 | } 293 | 294 | public class Listener5 295 | { 296 | public void TargetMethod(string name, int age) 297 | { 298 | Name = name; 299 | Age = age; 300 | } 301 | 302 | public string Name { get; set; } 303 | 304 | public int Age { get; set; } 305 | } 306 | 307 | public class Listener6 308 | { 309 | public void Listen(string Name, int Age) 310 | { 311 | SafeName = Name; 312 | SafeAge = Age; 313 | } 314 | 315 | public string SafeName { get; set; } 316 | 317 | public int SafeAge { get; set; } 318 | } 319 | 320 | public class NominalType 321 | { 322 | public int Id { get; set; } 323 | 324 | public Person Person { get; set; } 325 | } 326 | 327 | public class Person 328 | { 329 | public string Name { get; set; } 330 | } 331 | 332 | public interface IPerson 333 | { 334 | string Name { get; } 335 | } 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /version.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 3.0.0 4 | alpha1 5 | $(VersionPrefix) 6 | $(VersionPrefix)-$(VersionSuffix)-final 7 | t000 8 | a- 9 | $(FeatureBranchVersionPrefix)$(VersionSuffix)-$([System.Text.RegularExpressions.Regex]::Replace('$(FeatureBranchVersionSuffix)', '[^\w-]', '-')) 10 | $(VersionSuffix)-$(BuildNumber) 11 | 12 | 13 | --------------------------------------------------------------------------------